@burdenoff/microfe-files 2026.625.4 → 2026.626.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -7,60 +7,61 @@ import { useState as l } from "react";
|
|
|
7
7
|
import { AlertCircle as u, CheckCircle2 as d, Clock as f, Download as p, Loader2 as m, RotateCcw as h, Tag as g, Trash2 as _, X as v } from "lucide-react";
|
|
8
8
|
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
9
9
|
import { useI18n as S } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { IllustratedEmptyState as C } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/files/components/FileVersionPanel.tsx
|
|
11
|
-
var
|
|
12
|
-
let { t:
|
|
13
|
-
|
|
14
|
-
title: `${r(
|
|
15
|
-
description: r(
|
|
16
|
-
confirmLabel: r(
|
|
12
|
+
var w = ({ file: w, onClose: T }) => {
|
|
13
|
+
let { t: E } = S(), [D, O] = l(null), [k, A] = l(null), j = e(), { data: M, isLoading: N, error: P } = o(w.id), F = M?.versions || [], I = s(), L = a(), R = c(), z = async (e, t) => {
|
|
14
|
+
A({
|
|
15
|
+
title: `${r(E, "files.version.restoreVersion", "Restore version")} ${t}`,
|
|
16
|
+
description: r(E, "files.version.restoreDescription", "This will create a new version with the restored content."),
|
|
17
|
+
confirmLabel: r(E, "files.version.restore", "Restore"),
|
|
17
18
|
onConfirm: async () => {
|
|
18
19
|
try {
|
|
19
|
-
await
|
|
20
|
+
await I.mutateAsync({
|
|
20
21
|
versionId: e,
|
|
21
22
|
createNewVersion: !0,
|
|
22
|
-
changeMessage: `${r(
|
|
23
|
-
}),
|
|
23
|
+
changeMessage: `${r(E, "files.version.restoredFrom", "Restored from version")} ${t}`
|
|
24
|
+
}), j.success(`${r(E, "files.version.versionRestored", "Version {{number}} restored successfully", { number: t })}`);
|
|
24
25
|
} catch {
|
|
25
|
-
|
|
26
|
+
j.error(r(E, "files.version.restoreFailed", "Failed to restore version"));
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
});
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
title: `${r(
|
|
32
|
-
description: r(
|
|
33
|
-
confirmLabel: r(
|
|
30
|
+
}, B = async (e, t) => {
|
|
31
|
+
A({
|
|
32
|
+
title: `${r(E, "files.version.deleteVersion", "Delete version")} ${t}`,
|
|
33
|
+
description: r(E, "files.version.deleteDescription", "This action cannot be undone."),
|
|
34
|
+
confirmLabel: r(E, "files.version.deleteVersionBtn", "Delete version"),
|
|
34
35
|
variant: "danger",
|
|
35
36
|
onConfirm: async () => {
|
|
36
37
|
try {
|
|
37
|
-
await
|
|
38
|
+
await L.mutateAsync(e), j.success(`${r(E, "files.version.versionDeleted", "Version {{number}} deleted", { number: t })}`);
|
|
38
39
|
} catch {
|
|
39
|
-
|
|
40
|
+
j.error(r(E, "files.version.deleteFailed", "Failed to delete version"));
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
});
|
|
43
|
-
},
|
|
44
|
-
if (!
|
|
45
|
-
let e =
|
|
46
|
-
|
|
47
|
-
...
|
|
44
|
+
}, V = async () => {
|
|
45
|
+
if (!k || k.loading) return;
|
|
46
|
+
let e = k.onConfirm;
|
|
47
|
+
A({
|
|
48
|
+
...k,
|
|
48
49
|
loading: !0
|
|
49
|
-
}), await e(),
|
|
50
|
-
},
|
|
50
|
+
}), await e(), A(null);
|
|
51
|
+
}, H = async (e) => {
|
|
51
52
|
try {
|
|
52
|
-
let t = await
|
|
53
|
-
if (!t) throw Error(r(
|
|
53
|
+
let t = await R.getDownloadUrl(e);
|
|
54
|
+
if (!t) throw Error(r(E, "files.version.downloadUnavailable", "Version download URL is unavailable"));
|
|
54
55
|
window.open(t, "_blank", "noopener,noreferrer");
|
|
55
56
|
} catch (e) {
|
|
56
|
-
|
|
57
|
+
j.error(e instanceof Error ? e.message : r(E, "files.version.downloadFailed", "Failed to download version"));
|
|
57
58
|
}
|
|
58
59
|
};
|
|
59
60
|
return /* @__PURE__ */ x("div", {
|
|
60
61
|
className: "fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200",
|
|
61
62
|
role: "dialog",
|
|
62
63
|
"aria-modal": "true",
|
|
63
|
-
"aria-label": r(
|
|
64
|
+
"aria-label": r(E, "files.version.historyFor", "Version history for {{name}}", { name: w.name }),
|
|
64
65
|
children: [/* @__PURE__ */ x("div", {
|
|
65
66
|
className: "bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200",
|
|
66
67
|
children: [
|
|
@@ -70,51 +71,46 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
70
71
|
className: "flex items-center gap-3",
|
|
71
72
|
children: [/* @__PURE__ */ b(f, { className: "size-6 text-text-link" }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
|
|
72
73
|
className: "text-lg font-semibold text-text-primary",
|
|
73
|
-
children: r(
|
|
74
|
+
children: r(E, "files.version.versionHistory", "Version History")
|
|
74
75
|
}), /* @__PURE__ */ b("p", {
|
|
75
76
|
className: "text-sm text-text-secondary",
|
|
76
|
-
children:
|
|
77
|
+
children: w.name
|
|
77
78
|
})] })]
|
|
78
79
|
}), /* @__PURE__ */ b("button", {
|
|
79
80
|
type: "button",
|
|
80
|
-
onClick:
|
|
81
|
+
onClick: T,
|
|
81
82
|
className: "p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
|
|
82
|
-
"aria-label": r(
|
|
83
|
+
"aria-label": r(E, "files.version.closeHistory", "Close version history"),
|
|
83
84
|
children: /* @__PURE__ */ b(v, { className: "size-5" })
|
|
84
85
|
})]
|
|
85
86
|
}),
|
|
86
87
|
/* @__PURE__ */ x("div", {
|
|
87
88
|
className: "flex-1 overflow-y-auto p-6",
|
|
88
89
|
children: [
|
|
89
|
-
|
|
90
|
+
N && /* @__PURE__ */ b("div", {
|
|
90
91
|
className: "flex items-center justify-center py-12",
|
|
91
92
|
children: /* @__PURE__ */ b(m, { className: "size-8 text-text-link animate-spin" })
|
|
92
93
|
}),
|
|
93
|
-
|
|
94
|
+
P && /* @__PURE__ */ b("div", {
|
|
94
95
|
className: "flex items-center justify-center py-12",
|
|
95
96
|
children: /* @__PURE__ */ x("div", {
|
|
96
97
|
className: "text-center",
|
|
97
98
|
children: [/* @__PURE__ */ b(u, { className: "size-12 text-status-error-text mx-auto mb-4" }), /* @__PURE__ */ b("p", {
|
|
98
99
|
className: "text-sm text-text-secondary",
|
|
99
|
-
children: r(
|
|
100
|
+
children: r(E, "files.version.loadFailed", "Failed to load version history")
|
|
100
101
|
})]
|
|
101
102
|
})
|
|
102
103
|
}),
|
|
103
|
-
!
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
children: [/* @__PURE__ */ b(f, { className: "size-12 text-text-tertiary mx-auto mb-4" }), /* @__PURE__ */ b("p", {
|
|
108
|
-
className: "text-sm text-text-secondary",
|
|
109
|
-
children: r(T, "files.version.noHistory", "No version history available")
|
|
110
|
-
})]
|
|
111
|
-
})
|
|
104
|
+
!N && !P && F.length === 0 && /* @__PURE__ */ b(C, {
|
|
105
|
+
illustration: "empty-data",
|
|
106
|
+
title: r(E, "files.version.noHistory", "No version history available"),
|
|
107
|
+
description: r(E, "files.version.noHistoryDescription", "New versions appear here each time this file is re-uploaded or edited, so you can review and restore earlier copies.")
|
|
112
108
|
}),
|
|
113
|
-
!
|
|
109
|
+
!N && !P && F.length > 0 && /* @__PURE__ */ b("div", {
|
|
114
110
|
className: "space-y-3",
|
|
115
|
-
children:
|
|
116
|
-
className: `p-4 border rounded-lg transition-all duration-200 ${e.isCurrentVersion ? "border-action-primary-border bg-bg-accent" :
|
|
117
|
-
onClick: () =>
|
|
111
|
+
children: F.map((e) => /* @__PURE__ */ b("div", {
|
|
112
|
+
className: `p-4 border rounded-lg transition-all duration-200 ${e.isCurrentVersion ? "border-action-primary-border bg-bg-accent" : D === e.id ? "border-border-seam bg-bg-sunken" : "border-border-seam hover:border-border-strong hover:bg-action-ghost-bg-hover"}`,
|
|
113
|
+
onClick: () => O(e.id),
|
|
118
114
|
children: /* @__PURE__ */ x("div", {
|
|
119
115
|
className: "flex items-start justify-between gap-4",
|
|
120
116
|
children: [/* @__PURE__ */ x("div", {
|
|
@@ -126,18 +122,18 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
126
122
|
/* @__PURE__ */ x("span", {
|
|
127
123
|
className: "text-sm font-semibold text-text-primary",
|
|
128
124
|
children: [
|
|
129
|
-
r(
|
|
125
|
+
r(E, "files.version.version", "Version"),
|
|
130
126
|
" ",
|
|
131
127
|
e.versionNumber
|
|
132
128
|
]
|
|
133
129
|
}),
|
|
134
130
|
e.isCurrentVersion && /* @__PURE__ */ x("span", {
|
|
135
131
|
className: "inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded",
|
|
136
|
-
children: [/* @__PURE__ */ b(d, { className: "size-3" }), r(
|
|
132
|
+
children: [/* @__PURE__ */ b(d, { className: "size-3" }), r(E, "files.version.current", "Current")]
|
|
137
133
|
}),
|
|
138
134
|
e.isMajorVersion && /* @__PURE__ */ x("span", {
|
|
139
135
|
className: "inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded",
|
|
140
|
-
children: [/* @__PURE__ */ b(g, { className: "size-3" }), r(
|
|
136
|
+
children: [/* @__PURE__ */ b(g, { className: "size-3" }), r(E, "files.version.major", "Major")]
|
|
141
137
|
}),
|
|
142
138
|
e.versionLabel && /* @__PURE__ */ b("span", {
|
|
143
139
|
className: "inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded",
|
|
@@ -166,29 +162,29 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
166
162
|
children: [/* @__PURE__ */ b("button", {
|
|
167
163
|
type: "button",
|
|
168
164
|
onClick: (t) => {
|
|
169
|
-
t.stopPropagation(),
|
|
165
|
+
t.stopPropagation(), H(e.id);
|
|
170
166
|
},
|
|
171
167
|
className: "p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200",
|
|
172
|
-
title: r(
|
|
168
|
+
title: r(E, "files.version.downloadThisVersion", "Download this version"),
|
|
173
169
|
children: /* @__PURE__ */ b(p, { className: "size-4" })
|
|
174
170
|
}), !e.isCurrentVersion && /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b("button", {
|
|
175
171
|
type: "button",
|
|
176
172
|
onClick: (t) => {
|
|
177
|
-
t.stopPropagation(),
|
|
173
|
+
t.stopPropagation(), z(e.id, e.versionNumber);
|
|
178
174
|
},
|
|
179
|
-
disabled:
|
|
175
|
+
disabled: I.isPending,
|
|
180
176
|
className: "p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
181
|
-
title: r(
|
|
182
|
-
children:
|
|
177
|
+
title: r(E, "files.version.restoreThisVersion", "Restore this version"),
|
|
178
|
+
children: I.isPending ? /* @__PURE__ */ b(m, { className: "size-4 animate-spin" }) : /* @__PURE__ */ b(h, { className: "size-4" })
|
|
183
179
|
}), /* @__PURE__ */ b("button", {
|
|
184
180
|
type: "button",
|
|
185
181
|
onClick: (t) => {
|
|
186
|
-
t.stopPropagation(),
|
|
182
|
+
t.stopPropagation(), B(e.id, e.versionNumber);
|
|
187
183
|
},
|
|
188
|
-
disabled:
|
|
184
|
+
disabled: L.isPending,
|
|
189
185
|
className: "p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
190
|
-
title: r(
|
|
191
|
-
children:
|
|
186
|
+
title: r(E, "files.version.deleteThisVersion", "Delete this version"),
|
|
187
|
+
children: L.isPending ? /* @__PURE__ */ b(m, { className: "size-4 animate-spin" }) : /* @__PURE__ */ b(_, { className: "size-4" })
|
|
192
188
|
})] })]
|
|
193
189
|
})]
|
|
194
190
|
})
|
|
@@ -201,35 +197,35 @@ var C = ({ file: C, onClose: w }) => {
|
|
|
201
197
|
children: /* @__PURE__ */ x("div", {
|
|
202
198
|
className: "flex items-center justify-between text-sm text-text-secondary",
|
|
203
199
|
children: [/* @__PURE__ */ x("span", { children: [
|
|
204
|
-
|
|
200
|
+
M?.total || 0,
|
|
205
201
|
" ",
|
|
206
|
-
|
|
202
|
+
M?.total === 1 ? r(E, "files.version.versionSingular", "version") : r(E, "files.version.versions", "versions"),
|
|
207
203
|
" ",
|
|
208
|
-
r(
|
|
204
|
+
r(E, "files.version.total", "total")
|
|
209
205
|
] }), /* @__PURE__ */ b("button", {
|
|
210
206
|
type: "button",
|
|
211
|
-
onClick:
|
|
207
|
+
onClick: T,
|
|
212
208
|
className: "px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200",
|
|
213
|
-
children: r(
|
|
209
|
+
children: r(E, "files.common.actions.close", "Close")
|
|
214
210
|
})]
|
|
215
211
|
})
|
|
216
212
|
})
|
|
217
213
|
]
|
|
218
|
-
}),
|
|
219
|
-
title:
|
|
220
|
-
description:
|
|
221
|
-
confirmLabel:
|
|
222
|
-
cancelLabel: r(
|
|
223
|
-
variant:
|
|
224
|
-
loading:
|
|
225
|
-
onConfirm:
|
|
214
|
+
}), k && /* @__PURE__ */ b(i, {
|
|
215
|
+
title: k.title,
|
|
216
|
+
description: k.description,
|
|
217
|
+
confirmLabel: k.confirmLabel,
|
|
218
|
+
cancelLabel: r(E, "files.common.actions.cancel", "Cancel"),
|
|
219
|
+
variant: k.variant,
|
|
220
|
+
loading: k.loading,
|
|
221
|
+
onConfirm: V,
|
|
226
222
|
onCancel: () => {
|
|
227
|
-
|
|
223
|
+
k.loading || A(null);
|
|
228
224
|
}
|
|
229
225
|
})]
|
|
230
226
|
});
|
|
231
227
|
};
|
|
232
228
|
//#endregion
|
|
233
|
-
export {
|
|
229
|
+
export { w as FileVersionPanel };
|
|
234
230
|
|
|
235
231
|
//# sourceMappingURL=FileVersionPanel.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <Clock className=\"size-12 text-text-tertiary mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-seam bg-bg-sunken\"\n : \"border-border-seam hover:border-border-strong hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">\n {formatFileSize(version.size)}\n </span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-seam bg-bg-sunken\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"FileVersionPanel.js","names":[],"sources":["../../../src/files/components/FileVersionPanel.tsx"],"sourcesContent":["import { type FC, useState } from \"react\";\nimport {\n Clock,\n Download,\n RotateCcw,\n Trash2,\n X,\n CheckCircle2,\n AlertCircle,\n Loader2,\n Tag,\n} from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { IllustratedEmptyState } from \"@burdenoff/fe-libs/ui\";\nimport type { FileItem } from \"../types\";\nimport { formatFileSize, formatFileDate } from \"../utils\";\nimport { tWithFallback } from \"../utils/i18n\";\nimport {\n useFileVersions,\n useRestoreFileVersion,\n useDeleteFileVersion,\n useVersionDownloadUrl,\n} from \"../hooks/useFileVersions\";\nimport { useToast } from \"../hooks/useToast\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\ninterface FileVersionPanelProps {\n file: FileItem;\n onClose: () => void;\n}\n\nexport const FileVersionPanel: FC<FileVersionPanelProps> = ({\n file,\n onClose,\n}) => {\n const { t } = useI18n();\n const [selectedVersionId, setSelectedVersionId] = useState<string | null>(\n null,\n );\n const [confirmAction, setConfirmAction] = useState<{\n title: string;\n description: string;\n confirmLabel: string;\n variant?: \"danger\" | \"default\";\n onConfirm: () => Promise<void>;\n loading?: boolean;\n } | null>(null);\n const toast = useToast();\n\n // Fetch versions\n const { data: versionsData, isLoading, error } = useFileVersions(file.id);\n const versions = versionsData?.versions || [];\n\n // Mutations\n const restoreMutation = useRestoreFileVersion();\n const deleteMutation = useDeleteFileVersion();\n const versionDownload = useVersionDownloadUrl();\n\n const handleRestore = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.restoreVersion\", \"Restore version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.restoreDescription\",\n \"This will create a new version with the restored content.\",\n ),\n confirmLabel: tWithFallback(t, \"files.version.restore\", \"Restore\"),\n onConfirm: async () => {\n try {\n await restoreMutation.mutateAsync({\n versionId,\n createNewVersion: true,\n changeMessage: `${tWithFallback(t, \"files.version.restoredFrom\", \"Restored from version\")} ${versionNumber}`,\n });\n toast.success(\n `${tWithFallback(t, \"files.version.versionRestored\", \"Version {{number}} restored successfully\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.restoreFailed\",\n \"Failed to restore version\",\n ),\n );\n }\n },\n });\n };\n\n const handleDelete = async (versionId: string, versionNumber: number) => {\n setConfirmAction({\n title: `${tWithFallback(t, \"files.version.deleteVersion\", \"Delete version\")} ${versionNumber}`,\n description: tWithFallback(\n t,\n \"files.version.deleteDescription\",\n \"This action cannot be undone.\",\n ),\n confirmLabel: tWithFallback(\n t,\n \"files.version.deleteVersionBtn\",\n \"Delete version\",\n ),\n variant: \"danger\",\n onConfirm: async () => {\n try {\n await deleteMutation.mutateAsync(versionId);\n toast.success(\n `${tWithFallback(t, \"files.version.versionDeleted\", \"Version {{number}} deleted\", { number: versionNumber })}`,\n );\n } catch {\n toast.error(\n tWithFallback(\n t,\n \"files.version.deleteFailed\",\n \"Failed to delete version\",\n ),\n );\n }\n },\n });\n };\n\n const runConfirmAction = async () => {\n if (!confirmAction || confirmAction.loading) return;\n\n const action = confirmAction.onConfirm;\n setConfirmAction({ ...confirmAction, loading: true });\n await action();\n setConfirmAction(null);\n };\n\n const handleDownload = async (versionId: string) => {\n try {\n const downloadUrl = await versionDownload.getDownloadUrl(versionId);\n\n if (!downloadUrl) {\n throw new Error(\n tWithFallback(\n t,\n \"files.version.downloadUnavailable\",\n \"Version download URL is unavailable\",\n ),\n );\n }\n\n window.open(downloadUrl, \"_blank\", \"noopener,noreferrer\");\n } catch (error) {\n toast.error(\n error instanceof Error\n ? error.message\n : tWithFallback(\n t,\n \"files.version.downloadFailed\",\n \"Failed to download version\",\n ),\n );\n }\n };\n\n return (\n <div\n className=\"fixed inset-0 z-[110] bg-bg-overlay flex items-center justify-center p-4 animate-in fade-in duration-200\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label={tWithFallback(\n t,\n \"files.version.historyFor\",\n \"Version history for {{name}}\",\n { name: file.name },\n )}\n >\n <div className=\"bg-bg-surface rounded-lg shadow-xl max-w-3xl w-full max-h-[90vh] overflow-hidden flex flex-col animate-in zoom-in-95 duration-200\">\n {/* Header */}\n <div className=\"flex items-center justify-between p-6 border-b border-border-seam\">\n <div className=\"flex items-center gap-3\">\n <Clock className=\"size-6 text-text-link\" />\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tWithFallback(\n t,\n \"files.version.versionHistory\",\n \"Version History\",\n )}\n </h2>\n <p className=\"text-sm text-text-secondary\">{file.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-2 text-text-tertiary hover:text-text-secondary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n aria-label={tWithFallback(\n t,\n \"files.version.closeHistory\",\n \"Close version history\",\n )}\n >\n <X className=\"size-5\" />\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {isLoading && (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"size-8 text-text-link animate-spin\" />\n </div>\n )}\n\n {error && (\n <div className=\"flex items-center justify-center py-12\">\n <div className=\"text-center\">\n <AlertCircle className=\"size-12 text-status-error-text mx-auto mb-4\" />\n <p className=\"text-sm text-text-secondary\">\n {tWithFallback(\n t,\n \"files.version.loadFailed\",\n \"Failed to load version history\",\n )}\n </p>\n </div>\n </div>\n )}\n\n {!isLoading && !error && versions.length === 0 && (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={tWithFallback(\n t,\n \"files.version.noHistory\",\n \"No version history available\",\n )}\n description={tWithFallback(\n t,\n \"files.version.noHistoryDescription\",\n \"New versions appear here each time this file is re-uploaded or edited, so you can review and restore earlier copies.\",\n )}\n />\n )}\n\n {!isLoading && !error && versions.length > 0 && (\n <div className=\"space-y-3\">\n {versions.map((version: (typeof versions)[number]) => (\n <div\n key={version.id}\n className={`p-4 border rounded-lg transition-all duration-200 ${\n version.isCurrentVersion\n ? \"border-action-primary-border bg-bg-accent\"\n : selectedVersionId === version.id\n ? \"border-border-seam bg-bg-sunken\"\n : \"border-border-seam hover:border-border-strong hover:bg-action-ghost-bg-hover\"\n }`}\n onClick={() => setSelectedVersionId(version.id)}\n >\n <div className=\"flex items-start justify-between gap-4\">\n {/* Version info */}\n <div className=\"flex-1 min-w-0\">\n <div className=\"flex items-center gap-2 mb-2\">\n <span className=\"text-sm font-semibold text-text-primary\">\n {tWithFallback(t, \"files.version.version\", \"Version\")}{\" \"}\n {version.versionNumber}\n </span>\n\n {version.isCurrentVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-action-primary-bg text-text-inverse text-xs font-medium rounded\">\n <CheckCircle2 className=\"size-3\" />\n {tWithFallback(\n t,\n \"files.version.current\",\n \"Current\",\n )}\n </span>\n )}\n\n {version.isMajorVersion && (\n <span className=\"inline-flex items-center gap-1 px-2 py-0.5 bg-status-info-bg-subtle text-status-info-text text-xs font-medium rounded\">\n <Tag className=\"size-3\" />\n {tWithFallback(t, \"files.version.major\", \"Major\")}\n </span>\n )}\n\n {version.versionLabel && (\n <span className=\"inline-flex items-center px-2 py-0.5 bg-status-success-bg-subtle text-status-success-text text-xs font-medium rounded\">\n {version.versionLabel}\n </span>\n )}\n </div>\n\n {version.changeMessage && (\n <p className=\"text-sm text-text-primary mb-2\">\n {version.changeMessage}\n </p>\n )}\n\n <div className=\"flex items-center gap-4 text-xs text-text-secondary\">\n <span className=\"tabular-nums\">\n {formatFileSize(version.size)}\n </span>\n <span>•</span>\n <span>{formatFileDate(version.createdAt)}</span>\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDownload(version.id);\n }}\n className=\"p-2 text-text-secondary hover:text-text-link hover:bg-bg-accent rounded-lg transition-colors duration-200\"\n title={tWithFallback(\n t,\n \"files.version.downloadThisVersion\",\n \"Download this version\",\n )}\n >\n <Download className=\"size-4\" />\n </button>\n\n {!version.isCurrentVersion && (\n <>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleRestore(version.id, version.versionNumber);\n }}\n disabled={restoreMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-success-text hover:bg-status-success-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.restoreThisVersion\",\n \"Restore this version\",\n )}\n >\n {restoreMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <RotateCcw className=\"size-4\" />\n )}\n </button>\n\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(version.id, version.versionNumber);\n }}\n disabled={deleteMutation.isPending}\n className=\"p-2 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg-subtle rounded-lg transition-colors duration-200 disabled:opacity-50 disabled:cursor-not-allowed\"\n title={tWithFallback(\n t,\n \"files.version.deleteThisVersion\",\n \"Delete this version\",\n )}\n >\n {deleteMutation.isPending ? (\n <Loader2 className=\"size-4 animate-spin\" />\n ) : (\n <Trash2 className=\"size-4\" />\n )}\n </button>\n </>\n )}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"p-6 border-t border-border-seam bg-bg-sunken\">\n <div className=\"flex items-center justify-between text-sm text-text-secondary\">\n <span>\n {versionsData?.total || 0}{\" \"}\n {versionsData?.total !== 1\n ? tWithFallback(t, \"files.version.versions\", \"versions\")\n : tWithFallback(\n t,\n \"files.version.versionSingular\",\n \"version\",\n )}{\" \"}\n {tWithFallback(t, \"files.version.total\", \"total\")}\n </span>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"px-4 py-2 text-sm font-medium text-text-primary hover:bg-action-ghost-bg-hover rounded-lg transition-colors duration-200\"\n >\n {tWithFallback(t, \"files.common.actions.close\", \"Close\")}\n </button>\n </div>\n </div>\n </div>\n\n {confirmAction && (\n <ConfirmDialog\n title={confirmAction.title}\n description={confirmAction.description}\n confirmLabel={confirmAction.confirmLabel}\n cancelLabel={tWithFallback(\n t,\n \"files.common.actions.cancel\",\n \"Cancel\",\n )}\n variant={confirmAction.variant}\n loading={confirmAction.loading}\n onConfirm={runConfirmAction}\n onCancel={() => {\n if (!confirmAction.loading) {\n setConfirmAction(null);\n }\n }}\n />\n )}\n </div>\n );\n};\n"],"mappings":"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"}
|