diff --git a/package-lock.json b/package-lock.json
index 285d88b38..3b09092fb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@gisce/react-ooui",
- "version": "2.59.0-rc.1",
+ "version": "2.59.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@gisce/react-ooui",
- "version": "2.59.0-rc.1",
+ "version": "2.59.1",
"dependencies": {
"@ant-design/colors": "^7.2.0",
"@ant-design/plots": "^1.0.9",
diff --git a/package.json b/package.json
index 78565050f..c79dc776d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@gisce/react-ooui",
- "version": "2.59.0-rc.1",
+ "version": "2.59.1",
"engines": {
"node": "20.5.0"
},
diff --git a/src/actionbar/ActionBarSeparator.tsx b/src/actionbar/ActionBarSeparator.tsx
new file mode 100644
index 000000000..ff5dcf334
--- /dev/null
+++ b/src/actionbar/ActionBarSeparator.tsx
@@ -0,0 +1,6 @@
+import { memo } from "react";
+
+export const ActionBarSeparator = memo(() => (
+
+));
+ActionBarSeparator.displayName = "ActionBarSeparator";
diff --git a/src/actionbar/ActionButton.tsx b/src/actionbar/ActionButton.tsx
index cdd600d65..51f06992c 100644
--- a/src/actionbar/ActionButton.tsx
+++ b/src/actionbar/ActionButton.tsx
@@ -1,4 +1,3 @@
-import React from "react";
import ButtonWithTooltip from "@/common/ButtonWithTooltip";
import { LoadingOutlined } from "@ant-design/icons";
import { ButtonProps } from "antd";
diff --git a/src/actionbar/DashboardActionBar.tsx b/src/actionbar/DashboardActionBar.tsx
index cc90c0493..6209375de 100644
--- a/src/actionbar/DashboardActionBar.tsx
+++ b/src/actionbar/DashboardActionBar.tsx
@@ -11,12 +11,8 @@ import {
BorderOuterOutlined,
} from "@ant-design/icons";
import { useLocale } from "@gisce/react-formiga-components";
-import {
- ActionViewContext,
- ActionViewContextType,
-} from "@/context/ActionViewContext";
+import { ActionBarSeparator } from "./ActionBarSeparator";
import { ShareUrlButton } from "./ShareUrlButton";
-import { ActionBarSeparator } from "./FormActionBar";
function DashboardActionBar() {
const { isLoading, dashboardRef, moveItemsEnabled, setMoveItemsEnabled } =
diff --git a/src/actionbar/FormActionBar.tsx b/src/actionbar/FormActionBar.tsx
index d6f92e227..eb4c02d73 100644
--- a/src/actionbar/FormActionBar.tsx
+++ b/src/actionbar/FormActionBar.tsx
@@ -1,4 +1,4 @@
-import { useContext, useCallback } from "react";
+import { useContext, useCallback, memo, useMemo } from "react";
import { Space, Spin } from "antd";
import {
SaveOutlined,
@@ -27,19 +27,17 @@ import {
TabManagerContext,
TabManagerContextType,
} from "@/context/TabManagerContext";
-import {
- ContentRootContext,
- ContentRootContextType,
-} from "@/context/ContentRootContext";
import AttachmentsButton from "./AttachmentsButton";
import { Attachment } from "./AttachmentsButtonWrapper";
import { useNextPrevious } from "./useNextPrevious";
+import {
+ saveDocument,
+ useFormToolbarButtons,
+} from "@/hooks/useFormToolbarButtons";
+import { ActionBarSeparator } from "./ActionBarSeparator";
import { ShareUrlButton } from "./ShareUrlButton";
-function FormActionBar({ toolbar }: { toolbar: any }) {
- const contentRootContext = useContext(
- ContentRootContext,
- ) as ContentRootContextType;
+function FormActionBarComponent({ toolbar }: { toolbar: any }) {
const tabManagerContext = useContext(
TabManagerContext,
) as TabManagerContextType;
@@ -74,11 +72,12 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
isActive,
} = useActionViewContext();
- const { processAction } = contentRootContext || {};
- const { openRelate, openDefaultActionForModel } = tabManagerContext || {};
+ const { openDefaultActionForModel } = tabManagerContext || {};
- const mustDisableButtons =
- formIsSaving || removingItem || formIsLoading || duplicatingItem;
+ const mustDisableButtons = useMemo(
+ () => formIsSaving || removingItem || formIsLoading || duplicatingItem,
+ [formIsSaving, removingItem, formIsLoading, duplicatingItem],
+ );
const tryAction = useCallback(
(action: () => void) => {
@@ -91,6 +90,18 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
[formHasChanges, t],
);
+ const handleRefresh = useCallback(() => {
+ tryAction(() => (formRef.current as any).fetchValues());
+ }, [tryAction, formRef]);
+
+ const { actionButtonProps, printButtonProps, relateButtonProps } =
+ useFormToolbarButtons({
+ toolbar,
+ mustDisableButtons,
+ formRef,
+ onRefreshParentValues: handleRefresh,
+ });
+
const handleRemove = useCallback(async () => {
try {
setRemovingItem?.(true);
@@ -148,17 +159,53 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
}
}, [currentId, currentModel, formRef, goToResourceId, setDuplicatingItem]);
- const runAction = useCallback(
- (actionData: any) => {
- processAction?.({
- actionData,
- values: (formRef.current as any).getValues(),
- fields: (formRef.current as any).getFields(),
- context: (formRef.current as any).getContext(),
- onRefreshParentValues: () => (formRef.current as any).fetchValues(),
+ const handleChangeView = useCallback(
+ (view: any) => {
+ setPreviousView?.(currentView);
+ setFormHasChanges?.(false);
+ setCurrentView?.(view);
+ },
+ [currentView, setPreviousView, setFormHasChanges, setCurrentView],
+ );
+
+ const handleAddNewAttachment = useCallback(async () => {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) {
+ openDefaultActionForModel?.({
+ ...getAttachmentActionPayload(
+ currentModel as string,
+ result.currentId as number,
+ ),
+ initialViewType: "form",
+ });
+ }
+ }, [currentModel, onFormSave, openDefaultActionForModel]);
+
+ const handleListAllAttachments = useCallback(async () => {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) {
+ openDefaultActionForModel?.({
+ ...getAttachmentActionPayload(
+ currentModel as string,
+ result.currentId as number,
+ ),
+ initialViewType: "tree",
});
+ }
+ }, [currentModel, onFormSave, openDefaultActionForModel]);
+
+ const handleViewAttachmentDetails = useCallback(
+ async (attachment: Attachment) => {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) {
+ openDefaultActionForModel?.({
+ model: "ir.attachment",
+ res_id: attachment.id,
+ initialViewType: "form",
+ });
+ }
},
- [formRef, processAction],
+ [onFormSave, openDefaultActionForModel],
);
useHotkeys(
@@ -267,132 +314,34 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
icon={}
tooltip={t("refresh")}
disabled={mustDisableButtons || currentId === undefined}
- onClick={() => tryAction(() => (formRef.current as any).fetchValues())}
+ onClick={handleRefresh}
/>
{
- setPreviousView?.(currentView);
- setFormHasChanges?.(false);
- setCurrentView?.(view);
- }}
+ onChangeView={handleChangeView}
disabled={mustDisableButtons}
formHasChanges={formHasChanges}
/>
-
- }
- tooltip={t("previous")}
- disabled={mustDisableButtons}
- onClick={() => tryAction(onPreviousClick)}
- />
- }
- tooltip={t("next")}
- disabled={mustDisableButtons}
- onClick={() => tryAction(onNextClick)}
- />
-
-
- }
- placement="bottomRight"
- disabled={mustDisableButtons}
- onRetrieveData={async () => [
- { label: t("actions"), items: toolbar?.action },
- ]}
- onItemClick={async (action: any) => {
- if (action) {
- const result = await saveDocument({ onFormSave });
- if (result.succeed) runAction(action);
- }
- }}
- />
- }
- disabled={mustDisableButtons}
- placement="bottomRight"
- onRetrieveData={async () => [
- { label: t("reports"), items: toolbar?.print },
- ]}
- onItemClick={async (report: any) => {
- if (report) {
- const result = await saveDocument({ onFormSave });
- if (result.succeed) {
- runAction({
- ...report,
- datas: {
- ...(report.datas || {}),
- ids: [result.currentId as number],
- },
- });
- }
- }
- }}
- />
- }
- disabled={mustDisableButtons}
- placement="bottomRight"
- onRetrieveData={async () => [
- { label: t("related"), items: toolbar?.relate },
- ]}
- onItemClick={async (relate: any) => {
- if (relate) {
- const result = await saveDocument({ onFormSave });
- if (result.succeed) {
- openRelate({
- relateData: relate,
- values: (formRef.current as any).getValues(),
- fields: (formRef.current as any).getFields(),
- action_id: relate.id,
- action_type: relate.type,
- });
- }
- }
- }}
+
+
+ } {...actionButtonProps} />
+ } {...printButtonProps} />
+ } {...relateButtonProps} />
{
- const result = await saveDocument({ onFormSave });
- if (result.succeed) {
- openDefaultActionForModel({
- ...getAttachmentActionPayload(
- currentModel as string,
- result.currentId as number,
- ),
- initialViewType: "form",
- });
- }
- }}
- onListAllAttachments={async () => {
- const result = await saveDocument({ onFormSave });
- if (result.succeed) {
- openDefaultActionForModel({
- ...getAttachmentActionPayload(
- currentModel as string,
- result.currentId as number,
- ),
- initialViewType: "tree",
- });
- }
- }}
- onViewAttachmentDetails={async (attachment: Attachment) => {
- const result = await saveDocument({ onFormSave });
- if (result.succeed) {
- openDefaultActionForModel({
- model: "ir.attachment",
- res_id: attachment.id,
- initialViewType: "form",
- });
- }
- }}
+ onAddNewAttachment={handleAddNewAttachment}
+ onListAllAttachments={handleListAllAttachments}
+ onViewAttachmentDetails={handleViewAttachmentDetails}
/>
@@ -400,18 +349,40 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
);
}
-export const ActionBarSeparator = () => ;
+const FormActionBar = memo(FormActionBarComponent);
-const saveDocument = async ({
- onFormSave,
-}: {
- onFormSave?: () => Promise<{ succeed: boolean; id: number }>;
-}): Promise<{ succeed: boolean; currentId?: number }> => {
- const result = await onFormSave?.();
- return result?.succeed
- ? { succeed: true, currentId: result.id }
- : { succeed: false, currentId: undefined };
-};
+const NavigationButtons = memo(
+ ({
+ disabled,
+ onPreviousClick,
+ onNextClick,
+ tryAction,
+ }: {
+ disabled: boolean;
+ onPreviousClick: () => void;
+ onNextClick: () => void;
+ tryAction: (action: () => void) => void;
+ }) => {
+ const { t } = useLocale();
+ return (
+
+ }
+ tooltip={t("previous")}
+ disabled={disabled}
+ onClick={() => tryAction(onPreviousClick)}
+ />
+ }
+ tooltip={t("next")}
+ disabled={disabled}
+ onClick={() => tryAction(onNextClick)}
+ />
+
+ );
+ },
+);
+NavigationButtons.displayName = "NavigationButtons";
const getAttachmentActionPayload = (res_model: string, res_id: number) => ({
model: "ir.attachment",
diff --git a/src/actionbar/GraphActionBar.tsx b/src/actionbar/GraphActionBar.tsx
index 9a9e44c94..50db7af55 100644
--- a/src/actionbar/GraphActionBar.tsx
+++ b/src/actionbar/GraphActionBar.tsx
@@ -11,7 +11,7 @@ import ButtonWithBadge from "./ButtonWithBadge";
import { ReloadOutlined, FilterOutlined } from "@ant-design/icons";
import { View } from "@/types";
import { ShareUrlButton } from "./ShareUrlButton";
-import { ActionBarSeparator } from "./FormActionBar";
+import { ActionBarSeparator } from "./ActionBarSeparator";
function GraphActionBar({ refreshGraph }: { refreshGraph: () => void }) {
const { t } = useLocale();
diff --git a/src/actionbar/TreeActionBar.tsx b/src/actionbar/TreeActionBar.tsx
index fc93f30e8..e01f5cd3f 100644
--- a/src/actionbar/TreeActionBar.tsx
+++ b/src/actionbar/TreeActionBar.tsx
@@ -1,4 +1,12 @@
-import { useContext, useEffect, useState, useRef, useMemo } from "react";
+import {
+ useContext,
+ useEffect,
+ useState,
+ useRef,
+ memo,
+ useCallback,
+ useMemo,
+} from "react";
import { Space, Spin } from "antd";
import ChangeViewButton from "./ChangeViewButton";
import {
@@ -20,10 +28,6 @@ import { useLocale, DropdownButton } from "@gisce/react-formiga-components";
import showConfirmDialog from "@/ui/ConfirmDialog";
import ConnectionProvider from "@/ConnectionProvider";
import showErrorDialog from "@/ui/ActionErrorDialog";
-import {
- ContentRootContext,
- ContentRootContextType,
-} from "@/context/ContentRootContext";
import ButtonWithBadge from "./ButtonWithBadge";
import { showLogInfo } from "@/helpers/logInfoHelper";
import SearchBar from "./SearchBar";
@@ -32,8 +36,12 @@ import { mergeParams } from "@/helpers/searchHelper";
import { useFeatureIsEnabled } from "@/context/ConfigContext";
import { ErpFeatureKeys } from "@/models/erpFeature";
import { useHotkeys } from "react-hotkeys-hook";
+import {
+ useTreeToolbarButtons,
+ useRunTreeAction,
+} from "@/hooks/useTreeToolbarButtons";
+import { ActionBarSeparator } from "./ActionBarSeparator";
import { ShareUrlButton } from "./ShareUrlButton";
-import { ActionBarSeparator } from "./FormActionBar";
type Props = {
parentContext?: any;
@@ -41,7 +49,11 @@ type Props = {
toolbar?: any;
};
-function TreeActionBar(props: Props) {
+function TreeActionBarComponent({
+ parentContext = {},
+ treeExpandable,
+ toolbar,
+}: Props) {
const {
availableViews,
currentView,
@@ -70,148 +82,191 @@ function TreeActionBar(props: Props) {
isInfiniteTree,
} = useContext(ActionViewContext) as ActionViewContextType;
- const { parentContext = {}, treeExpandable, toolbar } = props;
const advancedExportEnabled = useFeatureIsEnabled(
ErpFeatureKeys.FEATURE_ADVANCED_EXPORT,
);
const { t } = useLocale();
- const contentRootContext = useContext(
- ContentRootContext,
- ) as ContentRootContextType;
- const { processAction } = contentRootContext || {};
const [exportModalVisible, setExportModalVisible] = useState(false);
const isFirstMount = useRef(true);
- useHotkeys(
- "ctrl+l,command+l",
- () => {
- if (!isActive) {
- return;
- }
- if (previousView) {
- setPreviousView?.(currentView);
- setCurrentView?.(previousView);
- }
- },
- { enableOnFormTags: true, preventDefault: true },
- [previousView, currentView, isActive],
- );
-
- useHotkeys(
- "ctrl+f,command+f",
- () => {
- if (!isActive) {
- return;
- }
- setSearchVisible?.(!searchVisible);
- },
- { enableOnFormTags: true, preventDefault: true },
- [searchVisible],
- );
+ const handleRefresh = useCallback(() => {
+ searchTreeRef?.current?.refreshResults();
+ }, [searchTreeRef]);
- useEffect(() => {
- if (isInfiniteTree && searchTreeNameSearch === undefined) {
- if (isFirstMount.current) {
- isFirstMount.current = false;
- return;
- }
+ const { actionButtonProps, printButtonProps } = useTreeToolbarButtons({
+ toolbar,
+ disabled: treeIsLoading,
+ parentContext,
+ selectedRowItems,
+ onRefreshParentValues: handleRefresh,
+ });
- searchTreeRef?.current?.refreshResults();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isInfiniteTree, searchTreeNameSearch]);
+ const runAction = useRunTreeAction({
+ selectedRowItems,
+ onRefreshParentValues: handleRefresh,
+ });
- const hasNameSearch: boolean =
- searchTreeNameSearch !== undefined &&
- searchTreeNameSearch.trim().length > 0;
+ const hasNameSearch = useMemo(
+ () =>
+ searchTreeNameSearch !== undefined &&
+ searchTreeNameSearch.trim().length > 0,
+ [searchTreeNameSearch],
+ );
- function tryDuplicate() {
- showConfirmDialog({
- confirmMessage: t("confirmDuplicate"),
- t,
- onOk: () => {
- duplicate();
- },
- });
- }
+ const finalDomain = useMemo(() => {
+ const domain = searchTreeRef?.current?.getDomain();
+ return mergeParams(domain || [], searchParams || []);
+ }, [searchTreeRef, searchParams]);
- function tryDelete() {
- showConfirmDialog({
- confirmMessage: t("confirmRemove"),
- t,
- onOk: () => {
- remove();
- },
- });
- }
+ const handleDuplicate = useCallback(async () => {
+ try {
+ setDuplicatingItem?.(true);
+ const currentId = selectedRowItems![0].id;
+ const newId = await ConnectionProvider.getHandler().duplicate({
+ id: currentId,
+ model: currentModel!,
+ context: { ...parentContext },
+ });
+ if (newId) {
+ searchTreeRef?.current?.refreshResults();
+ }
+ } catch (e) {
+ showErrorDialog(e);
+ } finally {
+ setDuplicatingItem?.(false);
+ }
+ }, [
+ currentModel,
+ parentContext,
+ searchTreeRef,
+ selectedRowItems,
+ setDuplicatingItem,
+ ]);
- async function remove() {
+ const handleRemove = useCallback(async () => {
try {
setRemovingItem?.(true);
-
await ConnectionProvider.getHandler().deleteObjects({
model: currentModel!,
ids: selectedRowItems!.map((item) => item.id),
context: { ...parentContext },
});
-
setCurrentId?.(undefined);
setCurrentItemIndex?.(undefined);
-
searchTreeRef?.current?.refreshResults();
} catch (e) {
showErrorDialog(e);
} finally {
setRemovingItem?.(false);
}
- }
+ }, [
+ currentModel,
+ parentContext,
+ searchTreeRef,
+ selectedRowItems,
+ setCurrentId,
+ setCurrentItemIndex,
+ setRemovingItem,
+ ]);
- async function duplicate() {
- try {
- setDuplicatingItem?.(true);
+ const handleChangeView = useCallback(
+ (newView: any) => {
+ setPreviousView?.(currentView);
+ setCurrentView?.(newView);
+ },
+ [currentView, setPreviousView, setCurrentView],
+ );
- const currentId = selectedRowItems![0].id;
+ const handleSearch = useCallback(
+ (searchString?: string) => {
+ if (searchString && searchString.trim().length > 0) {
+ setSearchTreeNameSearch?.(searchString);
+ } else {
+ setSearchTreeNameSearch?.(undefined);
+ if (!isInfiniteTree) {
+ searchTreeRef?.current?.refreshResults();
+ }
+ }
+ },
+ [isInfiniteTree, searchTreeRef, setSearchTreeNameSearch],
+ );
- const newId = await ConnectionProvider.getHandler().duplicate({
- id: currentId,
- model: currentModel!,
- context: { ...parentContext },
- });
+ const handleExportAction = useCallback(
+ (itemClicked: any) => {
+ if (itemClicked.id === "print_screen") {
+ let idsToExport = selectedRowItems?.map((item) => item.id) || [];
+ if (idsToExport.length === 0) {
+ idsToExport = results?.map((item) => item.id) || [];
+ }
- if (newId) {
- searchTreeRef?.current?.refreshResults();
+ runAction(
+ {
+ id: -1,
+ model: currentModel,
+ report_name: "printscreen.list",
+ type: "ir.actions.report.xml",
+ datas: {
+ model: currentModel,
+ ids: idsToExport,
+ },
+ },
+ parentContext,
+ );
+ return;
}
- } catch (e) {
- showErrorDialog(e);
- } finally {
- setDuplicatingItem?.(false);
+ setExportModalVisible(true);
+ },
+ [currentModel, parentContext, results, runAction, selectedRowItems],
+ );
+
+ useEffect(() => {
+ if (isInfiniteTree && searchTreeNameSearch === undefined) {
+ if (isFirstMount.current) {
+ isFirstMount.current = false;
+ return;
+ }
+ searchTreeRef?.current?.refreshResults();
}
- }
+ }, [isInfiniteTree, searchTreeNameSearch, searchTreeRef]);
- function runAction(actionData: any) {
- processAction?.({
- actionData,
- values: {
- active_id: selectedRowItems?.map((item) => item.id)[0],
- active_ids: selectedRowItems?.map((item) => item.id),
- },
- fields: {},
- context: {
- ...parentContext,
- active_id: selectedRowItems?.map((item) => item.id)[0],
- active_ids: selectedRowItems?.map((item) => item.id),
- },
- onRefreshParentValues: () => {
- searchTreeRef?.current?.refreshResults();
- },
+ useHotkeys(
+ "ctrl+l,command+l",
+ () => {
+ if (!isActive) return;
+ if (previousView) {
+ setPreviousView?.(currentView);
+ setCurrentView?.(previousView);
+ }
+ },
+ { enableOnFormTags: true, preventDefault: true },
+ [previousView, currentView, isActive, setPreviousView, setCurrentView],
+ );
+
+ useHotkeys(
+ "ctrl+f,command+f",
+ () => {
+ if (!isActive) return;
+ setSearchVisible?.(!searchVisible);
+ },
+ { enableOnFormTags: true, preventDefault: true },
+ [searchVisible, isActive, setSearchVisible],
+ );
+
+ const tryDuplicate = useCallback(() => {
+ showConfirmDialog({
+ confirmMessage: t("confirmDuplicate"),
+ t,
+ onOk: handleDuplicate,
});
- }
+ }, [handleDuplicate, t]);
- const finalDomain = (() => {
- const domain = searchTreeRef?.current?.getDomain();
- const finalValues = mergeParams(domain || [], searchParams || []);
- return finalValues;
- })();
+ const tryDelete = useCallback(() => {
+ showConfirmDialog({
+ confirmMessage: t("confirmRemove"),
+ t,
+ onOk: handleRemove,
+ });
+ }, [handleRemove, t]);
return (
@@ -222,38 +277,25 @@ function TreeActionBar(props: Props) {
>
)}
- {treeExpandable ? null : (
+ {!treeExpandable && (
<>
{
- if (searchString && searchString.trim().length > 0) {
- setSearchTreeNameSearch?.(searchString);
- } else {
- setSearchTreeNameSearch?.(undefined);
- if (!isInfiniteTree) {
- searchTreeRef?.current?.refreshResults();
- }
- }
- }}
+ onSearch={handleSearch}
+ />
+
+ }
+ tooltip={t("advanced_search")}
+ type={searchVisible ? "primary" : "default"}
+ onClick={() => setSearchVisible?.(!searchVisible)}
+ disabled={duplicatingItem || removingItem || treeIsLoading}
+ badgeNumber={searchParams?.length}
/>
- {!treeExpandable && (
-
- }
- tooltip={t("advanced_search")}
- type={searchVisible ? "primary" : "default"}
- onClick={() => {
- setSearchVisible?.(!searchVisible);
- }}
- disabled={duplicatingItem || removingItem || treeIsLoading}
- badgeNumber={searchParams?.length}
- />
- )}
0) || treeIsLoading
}
- loading={false}
- onClick={() => {
- showLogInfo(currentModel!, selectedRowItems![0].id, t);
- }}
+ onClick={() => showLogInfo(currentModel!, selectedRowItems![0].id, t)}
/>
}
tooltip={t("refresh")}
disabled={duplicatingItem || removingItem || treeIsLoading}
- loading={false}
- onClick={() => {
- searchTreeRef?.current?.refreshResults();
- }}
+ onClick={handleRefresh}
/>
{!treeExpandable && (
<>
@@ -307,84 +343,21 @@ function TreeActionBar(props: Props) {
{
- setPreviousView?.(currentView);
- setCurrentView?.(newView);
- }}
+ onChangeView={handleChangeView}
previousView={previousView}
disabled={treeIsLoading}
/>
>
)}
- }
- placement="bottomRight"
- disabled={
- !(selectedRowItems && selectedRowItems?.length > 0) || treeIsLoading
- }
- onRetrieveData={async () => [
- { label: t("actions"), items: toolbar?.action || [] },
- ]}
- onItemClick={(action: any) => {
- if (!action) {
- return;
- }
-
- runAction(action);
- }}
- />
- }
- placement="bottomRight"
- disabled={
- !(selectedRowItems && selectedRowItems?.length > 0) || treeIsLoading
- }
- onRetrieveData={async () => {
- return [{ label: t("reports"), items: toolbar?.print || [] }];
- }}
- onItemClick={(report: any) => {
- if (!report) {
- return;
- }
-
- runAction({
- ...report,
- datas: {
- ...(report.datas || {}),
- ids: selectedRowItems!.map((item) => item.id),
- },
- });
- }}
- />
+ } {...actionButtonProps} />
+ } {...printButtonProps} />
{advancedExportEnabled && (
<>
(
-
- )}
- />
- }
+ icon={}
onRetrieveData={async () => [
{
label: t("export"),
@@ -400,30 +373,7 @@ function TreeActionBar(props: Props) {
],
},
]}
- onItemClick={(itemClicked: any) => {
- if (itemClicked.id === "print_screen") {
- let idsToExport =
- selectedRowItems?.map((item) => item.id) || [];
-
- if (idsToExport.length === 0) {
- idsToExport = results?.map((item) => item.id) || [];
- }
-
- runAction({
- id: -1,
- model: currentModel,
- report_name: "printscreen.list",
- type: "ir.actions.report.xml",
- datas: {
- model: currentModel,
- ids: idsToExport,
- },
- });
- return;
- }
-
- setExportModalVisible(true);
- }}
+ onItemClick={handleExportAction}
disabled={
duplicatingItem || removingItem || treeIsLoading || hasNameSearch
}
@@ -447,4 +397,31 @@ function TreeActionBar(props: Props) {
);
}
+const TreeActionBar = memo(TreeActionBarComponent);
export default TreeActionBar;
+
+const ExportIcon = memo(() => (
+ (
+
+ )}
+ />
+));
+
+ExportIcon.displayName = "ExportIcon";
diff --git a/src/context/TabManagerContext.tsx b/src/context/TabManagerContext.tsx
index 8a9564653..58c30b789 100644
--- a/src/context/TabManagerContext.tsx
+++ b/src/context/TabManagerContext.tsx
@@ -79,6 +79,7 @@ const TabManagerProvider = (props: TabManagerProviderProps): any => {
useEffect(() => {
if (noTabs) {
document.title = title;
+ window.history.replaceState({}, "", "/");
}
}, [noTabs, title]);
diff --git a/src/hooks/useFormToolbarButtons.ts b/src/hooks/useFormToolbarButtons.ts
new file mode 100644
index 000000000..f5892a226
--- /dev/null
+++ b/src/hooks/useFormToolbarButtons.ts
@@ -0,0 +1,132 @@
+import { useCallback, useContext, RefObject } from "react";
+import { useLocale } from "@gisce/react-formiga-components";
+import {
+ ContentRootContext,
+ ContentRootContextType,
+} from "@/context/ContentRootContext";
+import {
+ TabManagerContext,
+ TabManagerContextType,
+} from "@/context/TabManagerContext";
+
+interface UseFormToolbarButtonsProps {
+ toolbar: any;
+ mustDisableButtons?: boolean;
+ formRef: RefObject;
+ onRefreshParentValues?: () => void;
+}
+
+interface SaveDocumentResult {
+ succeed: boolean;
+ currentId?: number;
+}
+
+export const useFormToolbarButtons = ({
+ toolbar,
+ mustDisableButtons = false,
+ formRef,
+ onRefreshParentValues,
+}: UseFormToolbarButtonsProps) => {
+ const { t } = useLocale();
+ const contentRootContext = useContext(
+ ContentRootContext,
+ ) as ContentRootContextType;
+ const tabManagerContext = useContext(
+ TabManagerContext,
+ ) as TabManagerContextType;
+
+ const { processAction } = contentRootContext || {};
+ const { openRelate } = tabManagerContext || {};
+
+ const onFormSave = useCallback(async () => {
+ return await formRef.current?.submitForm();
+ }, [formRef]);
+
+ const runAction = useCallback(
+ (actionData: any) => {
+ processAction?.({
+ actionData,
+ values: formRef.current?.getValues(),
+ fields: formRef.current?.getFields(),
+ context: formRef.current?.getContext(),
+ onRefreshParentValues,
+ });
+ },
+ [formRef, processAction, onRefreshParentValues],
+ );
+
+ const actionButtonProps = {
+ disabled: mustDisableButtons,
+ placement: "bottomRight" as const,
+ onRetrieveData: async () => [
+ { label: t("actions"), items: toolbar?.action },
+ ],
+ onItemClick: async (action: any) => {
+ if (action) {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) runAction(action);
+ }
+ },
+ };
+
+ const printButtonProps = {
+ disabled: mustDisableButtons,
+ placement: "bottomRight" as const,
+ onRetrieveData: async () => [
+ { label: t("reports"), items: toolbar?.print },
+ ],
+ onItemClick: async (report: any) => {
+ if (report) {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) {
+ runAction({
+ ...report,
+ datas: {
+ ...(report.datas || {}),
+ ids: [result.currentId as number],
+ },
+ });
+ }
+ }
+ },
+ };
+
+ const relateButtonProps = {
+ disabled: mustDisableButtons,
+ placement: "bottomRight" as const,
+ onRetrieveData: async () => [
+ { label: t("related"), items: toolbar?.relate },
+ ],
+ onItemClick: async (relate: any) => {
+ if (relate) {
+ const result = await saveDocument({ onFormSave });
+ if (result.succeed) {
+ openRelate({
+ relateData: relate,
+ values: formRef.current?.getValues(),
+ fields: formRef.current?.getFields(),
+ action_id: relate.id,
+ action_type: relate.type,
+ });
+ }
+ }
+ },
+ };
+
+ return {
+ actionButtonProps,
+ printButtonProps,
+ relateButtonProps,
+ };
+};
+
+export const saveDocument = async ({
+ onFormSave,
+}: {
+ onFormSave?: () => Promise<{ succeed: boolean; id: number }>;
+}): Promise => {
+ const result = await onFormSave?.();
+ return result?.succeed
+ ? { succeed: true, currentId: result.id }
+ : { succeed: false, currentId: undefined };
+};
diff --git a/src/hooks/useTreeToolbarButtons.ts b/src/hooks/useTreeToolbarButtons.ts
new file mode 100644
index 000000000..7343620f3
--- /dev/null
+++ b/src/hooks/useTreeToolbarButtons.ts
@@ -0,0 +1,104 @@
+import { useCallback, useContext } from "react";
+import { useLocale } from "@gisce/react-formiga-components";
+import {
+ ContentRootContext,
+ ContentRootContextType,
+} from "@/context/ContentRootContext";
+
+interface UseTreeToolbarButtonsProps {
+ toolbar: any;
+ disabled?: boolean;
+ parentContext?: any;
+ selectedRowItems?: any[];
+ onRefreshParentValues?: () => void;
+}
+
+export const useRunTreeAction = ({
+ selectedRowItems,
+ onRefreshParentValues,
+}: {
+ selectedRowItems?: any[];
+ onRefreshParentValues?: () => void;
+}) => {
+ const contentRootContext = useContext(
+ ContentRootContext,
+ ) as ContentRootContextType;
+ const { processAction } = contentRootContext || {};
+
+ return useCallback(
+ (actionData: any, context: any = {}) => {
+ processAction?.({
+ actionData,
+ values: {
+ active_id: selectedRowItems?.map((item) => item.id)[0],
+ active_ids: selectedRowItems?.map((item) => item.id),
+ },
+ fields: {},
+ context: {
+ ...context,
+ active_id: selectedRowItems?.map((item) => item.id)[0],
+ active_ids: selectedRowItems?.map((item) => item.id),
+ },
+ onRefreshParentValues,
+ });
+ },
+ [processAction, selectedRowItems, onRefreshParentValues],
+ );
+};
+
+export const useTreeToolbarButtons = ({
+ toolbar,
+ disabled = false,
+ parentContext = {},
+ selectedRowItems = [],
+ onRefreshParentValues,
+}: UseTreeToolbarButtonsProps) => {
+ const { t } = useLocale();
+ const runAction = useRunTreeAction({
+ selectedRowItems,
+ onRefreshParentValues,
+ });
+
+ const actionButtonProps = {
+ placement: "bottomRight" as const,
+ disabled: !selectedRowItems?.length || disabled,
+ onRetrieveData: async () => [
+ { label: t("actions"), items: toolbar?.action || [] },
+ ],
+ onItemClick: (action: any) => {
+ if (!action) {
+ return;
+ }
+ runAction(action, parentContext);
+ },
+ };
+
+ const printButtonProps = {
+ placement: "bottomRight" as const,
+ disabled: !selectedRowItems?.length || disabled,
+ onRetrieveData: async () => [
+ { label: t("reports"), items: toolbar?.print || [] },
+ ],
+ onItemClick: (report: any) => {
+ if (!report) {
+ return;
+ }
+
+ runAction(
+ {
+ ...report,
+ datas: {
+ ...(report.datas || {}),
+ ids: selectedRowItems!.map((item) => item.id),
+ },
+ },
+ parentContext,
+ );
+ },
+ };
+
+ return {
+ actionButtonProps,
+ printButtonProps,
+ };
+};
diff --git a/src/locales/ca_ES.ts b/src/locales/ca_ES.ts
index e89064c04..447bd8d51 100644
--- a/src/locales/ca_ES.ts
+++ b/src/locales/ca_ES.ts
@@ -109,6 +109,12 @@ export default {
not: "No",
loading: "Carregant...",
pendingToCalculate: "Pendent de calcular",
+ createNewItem: "Crear nou element",
+ searchExistingItem: "Cercar element existent",
+ toggleViewMode: "Canviar mode de vista",
+ previousItem: "Element anterior",
+ nextItem: "Element següent",
+ unlink: "Desvincular",
share: "Compartir URL",
copyToClipboard: "Copiar al porta-retalls",
urlCopiedToClipboard: "URL copiada al porta-retalls",
diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts
index 2525f6c2c..86572832a 100644
--- a/src/locales/en_US.ts
+++ b/src/locales/en_US.ts
@@ -105,6 +105,12 @@ export default {
not: "Not",
loading: "Loading...",
pendingToCalculate: "Pending to calculate",
+ createNewItem: "Create new item",
+ searchExistingItem: "Search existing item",
+ toggleViewMode: "Toggle view mode",
+ previousItem: "Previous item",
+ nextItem: "Next item",
+ unlink: "Unlink",
share: "Compartir URL",
urlCopiedToClipboard: "URL copied to clipboard",
copyToClipboard: "Copy to clipboard",
diff --git a/src/locales/es_ES.ts b/src/locales/es_ES.ts
index 5e0e903b0..08c12144e 100644
--- a/src/locales/es_ES.ts
+++ b/src/locales/es_ES.ts
@@ -111,6 +111,12 @@ export default {
not: "No",
loading: "Cargando...",
pendingToCalculate: "Pendiente de calcular",
+ createNewItem: "Crear nuevo elemento",
+ searchExistingItem: "Buscar elemento existente",
+ toggleViewMode: "Cambiar modo de vista",
+ previousItem: "Elemento anterior",
+ nextItem: "Elemento siguiente",
+ unlink: "Desvincular",
share: "Compartir URL",
urlCopiedToClipboard: "URL copiada al portapapeles",
copyToClipboard: "Copiar al portapapeles",
diff --git a/src/views/ActionView.tsx b/src/views/ActionView.tsx
index 8d59d4db4..bdccc7bf3 100644
--- a/src/views/ActionView.tsx
+++ b/src/views/ActionView.tsx
@@ -419,8 +419,6 @@ function ActionView(props: Props, ref: any) {
}
}
- function content() {}
-
function onNewClicked() {
if (currentId === undefined && currentView!.type === "form") {
(formRef.current as any).clearAndReload();
diff --git a/src/views/actionViews/FormActionView.tsx b/src/views/actionViews/FormActionView.tsx
index 165d57c08..dc5102a0e 100644
--- a/src/views/actionViews/FormActionView.tsx
+++ b/src/views/actionViews/FormActionView.tsx
@@ -2,7 +2,6 @@ import FormActionBar from "@/actionbar/FormActionBar";
import { FormView } from "@/types";
import TitleHeader from "@/ui/TitleHeader";
import Form from "@/widgets/views/Form";
-import React from "react";
export type FormActionViewProps = {
formView?: FormView;
diff --git a/src/widgets/base/one2many/One2many.tsx b/src/widgets/base/one2many/One2many.tsx
index 25437defa..b4478386f 100644
--- a/src/widgets/base/one2many/One2many.tsx
+++ b/src/widgets/base/one2many/One2many.tsx
@@ -2,7 +2,7 @@ import { useContext, useState } from "react";
import { One2many as One2manyOoui } from "@gisce/ooui";
import Field from "@/common/Field";
import { Spin, Alert } from "antd";
-import { Views, ViewType } from "@/types";
+import { FormView, TreeView, Views, ViewType } from "@/types";
import ConnectionProvider from "@/ConnectionProvider";
import One2manyProvider from "@/context/One2manyContext";
import { One2manyInput } from "@/widgets/base/one2many/One2manyInput";
@@ -36,9 +36,21 @@ export const One2many = (props: Props) => {
}, [ooui]);
const getViewData = async (type: ViewType) => {
+ const getViewPromise = ConnectionProvider.getHandler().getView({
+ model: relation,
+ type,
+ context: { ...getContext?.(), ...context },
+ });
+
if (oouiViews && oouiViews[type]) {
- return oouiViews[type];
+ const view = oouiViews[type];
+ if (!view.toolbar && (type === "form" || type === "tree")) {
+ const viewWithToolbar: TreeView | FormView = await getViewPromise;
+ return { ...view, toolbar: viewWithToolbar.toolbar };
+ }
+ return view;
}
+
return await ConnectionProvider.getHandler().getView({
model: relation,
type,
diff --git a/src/widgets/base/one2many/One2manyForm.tsx b/src/widgets/base/one2many/One2manyForm.tsx
index b9a07f9cb..f93a91e72 100644
--- a/src/widgets/base/one2many/One2manyForm.tsx
+++ b/src/widgets/base/one2many/One2manyForm.tsx
@@ -4,7 +4,7 @@ import {
One2manyContext,
One2manyContextType,
} from "@/context/One2manyContext";
-import { useContext } from "react";
+import { useContext, forwardRef } from "react";
import { One2manyItem } from "./One2manyInput";
import { filterDuplicateItems } from "@/helpers/one2manyHelper";
import { useLocale } from "@gisce/react-formiga-components";
@@ -18,51 +18,58 @@ export type One2manyFormProps = {
onChange: (items: One2manyItem[]) => void;
};
-export const One2manyForm = ({
- formView,
- items,
- context,
- relation,
- readOnly,
- onChange,
-}: One2manyFormProps) => {
- const { itemIndex } = useContext(One2manyContext) as One2manyContextType;
+export const One2manyForm = forwardRef(
+ (
+ {
+ formView,
+ items,
+ context,
+ relation,
+ readOnly,
+ onChange,
+ }: One2manyFormProps,
+ ref,
+ ) => {
+ const { itemIndex } = useContext(One2manyContext) as One2manyContextType;
+ const { t } = useLocale();
- const { t } = useLocale();
+ if (items.length === 0) {
+ return t("noCurrentEntries");
+ }
- if (items.length === 0) {
- return t("noCurrentEntries");
- }
+ return (
+