Skip to content

Commit 4c09f98

Browse files
Merge pull request #823 from gisce/feat/add-share-url-button
feat: add ShareUrlButton functionality
2 parents 9281128 + 644e14e commit 4c09f98

21 files changed

+776
-225
lines changed

src/actionbar/ActionButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ButtonProps } from "antd";
55

66
type Props = ButtonProps & {
77
tooltip: string;
8-
onClick: any;
8+
onClick?: any;
99
icon: any;
1010
disabled?: boolean;
1111
label?: string;

src/actionbar/DashboardActionBar.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext } from "react";
1+
import { useContext } from "react";
22
import {
33
DashboardActionContext,
44
DashboardActionContextType,
@@ -11,6 +11,12 @@ import {
1111
BorderOuterOutlined,
1212
} from "@ant-design/icons";
1313
import { useLocale } from "@gisce/react-formiga-components";
14+
import {
15+
ActionViewContext,
16+
ActionViewContextType,
17+
} from "@/context/ActionViewContext";
18+
import { ShareUrlButton } from "./ShareUrlButton";
19+
import { ActionBarSeparator } from "./FormActionBar";
1420

1521
function DashboardActionBar() {
1622
const { isLoading, dashboardRef, moveItemsEnabled, setMoveItemsEnabled } =
@@ -33,7 +39,7 @@ function DashboardActionBar() {
3339
setMoveItemsEnabled(!moveItemsEnabled);
3440
}}
3541
/>
36-
{separator()}
42+
<ActionBarSeparator />
3743
<ActionButton
3844
icon={<SettingOutlined />}
3945
tooltip={t("configDashboard")}
@@ -52,12 +58,10 @@ function DashboardActionBar() {
5258
dashboardRef?.current.refresh();
5359
}}
5460
/>
61+
<ActionBarSeparator />
62+
<ShareUrlButton />
5563
</Space>
5664
);
5765
}
5866

59-
function separator() {
60-
return <div className="inline-block w-2" />;
61-
}
62-
6367
export default DashboardActionBar;

src/actionbar/FormActionBar.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
import AttachmentsButton from "./AttachmentsButton";
3535
import { Attachment } from "./AttachmentsButtonWrapper";
3636
import { useNextPrevious } from "./useNextPrevious";
37+
import { ShareUrlButton } from "./ShareUrlButton";
3738

3839
function FormActionBar({ toolbar }: { toolbar: any }) {
3940
const contentRootContext = useContext(
@@ -197,8 +198,8 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
197198
{formIsLoading && (
198199
<>
199200
<Spin />
200-
{separator()}
201-
{separator()}
201+
<ActionBarSeparator />
202+
<ActionBarSeparator />
202203
</>
203204
)}
204205
<NewButton disabled={mustDisableButtons} />
@@ -237,7 +238,7 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
237238
})
238239
}
239240
/>
240-
{separator()}
241+
<ActionBarSeparator />
241242
<ActionButton
242243
icon={<InfoCircleOutlined />}
243244
tooltip={t("showLogs")}
@@ -250,7 +251,7 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
250251
disabled={mustDisableButtons || currentId === undefined}
251252
onClick={() => tryAction(() => (formRef.current as any).fetchValues())}
252253
/>
253-
{separator()}
254+
<ActionBarSeparator />
254255
<ChangeViewButton
255256
currentView={currentView}
256257
previousView={previousView}
@@ -263,7 +264,7 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
263264
disabled={mustDisableButtons}
264265
formHasChanges={formHasChanges}
265266
/>
266-
{separator()}
267+
<ActionBarSeparator />
267268
<Space>
268269
<ActionButton
269270
icon={<LeftOutlined />}
@@ -278,7 +279,7 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
278279
onClick={() => tryAction(onNextClick)}
279280
/>
280281
</Space>
281-
{separator()}
282+
<ActionBarSeparator />
282283
<DropdownButton
283284
icon={<ThunderboltOutlined />}
284285
placement="bottomRight"
@@ -375,11 +376,13 @@ function FormActionBar({ toolbar }: { toolbar: any }) {
375376
}
376377
}}
377378
/>
379+
<ActionBarSeparator />
380+
<ShareUrlButton res_id={currentId} />
378381
</Space>
379382
);
380383
}
381384

382-
const separator = () => <div className="inline-block w-2" />;
385+
export const ActionBarSeparator = () => <div className="inline-block w-2" />;
383386

384387
const saveDocument = async ({
385388
onFormSave,

src/actionbar/GraphActionBar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useContext } from "react";
1+
import { useContext } from "react";
22
import { Space } from "antd";
33
import ChangeViewButton from "./ChangeViewButton";
44
import {
@@ -10,6 +10,8 @@ import { useLocale } from "@gisce/react-formiga-components";
1010
import ButtonWithBadge from "./ButtonWithBadge";
1111
import { ReloadOutlined, FilterOutlined } from "@ant-design/icons";
1212
import { View } from "@/types";
13+
import { ShareUrlButton } from "./ShareUrlButton";
14+
import { ActionBarSeparator } from "./FormActionBar";
1315

1416
function GraphActionBar({ refreshGraph }: { refreshGraph: () => void }) {
1517
const { t } = useLocale();
@@ -60,6 +62,8 @@ function GraphActionBar({ refreshGraph }: { refreshGraph: () => void }) {
6062
disabled={false}
6163
previousView={previousView}
6264
/>
65+
<ActionBarSeparator />
66+
<ShareUrlButton searchParams={searchParams} />
6367
</Space>
6468
);
6569
}

src/actionbar/ShareUrlButton.tsx

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { useState, useCallback } from "react";
2+
import { Button, Input, message, Space, Popover, theme } from "antd";
3+
import { CopyOutlined, CheckOutlined } from "@ant-design/icons";
4+
import { useLocale } from "@gisce/react-formiga-components";
5+
import { createShareOpenUrl } from "@/helpers/shareUrlHelper";
6+
import ActionButton from "./ActionButton";
7+
import { IconExternalLink, IconShare2 } from "@tabler/icons-react";
8+
import { useTabs } from "@/context/TabManagerContext";
9+
import { useActionViewContext } from "@/context/ActionViewContext";
10+
import { ActionInfo } from "@/types";
11+
12+
export type ShareUrlButtonProps = {
13+
res_id?: number;
14+
searchParams?: any[];
15+
};
16+
17+
export function ShareUrlButton({ res_id, searchParams }: ShareUrlButtonProps) {
18+
const { currentView } = useActionViewContext();
19+
const initialView = {
20+
id: currentView.view_id,
21+
type: currentView.type,
22+
};
23+
const { token } = theme.useToken();
24+
const { t } = useLocale();
25+
const [isCopied, setIsCopied] = useState(false);
26+
const { currentTab } = useTabs();
27+
28+
const copyToClipboard = useCallback(
29+
(url: string) => {
30+
try {
31+
// Create a temporary textarea element
32+
const tempInput = document.createElement("textarea");
33+
tempInput.value = url;
34+
document.body.appendChild(tempInput);
35+
36+
// Select the text in the textarea
37+
tempInput.select();
38+
tempInput.setSelectionRange(0, 99999); // For mobile devices
39+
40+
// Copy the text using execCommand
41+
42+
const successful = document.execCommand("copy");
43+
44+
// Clean up the temporary element
45+
document.body.removeChild(tempInput);
46+
47+
// Handle success or failure
48+
if (successful) {
49+
setIsCopied(true);
50+
message.success(t("urlCopiedToClipboard"));
51+
setTimeout(() => setIsCopied(false), 2000);
52+
} else {
53+
throw new Error("Copy command was unsuccessful.");
54+
}
55+
} catch (err) {
56+
console.error("Error copying to clipboard:", err);
57+
message.error(t("errorCopyingToClipboard"));
58+
}
59+
},
60+
[setIsCopied, t],
61+
);
62+
63+
if (!currentTab?.action) return null;
64+
const { action_id } = currentTab?.action || {};
65+
const finalActionData: ActionInfo = {
66+
...currentTab.action,
67+
...(initialView && { initialView }),
68+
...(searchParams && { searchParams }),
69+
...(res_id && { res_id }),
70+
};
71+
const shareUrl = createShareOpenUrl(finalActionData);
72+
const { type } = initialView || {};
73+
74+
let moreDataNeededForCopying = !action_id;
75+
if (type === "form") {
76+
moreDataNeededForCopying = !action_id || !res_id;
77+
}
78+
79+
const popoverContent = (
80+
<div style={{ padding: 2 }}>
81+
<Space.Compact style={{ width: "100%" }}>
82+
<Input
83+
value={shareUrl}
84+
readOnly
85+
style={{
86+
borderRadius: 6,
87+
flex: 1,
88+
marginRight: 8,
89+
minWidth: 300,
90+
}}
91+
/>
92+
<Button
93+
title={t("copyToClipboard")}
94+
type="text"
95+
style={{
96+
marginRight: 8,
97+
}}
98+
icon={
99+
isCopied ? (
100+
<CheckOutlined style={{ color: token.colorSuccess }} />
101+
) : (
102+
<CopyOutlined style={{ color: token.colorTextSecondary }} />
103+
)
104+
}
105+
onClick={() => copyToClipboard(shareUrl)}
106+
/>
107+
<Button
108+
title={t("openInNewTab")}
109+
style={{ height: 28 }}
110+
type="text"
111+
icon={<IconExternalLink size={18} color={token.colorTextSecondary} />}
112+
onClick={() => window.open(shareUrl, "_blank", "noopener,noreferrer")}
113+
/>
114+
</Space.Compact>
115+
</div>
116+
);
117+
118+
return (
119+
<div style={{ maxHeight: 28 }}>
120+
<Popover content={popoverContent} trigger="click" placement="bottom">
121+
<ActionButton
122+
style={{ height: 28 }}
123+
icon={<IconShare2 size={16} color={token.colorTextSecondary} />}
124+
disabled={moreDataNeededForCopying}
125+
tooltip={t("share")}
126+
/>
127+
</Popover>
128+
</div>
129+
);
130+
}

src/actionbar/TreeActionBar.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext, useEffect, useState, useRef } from "react";
1+
import { useContext, useEffect, useState, useRef, useMemo } from "react";
22
import { Space, Spin } from "antd";
33
import ChangeViewButton from "./ChangeViewButton";
44
import {
@@ -32,6 +32,8 @@ import { mergeParams } from "@/helpers/searchHelper";
3232
import { useFeatureIsEnabled } from "@/context/ConfigContext";
3333
import { ErpFeatureKeys } from "@/models/erpFeature";
3434
import { useHotkeys } from "react-hotkeys-hook";
35+
import { ShareUrlButton } from "./ShareUrlButton";
36+
import { ActionBarSeparator } from "./FormActionBar";
3537

3638
type Props = {
3739
parentContext?: any;
@@ -205,13 +207,19 @@ function TreeActionBar(props: Props) {
205207
});
206208
}
207209

210+
const finalDomain = (() => {
211+
const domain = searchTreeRef?.current?.getDomain();
212+
const finalValues = mergeParams(domain || [], searchParams || []);
213+
return finalValues;
214+
})();
215+
208216
return (
209217
<Space wrap={true}>
210218
{treeIsLoading && (
211219
<>
212220
<Spin />
213-
{separator()}
214-
{separator()}
221+
<ActionBarSeparator />
222+
<ActionBarSeparator />
215223
</>
216224
)}
217225
{treeExpandable ? null : (
@@ -246,7 +254,7 @@ function TreeActionBar(props: Props) {
246254
badgeNumber={searchParams?.length}
247255
/>
248256
)}
249-
{separator()}
257+
<ActionBarSeparator />
250258
<NewButton disabled={treeIsLoading} />
251259
<ActionButton
252260
icon={<CopyOutlined />}
@@ -270,7 +278,7 @@ function TreeActionBar(props: Props) {
270278
loading={removingItem}
271279
onClick={tryDelete}
272280
/>
273-
{separator()}
281+
<ActionBarSeparator />
274282
</>
275283
)}
276284
<ActionButton
@@ -295,7 +303,7 @@ function TreeActionBar(props: Props) {
295303
/>
296304
{!treeExpandable && (
297305
<>
298-
{separator()}
306+
<ActionBarSeparator />
299307
<ChangeViewButton
300308
currentView={currentView}
301309
availableViews={availableViews}
@@ -308,7 +316,7 @@ function TreeActionBar(props: Props) {
308316
/>
309317
</>
310318
)}
311-
{separator()}
319+
<ActionBarSeparator />
312320
<DropdownButton
313321
icon={<ThunderboltOutlined />}
314322
placement="bottomRight"
@@ -351,7 +359,7 @@ function TreeActionBar(props: Props) {
351359
/>
352360
{advancedExportEnabled && (
353361
<>
354-
{separator()}
362+
<ActionBarSeparator />
355363
<DropdownButton
356364
placement="bottomRight"
357365
icon={
@@ -424,10 +432,7 @@ function TreeActionBar(props: Props) {
424432
visible={exportModalVisible}
425433
onClose={() => setExportModalVisible(false)}
426434
model={currentModel!}
427-
domain={mergeParams(
428-
searchTreeRef?.current?.getDomain() || [],
429-
searchParams || [],
430-
)}
435+
domain={finalDomain}
431436
limit={limit}
432437
totalRegisters={totalItems || 0}
433438
selectedRegistersToExport={selectedRowItems}
@@ -436,12 +441,10 @@ function TreeActionBar(props: Props) {
436441
/>
437442
</>
438443
)}
444+
<ActionBarSeparator />
445+
<ShareUrlButton searchParams={searchParams} />
439446
</Space>
440447
);
441448
}
442449

443-
function separator() {
444-
return <div className="inline-block w-2" />;
445-
}
446-
447450
export default TreeActionBar;

0 commit comments

Comments
 (0)