Skip to content

Commit

Permalink
feat: improve URL and title management in ActionView
Browse files Browse the repository at this point in the history
gisce/webclient#1612
- Added `useAutoUpdateUrlAndTitle` hook to automatically update the document title and URL based on the current tab and context.
- Introduced `useUrlFromCurrentTab` hook to generate shareable URLs based on the current tab's action and parameters.
- Updated `ConfigContext` to include a `title` property for better context management.
- Refactored `shareUrlHelper` to simplify URL creation logic.
- Improved `ActionView` component structure by extracting content rendering into a separate `ActionViewContent` component for better readability and maintainability.
  • Loading branch information
mguellsegarra committed Jan 20, 2025
1 parent 7286618 commit 644e14e
Show file tree
Hide file tree
Showing 5 changed files with 231 additions and 99 deletions.
6 changes: 5 additions & 1 deletion src/context/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ type ConfigContextProps = ConfigContextValues & {

type ConfigContextValues = {
erpFeatures: ErpFeaturesMap;
title: string;
globalValues?: Record<string, any>;
rootContext?: Record<string, any>;
devMode?: boolean;
};

const defaultConfigContext: ConfigContextValues = {
erpFeatures: {},
title: "Webclient",
globalValues: {},
rootContext: {},
devMode: false,
Expand Down Expand Up @@ -55,6 +57,7 @@ export const ConfigContextProvider = memo(
globalValues,
rootContext,
devMode,
title,
children,
}: ConfigContextProps & { children?: React.ReactNode }) => {
const providerValue = useMemo(
Expand All @@ -63,8 +66,9 @@ export const ConfigContextProvider = memo(
globalValues,
rootContext,
devMode,
title,
}),
[erpFeatures, globalValues, rootContext, devMode],
[erpFeatures, globalValues, rootContext, devMode, title],
);

return (
Expand Down
8 changes: 3 additions & 5 deletions src/helpers/shareUrlHelper.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { ActionInfo, ActionRawData } from "@/types";

const OPEN_ACTION_PATH = "openAction";
const OPEN_ACTION_PATH = "action";

export const createShareOpenUrl = (action: ActionInfo) => {
const url = new URL(window.location.href);
url.pathname += url.pathname.endsWith("/")
? OPEN_ACTION_PATH
: `/${OPEN_ACTION_PATH}`;
const url = new URL(window.location.origin);
url.pathname = OPEN_ACTION_PATH;

// Parameters to exclude from the URL
const ignoredParams = ["target", "context", "domain"];
Expand Down
30 changes: 30 additions & 0 deletions src/hooks/useAutoUpdateUrlAndTitle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useEffect } from "react";
import { useTabs } from "@/context/TabManagerContext";
import { useConfigContext } from "@/context/ConfigContext";
import { useUrlFromCurrentTab } from "./useUrlFromCurrentTab";

export const useAutoUpdateUrlAndTitle = () => {
const { currentTab } = useTabs();
const { title } = useConfigContext();

const { shareUrl } = useUrlFromCurrentTab({ currentTab });

useEffect(() => {
if (shareUrl) {
const url = new URL(shareUrl, window.location.origin);
if (
window.location.pathname + window.location.search !==
url.pathname + url.search
) {
window.history.replaceState({}, "", url.pathname + url.search);
}
}
}, [currentTab, shareUrl]);

useEffect(() => {
document.title = title;
if (currentTab?.title && currentTab.title.length > 0) {
document.title = currentTab?.title + " - " + title;
}
}, [currentTab, title]);
};
47 changes: 47 additions & 0 deletions src/hooks/useUrlFromCurrentTab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useTabs } from "@/context/TabManagerContext";
import { useActionViewContext } from "@/context/ActionViewContext";
import { createShareOpenUrl } from "@/helpers/shareUrlHelper";
import { ActionInfo, Tab } from "@/types";

interface UseUrlFromCurrentTabResult {
shareUrl: string | null;
canShare: boolean;
}

export function useUrlFromCurrentTab({
currentTab: currentTabProps,
}: {
currentTab?: Tab;
}): UseUrlFromCurrentTabResult {
const { currentView, searchParams, currentId } = useActionViewContext();
const { currentTab: currentTabContext } = useTabs();

const currentTab = currentTabProps || currentTabContext;

if (!currentTab?.action) {
return { shareUrl: null, canShare: false };
}

const initialView = {
id: currentView.view_id,
type: currentView.type,
};

const { action_id } = currentTab.action;
const finalActionData: ActionInfo = {
...currentTab.action,
...(initialView && { initialView }),
...(searchParams && { searchParams }),
...(currentId && { res_id: currentId }),
};

const shareUrl = createShareOpenUrl(finalActionData);
const { type } = initialView;

let canShare = !!action_id;
if (type === "form") {
canShare = !!action_id && !!currentId;
}

return { shareUrl, canShare };
}
239 changes: 146 additions & 93 deletions src/views/ActionView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { TreeActionView } from "./actionViews/TreeActionView";
import { DashboardActionView } from "./actionViews/DashboardActionView";
import { resolveViewInfoPromises } from "@/helpers/viewHelper";
import { useDeepCompareEffect } from "use-deep-compare";
import { useAutoUpdateUrlAndTitle } from "@/hooks/useAutoUpdateUrlAndTitle";

type Props = {
domain: any;
Expand Down Expand Up @@ -406,98 +407,7 @@ function ActionView(props: Props, ref: any) {
}
}

function content() {
return availableViews.map((view) => {
switch (view.type) {
case "form": {
return (
<FormActionView
key={`${view.type}-${view.view_id}`}
formRef={formRef}
currentId={currentId}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
formView={view as FormView}
model={model}
context={context}
domain={domain}
defaultValues={formDefaultValues}
forcedValues={formForcedValues}
results={results}
setResults={setResults}
setCurrentItemIndex={setCurrentItemIndex}
/>
);
}
case "tree": {
return (
<TreeActionView
key={`${view.type}-${view.view_id}`}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
limit={limit}
model={model}
context={context}
domain={domain}
formView={
availableViews.find((v) => v.type === "form") as FormView
}
treeView={view as TreeView}
searchTreeRef={searchTreeRef}
searchTreeNameSearch={searchTreeNameSearch}
availableViews={availableViews}
results={results}
setCurrentItemIndex={setCurrentItemIndex}
setCurrentId={setCurrentId}
setCurrentView={setCurrentView}
/>
);
}
case "graph": {
return (
<GraphActionView
key={`${view.type}-${view.view_id}`}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
viewData={view as GraphView}
model={model}
context={context}
domain={domain}
treeView={
availableViews.find((v) => v.type === "tree") as TreeView
}
formView={
availableViews.find((v) => v.type === "form") as FormView
}
graphView={
availableViews.find(
(v) => v.view_id === view.view_id,
) as GraphView
}
/>
);
}
case "dashboard": {
return (
<DashboardActionView
key={`${view.type}-${view.view_id}`}
dashboardData={view as DashboardView}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
/>
);
}
}
});
}
function content() {}

function onNewClicked() {
if (currentId === undefined && currentView!.type === "form") {
Expand Down Expand Up @@ -548,7 +458,25 @@ function ActionView(props: Props, ref: any) {
isActive={tabKey === activeKey}
initialSearchParams={initialSearchParams}
>
{content()}
<ActionViewContent
availableViews={availableViews}
formRef={formRef}
currentId={currentId}
currentView={currentView}
model={model}
context={context}
domain={domain}
formDefaultValues={formDefaultValues}
results={results}
setResults={setResults}
setCurrentItemIndex={setCurrentItemIndex}
formForcedValues={formForcedValues}
limit={limit}
searchTreeRef={searchTreeRef}
searchTreeNameSearch={searchTreeNameSearch}
setCurrentView={setCurrentView}
setCurrentId={setCurrentId}
/>
<GoToResourceModal
visible={gtResourceModalVisible}
onIdSubmitted={goToResourceId}
Expand All @@ -561,4 +489,129 @@ function ActionView(props: Props, ref: any) {
);
}

const ActionViewContent = ({
availableViews,
formRef,
currentId,
currentView,
model,
context,
domain,
formDefaultValues,
results,
setResults,
setCurrentItemIndex,
formForcedValues,
limit,
searchTreeRef,
searchTreeNameSearch,
setCurrentView,
setCurrentId,
}: {
availableViews: View[];
formRef: React.RefObject<any>;
currentId: number | undefined;
currentView: View;
model: string;
context: any;
domain: any;
formDefaultValues: any;
results: any;
setResults: any;
setCurrentItemIndex: any;
setCurrentId: any;
setCurrentView: any;
limit?: number;
searchTreeRef: React.RefObject<any>;
searchTreeNameSearch?: string;
formForcedValues: any;
}) => {
useAutoUpdateUrlAndTitle();

return availableViews.map((view) => {
switch (view.type) {
case "form": {
return (
<FormActionView
key={`${view.type}-${view.view_id}`}
formRef={formRef}
currentId={currentId}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
formView={view as FormView}
model={model}
context={context}
domain={domain}
defaultValues={formDefaultValues}
forcedValues={formForcedValues}
results={results}
setResults={setResults}
setCurrentItemIndex={setCurrentItemIndex}
/>
);
}
case "tree": {
return (
<TreeActionView
key={`${view.type}-${view.view_id}`}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
limit={limit}
model={model}
context={context}
domain={domain}
formView={availableViews.find((v) => v.type === "form") as FormView}
treeView={view as TreeView}
searchTreeRef={searchTreeRef}
searchTreeNameSearch={searchTreeNameSearch}
availableViews={availableViews}
results={results}
setCurrentItemIndex={setCurrentItemIndex}
setCurrentId={setCurrentId}
setCurrentView={setCurrentView}
/>
);
}
case "graph": {
return (
<GraphActionView
key={`${view.type}-${view.view_id}`}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
viewData={view as GraphView}
model={model}
context={context}
domain={domain}
treeView={availableViews.find((v) => v.type === "tree") as TreeView}
formView={availableViews.find((v) => v.type === "form") as FormView}
graphView={
availableViews.find(
(v) => v.view_id === view.view_id,
) as GraphView
}
/>
);
}
case "dashboard": {
return (
<DashboardActionView
key={`${view.type}-${view.view_id}`}
dashboardData={view as DashboardView}
visible={
currentView!.type === view.type &&
currentView!.view_id === view.view_id
}
/>
);
}
}
});
};

export default forwardRef(ActionView);

0 comments on commit 644e14e

Please sign in to comment.