strapi-plugin-timeline 0.0.0

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.
@@ -0,0 +1,1005 @@
1
+ import React, { useRef, useEffect, useState, useMemo } from "react";
2
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
3
+ import { Modal, Flex, Loader, Box, RawTable, RawThead, RawTr, RawTh, Badge, Typography, RawTbody, RawTd, JSONInput, Button, Card, CardBody, Link, Avatar, CardContent, CardTitle, CardSubtitle, Tag, IconButton, DatePicker, Dialog } from "@strapi/design-system";
4
+ import { Mail, ArrowsCounterClockwise, Eye, ArrowClockwise } from "@strapi/icons";
5
+ import { useFetchClient, useAuth, useNotification } from "@strapi/strapi/admin";
6
+ import { NavLink } from "react-router-dom";
7
+ import { BlocksRenderer } from "@strapi/blocks-react-renderer";
8
+ import ReactMarkdown from "react-markdown";
9
+ import remarkGfm from "remark-gfm";
10
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
11
+ const v = glob[path];
12
+ if (v) {
13
+ return typeof v === "function" ? v() : Promise.resolve(v);
14
+ }
15
+ return new Promise((_, reject) => {
16
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
17
+ reject.bind(
18
+ null,
19
+ new Error(
20
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
21
+ )
22
+ )
23
+ );
24
+ });
25
+ };
26
+ const PLUGIN_ID = "timeline";
27
+ const Initializer = ({ setPlugin }) => {
28
+ const ref = useRef(setPlugin);
29
+ useEffect(() => {
30
+ ref.current(PLUGIN_ID);
31
+ }, []);
32
+ return null;
33
+ };
34
+ const PluginIcon = ({ width = 20, height = 20 }) => /* @__PURE__ */ jsx(
35
+ "svg",
36
+ {
37
+ xmlns: "http://www.w3.org/2000/svg",
38
+ viewBox: "0 0 519 519",
39
+ width,
40
+ height,
41
+ fill: "currentColor",
42
+ "aria-hidden": "true",
43
+ focusable: "false",
44
+ children: /* @__PURE__ */ jsxs("g", { transform: "translate(0,519) scale(0.1,-0.1)", stroke: "none", children: [
45
+ /* @__PURE__ */ jsx("path", { d: "M1130 4724 c-45 -20 -96 -69 -123 -119 -18 -34 -22 -61 -25 -177 l-4 -137 1649 -3 1648 -3 56 -26 c72 -34 134 -96 168 -168 l26 -56 3 -330 3 -330 37 -25 c67 -45 246 -234 305 -319 l57 -85 0 805 0 804 -24 51 c-27 57 -84 110 -136 124 -22 6 -691 10 -1820 10 -1571 -1 -1789 -3 -1820 -16z" }),
46
+ /* @__PURE__ */ jsx("path", { d: "M427 4063 c-52 -15 -122 -84 -144 -143 -17 -42 -18 -127 -18 -1285 0 -1132 1 -1243 17 -1276 25 -56 88 -120 143 -145 l50 -24 512 0 512 0 -30 67 c-39 88 -82 234 -105 358 -26 138 -27 499 -2 630 128 679 617 1232 1283 1453 413 138 844 145 1229 21 154 -49 215 -75 317 -135 42 -24 78 -44 80 -44 9 0 -8 387 -18 411 -20 45 -63 87 -106 103 -37 14 -244 16 -1861 18 -1382 2 -1828 0 -1859 -9z" }),
47
+ /* @__PURE__ */ jsx("path", { d: "M3165 3559 c-458 -44 -887 -254 -1163 -571 -379 -434 -506 -1031 -337 -1584 205 -671 818 -1123 1575 -1160 382 -19 802 103 1109 323 326 233 561 594 640 984 77 374 32 749 -129 1070 -209 419 -578 728 -1038 868 -117 36 -229 57 -377 71 -123 11 -150 11 -280 -1z m409 -365 c382 -73 705 -308 904 -659 166 -293 203 -683 96 -1010 -108 -331 -330 -583 -659 -748 -209 -105 -462 -153 -709 -136 -245 18 -465 92 -670 227 -90 59 -240 204 -312 299 -234 310 -307 755 -188 1135 126 400 461 734 856 852 219 65 469 80 682 40z" }),
48
+ /* @__PURE__ */ jsx("path", { d: "M3285 3013 c-11 -3 -35 -16 -53 -30 -64 -48 -62 -27 -62 -631 l0 -549 23 -34 c12 -19 42 -44 67 -57 44 -22 46 -22 525 -22 479 0 481 0 525 23 113 57 122 213 17 291 -28 20 -40 21 -432 24 l-404 3 -3 442 -3 442 -24 34 c-38 53 -111 80 -176 64z" }),
49
+ /* @__PURE__ */ jsx("path", { d: "M940 817 c0 -174 13 -220 83 -292 37 -37 69 -59 110 -74 56 -20 73 -21 530 -21 422 1 470 2 452 16 -11 8 -51 38 -90 66 -96 70 -268 247 -340 351 l-60 86 -342 0 -343 1 0 -133z" })
50
+ ] })
51
+ }
52
+ );
53
+ const ACTION_COLORS$1 = {
54
+ create: { bg: "success100", text: "success700" },
55
+ update: { bg: "warning100", text: "warning700" },
56
+ publish: { bg: "primary100", text: "primary700" },
57
+ delete: { bg: "danger100", text: "danger700" },
58
+ clean: { bg: "danger100", text: "danger700" },
59
+ restore: { bg: "primary100", text: "primary700" }
60
+ };
61
+ const DATE_KEYS = /* @__PURE__ */ new Set(["createdAt", "updatedAt", "publishedAt"]);
62
+ const USER_KEYS = /* @__PURE__ */ new Set(["createdBy", "updatedBy"]);
63
+ const looksLikeHtml = (str) => /<[a-z][\s\S]*?>/i.test(str);
64
+ const looksLikeBlocks = (value) => Array.isArray(value) && value.length > 0 && typeof value[0] === "object" && value[0] !== null && "type" in value[0] && "children" in value[0];
65
+ const formatDate = (dateStr) => new Date(dateStr).toLocaleString();
66
+ const prettifyKey$1 = (key) => key.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
67
+ const getInitials = (firstname, lastname, fallback) => {
68
+ if (firstname || lastname) {
69
+ return `${(firstname || "")[0] || ""}${(lastname || "")[0] || ""}`.toUpperCase();
70
+ }
71
+ return fallback[0].toUpperCase();
72
+ };
73
+ const getUserLink = (userId, currentUserId) => {
74
+ if (currentUserId && String(userId) === String(currentUserId)) return "/me";
75
+ return `/settings/users/${userId}`;
76
+ };
77
+ const TimelineDetailModal = ({
78
+ entry,
79
+ open,
80
+ onClose,
81
+ contentTypes
82
+ }) => {
83
+ const { get } = useFetchClient();
84
+ const { user: currentUser } = useAuth("TimelineDetailModal", (auth) => ({ user: auth.user }));
85
+ const [config, setConfig] = useState(null);
86
+ const [configLoading, setConfigLoading] = useState(false);
87
+ const [resolvedRelations, setResolvedRelations] = useState({});
88
+ useEffect(() => {
89
+ if (!entry || !open) {
90
+ setConfig(null);
91
+ setResolvedRelations({});
92
+ return;
93
+ }
94
+ let cancelled = false;
95
+ const fetchConfigAndRelations = async () => {
96
+ setConfigLoading(true);
97
+ setResolvedRelations({});
98
+ let configData = null;
99
+ try {
100
+ const response = await get(
101
+ `/content-manager/content-types/${entry.contentType}/configuration`
102
+ );
103
+ configData = response.data?.data?.contentType || null;
104
+ } catch {
105
+ configData = null;
106
+ }
107
+ if (cancelled) return;
108
+ setConfig(configData);
109
+ const contentObj2 = entry.content && typeof entry.content === "object" && !Array.isArray(entry.content) ? entry.content : null;
110
+ const schema2 = entry.contentTypeSchema || {};
111
+ if (configData && contentObj2) {
112
+ const resolved = {};
113
+ for (const key of Object.keys(contentObj2)) {
114
+ const fieldSchema = schema2[key];
115
+ if (fieldSchema?.type !== "relation") continue;
116
+ const mainField = configData.metadatas?.[key]?.edit?.mainField;
117
+ if (!mainField || mainField === "id" || mainField === "documentId") continue;
118
+ const target = fieldSchema.target;
119
+ if (!target || target.startsWith("plugin::users-permissions.")) continue;
120
+ const targetCt = contentTypes.find((c) => c.uid === target);
121
+ if (!targetCt) continue;
122
+ const value = contentObj2[key];
123
+ const ids = Array.isArray(value) ? value.map(String) : value ? [String(value)] : [];
124
+ resolved[key] = {};
125
+ for (const id of ids.slice(0, 20)) {
126
+ if (cancelled) return;
127
+ try {
128
+ const path = targetCt.kind === "singleType" ? `/content-manager/single-types/${target}` : `/content-manager/collection-types/${target}/${id}`;
129
+ const resp = await get(path);
130
+ const data = resp.data?.data || resp.data;
131
+ resolved[key][id] = data?.[mainField] ? String(data[mainField]) : id;
132
+ } catch {
133
+ resolved[key][id] = id;
134
+ }
135
+ }
136
+ }
137
+ if (!cancelled) setResolvedRelations(resolved);
138
+ }
139
+ if (!cancelled) setConfigLoading(false);
140
+ };
141
+ fetchConfigAndRelations();
142
+ return () => {
143
+ cancelled = true;
144
+ };
145
+ }, [entry?.contentType, entry?.id, open]);
146
+ if (!entry) return null;
147
+ const getDisplayName = (uid) => {
148
+ const ct = contentTypes.find((c) => c.uid === uid);
149
+ return ct?.displayName || uid.split(".").pop() || uid;
150
+ };
151
+ const getContentTypeKind = (uid) => {
152
+ const ct = contentTypes.find((c) => c.uid === uid);
153
+ return ct?.kind || "collectionType";
154
+ };
155
+ const getContentManagerLink = (uid, docId) => {
156
+ const kind = getContentTypeKind(uid);
157
+ if (kind === "singleType") {
158
+ return `/content-manager/single-types/${uid}`;
159
+ }
160
+ return `/content-manager/collection-types/${uid}/${docId}`;
161
+ };
162
+ const getFieldLabel = (key) => {
163
+ if (key === "documentId") {
164
+ return "Content";
165
+ }
166
+ if (config?.metadatas?.[key]?.edit?.label) {
167
+ return config.metadatas[key].edit.label;
168
+ }
169
+ return prettifyKey$1(key);
170
+ };
171
+ const colors = ACTION_COLORS$1[entry.action] || ACTION_COLORS$1.update;
172
+ const contentObj = entry.content && typeof entry.content === "object" && !Array.isArray(entry.content) ? entry.content : null;
173
+ const contentKeys = contentObj ? Object.keys(contentObj) : [];
174
+ const schema = entry.contentTypeSchema || {};
175
+ const getRelationLink = (target, docId) => {
176
+ const ct = contentTypes.find((c) => c.uid === target);
177
+ if (!ct) return null;
178
+ if (ct.kind === "singleType") {
179
+ return `/content-manager/single-types/${target}`;
180
+ }
181
+ return `/content-manager/collection-types/${target}/${docId}`;
182
+ };
183
+ const getEffectiveType = (fieldSchema) => {
184
+ if (!fieldSchema) return "unknown";
185
+ if (fieldSchema.type === "customField" && fieldSchema.customFieldType) {
186
+ return fieldSchema.customFieldType;
187
+ }
188
+ return fieldSchema.type;
189
+ };
190
+ const renderRichContent = (value) => {
191
+ if (looksLikeHtml(value)) {
192
+ return /* @__PURE__ */ jsx(
193
+ Box,
194
+ {
195
+ padding: 3,
196
+ background: "neutral100",
197
+ hasRadius: true,
198
+ style: { maxHeight: "300px", overflow: "auto" },
199
+ children: /* @__PURE__ */ jsx(
200
+ "div",
201
+ {
202
+ style: { fontSize: "14px", lineHeight: "1.6" },
203
+ dangerouslySetInnerHTML: { __html: value }
204
+ }
205
+ )
206
+ }
207
+ );
208
+ }
209
+ return /* @__PURE__ */ jsx(
210
+ Box,
211
+ {
212
+ padding: 3,
213
+ background: "neutral100",
214
+ hasRadius: true,
215
+ style: { maxHeight: "300px", overflow: "auto", fontSize: "14px", lineHeight: "1.6" },
216
+ children: /* @__PURE__ */ jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], children: value })
217
+ }
218
+ );
219
+ };
220
+ const getResolvedLabel = (key, id) => resolvedRelations[key]?.[id] || id;
221
+ const renderAdminUser = (value) => {
222
+ if (!value || typeof value !== "object") return null;
223
+ const name = [value.firstname, value.lastname].filter(Boolean).join(" ") || "Unknown";
224
+ const initials = getInitials(value.firstname, value.lastname, name);
225
+ const link = value.id ? getUserLink(value.id, currentUser?.id) : null;
226
+ return /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsx(Card, { style: { width: "100%" }, children: /* @__PURE__ */ jsxs(CardBody, { style: { alignItems: "center" }, children: [
227
+ link ? /* @__PURE__ */ jsx(Link, { tag: NavLink, to: link, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx(
228
+ Avatar.Item,
229
+ {
230
+ src: void 0,
231
+ alt: value.id ?? name,
232
+ fallback: initials
233
+ }
234
+ ) }) : /* @__PURE__ */ jsx(
235
+ Avatar.Item,
236
+ {
237
+ src: void 0,
238
+ alt: value.id ?? name,
239
+ fallback: initials
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsxs(CardContent, { paddingLeft: 2, children: [
243
+ /* @__PURE__ */ jsx(CardTitle, { children: link ? /* @__PURE__ */ jsx(Link, { tag: NavLink, to: link, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "primary600", children: name }) }) : /* @__PURE__ */ jsx(Typography, { variant: "omega", children: name }) }),
244
+ value.email && /* @__PURE__ */ jsx(CardSubtitle, { children: /* @__PURE__ */ jsx(Link, { href: `mailto:${value.email}`, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsxs(Flex, { alignItems: "center", gap: 2, children: [
245
+ /* @__PURE__ */ jsx(Mail, { color: "neutral600" }),
246
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral600", children: value.email })
247
+ ] }) }) })
248
+ ] })
249
+ ] }) }) });
250
+ };
251
+ const renderContentValue = (key, value) => {
252
+ if (value === null || value === void 0) return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "—" });
253
+ const fieldSchema = schema[key];
254
+ const effectiveType = getEffectiveType(fieldSchema);
255
+ if (key === "documentId" && typeof value === "string") {
256
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 1, alignItems: "flex-start", children: [
257
+ /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", variant: "omega", children: getDisplayName(entry.contentType) }),
258
+ /* @__PURE__ */ jsx(
259
+ Link,
260
+ {
261
+ tag: NavLink,
262
+ to: getContentManagerLink(entry.contentType, value),
263
+ style: { textDecoration: "none" },
264
+ children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", textColor: "primary600", children: [
265
+ value,
266
+ entry.locale && /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary500", marginLeft: 2, children: entry.locale ? entry.locale.toUpperCase() : "—" })
267
+ ] })
268
+ }
269
+ )
270
+ ] });
271
+ }
272
+ if (DATE_KEYS.has(key) && typeof value === "string") {
273
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatDate(value) });
274
+ }
275
+ if ((effectiveType === "datetime" || effectiveType === "date" || effectiveType === "time") && typeof value === "string") {
276
+ if (effectiveType === "time") {
277
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: value });
278
+ }
279
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatDate(value) });
280
+ }
281
+ if (USER_KEYS.has(key) && typeof value === "object" && value !== null) {
282
+ return renderAdminUser(value);
283
+ }
284
+ if (USER_KEYS.has(key) && (typeof value === "string" || typeof value === "number")) {
285
+ return /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
286
+ "User #",
287
+ String(value)
288
+ ] });
289
+ }
290
+ if (key === "locale" && typeof value === "string") {
291
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: value.toUpperCase() });
292
+ }
293
+ if (key === "localizations" && Array.isArray(value)) {
294
+ if (value.length === 0) return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "—" });
295
+ const kind = getContentTypeKind(entry.contentType);
296
+ const entryLocale = contentObj?.locale;
297
+ const seen = /* @__PURE__ */ new Set();
298
+ const filtered = value.filter((item) => {
299
+ const loc = typeof item === "object" ? item.locale : null;
300
+ if (loc && loc === entryLocale) return false;
301
+ const key2 = loc || (typeof item === "object" ? item.documentId : String(item));
302
+ if (seen.has(key2)) return false;
303
+ seen.add(key2);
304
+ return true;
305
+ });
306
+ if (filtered.length === 0) return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "—" });
307
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, wrap: "wrap", children: filtered.map((item, i) => {
308
+ const itemLocale = typeof item === "object" ? item.locale : null;
309
+ const docId = typeof item === "object" ? item.documentId : String(item);
310
+ const label = itemLocale ? itemLocale.toUpperCase() : docId;
311
+ let link;
312
+ if (kind === "singleType") {
313
+ link = `/content-manager/single-types/${entry.contentType}`;
314
+ } else {
315
+ link = `/content-manager/collection-types/${entry.contentType}/${docId}`;
316
+ }
317
+ if (itemLocale) link += `?plugins[i18n][locale]=${itemLocale}`;
318
+ return /* @__PURE__ */ jsx(Link, { tag: NavLink, to: link, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx(Badge, { backgroundColor: "primary100", textColor: "primary500", marginLeft: 2, children: label }) }, i);
319
+ }) });
320
+ }
321
+ if (fieldSchema?.type === "relation") {
322
+ const target = fieldSchema.target || "";
323
+ const isUsersPermissions = target.startsWith("plugin::users-permissions.");
324
+ if (Array.isArray(value)) {
325
+ if (value.length === 0) return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: "—" });
326
+ return /* @__PURE__ */ jsx(Flex, { gap: 1, wrap: "wrap", children: value.map((id, i) => {
327
+ const label = getResolvedLabel(key, String(id));
328
+ const link = !isUsersPermissions ? getRelationLink(target, String(id)) : null;
329
+ return link ? /* @__PURE__ */ jsx(Link, { tag: NavLink, to: link, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx(Tag, { icon: /* @__PURE__ */ jsx(Link, {}), children: label }) }, i) : /* @__PURE__ */ jsx(Tag, { icon: /* @__PURE__ */ jsx(Link, {}), children: label }, i);
330
+ }) });
331
+ }
332
+ if (typeof value === "string" || typeof value === "number") {
333
+ const label = getResolvedLabel(key, String(value));
334
+ const link = !isUsersPermissions ? getRelationLink(target, String(value)) : null;
335
+ return link ? /* @__PURE__ */ jsx(Link, { tag: NavLink, to: link, style: { textDecoration: "none" }, children: /* @__PURE__ */ jsx(Tag, { icon: /* @__PURE__ */ jsx(Link, {}), children: label }) }) : /* @__PURE__ */ jsx(Tag, { icon: /* @__PURE__ */ jsx(Link, {}), children: label });
336
+ }
337
+ }
338
+ if (effectiveType === "blocks" && looksLikeBlocks(value)) {
339
+ return /* @__PURE__ */ jsx(
340
+ Box,
341
+ {
342
+ padding: 3,
343
+ background: "neutral100",
344
+ hasRadius: true,
345
+ style: { maxHeight: "300px", overflow: "auto", fontSize: "14px", lineHeight: "1.6" },
346
+ children: /* @__PURE__ */ jsx(BlocksRenderer, { content: value })
347
+ }
348
+ );
349
+ }
350
+ if (effectiveType === "richtext" && typeof value === "string") {
351
+ return renderRichContent(value);
352
+ }
353
+ if ((effectiveType === "text" || effectiveType === "string") && typeof value === "string" && fieldSchema?.type === "customField" && looksLikeHtml(value)) {
354
+ return /* @__PURE__ */ jsx(
355
+ Box,
356
+ {
357
+ padding: 3,
358
+ background: "neutral100",
359
+ hasRadius: true,
360
+ style: { maxHeight: "300px", overflow: "auto" },
361
+ children: /* @__PURE__ */ jsx(
362
+ "div",
363
+ {
364
+ style: { fontSize: "14px", lineHeight: "1.6" },
365
+ dangerouslySetInnerHTML: { __html: value }
366
+ }
367
+ )
368
+ }
369
+ );
370
+ }
371
+ if (typeof value === "object") {
372
+ return /* @__PURE__ */ jsx(JSONInput, { value: JSON.stringify(value, null, 2), disabled: true });
373
+ }
374
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: String(value) });
375
+ };
376
+ const summaryUserValue = {
377
+ id: entry.userId,
378
+ firstname: entry.userName?.split(" ")[0] || null,
379
+ lastname: entry.userName?.split(" ").slice(1).join(" ") || null,
380
+ email: null
381
+ };
382
+ if (contentObj) {
383
+ for (const k of ["createdBy", "updatedBy"]) {
384
+ const u = contentObj[k];
385
+ if (u && typeof u === "object" && u.id && String(u.id) === String(entry.userId) && u.email) {
386
+ summaryUserValue.email = u.email;
387
+ if (u.firstname) summaryUserValue.firstname = u.firstname;
388
+ if (u.lastname) summaryUserValue.lastname = u.lastname;
389
+ break;
390
+ }
391
+ }
392
+ }
393
+ return /* @__PURE__ */ jsx(Modal.Root, { open, onOpenChange: (isOpen) => !isOpen && onClose(), children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
394
+ /* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Timeline Entry Detail" }) }),
395
+ /* @__PURE__ */ jsxs(Modal.Body, { children: [
396
+ /* @__PURE__ */ jsx(Flex, { justifyContent: "space-between", alignItems: "center", children: configLoading && /* @__PURE__ */ jsx(Loader, { small: true, children: "Loading field information" }) }),
397
+ /* @__PURE__ */ jsx(Box, { children: contentKeys.length > 0 ? /* @__PURE__ */ jsxs(RawTable, { colCount: 2, rowCount: contentKeys.length + 1, width: "100%", children: [
398
+ /* @__PURE__ */ jsx(RawThead, { children: /* @__PURE__ */ jsx(RawTr, { children: /* @__PURE__ */ jsx(RawTh, { colSpan: 2, children: /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 2, alignItems: "flex-start", children: [
399
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", children: [
400
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: colors.bg, textColor: colors.text, children: entry.action }),
401
+ /* @__PURE__ */ jsx(Typography, { variant: "omega", children: formatDate(entry.createdAt) })
402
+ ] }) }),
403
+ /* @__PURE__ */ jsx(Box, { marginBottom: 6, width: "100%", children: renderAdminUser(summaryUserValue) })
404
+ ] }) }) }) }) }),
405
+ /* @__PURE__ */ jsx(RawTbody, { children: contentKeys.filter((key) => key !== "id" && key !== "locale").map((key) => /* @__PURE__ */ jsxs(
406
+ RawTr,
407
+ {
408
+ style: { borderBottom: "1px solid #dcdce4", borderLeft: "none", borderRight: "none" },
409
+ children: [
410
+ /* @__PURE__ */ jsx(RawTd, { paddingTop: 4, paddingBottom: 4, style: { verticalAlign: "middle" }, children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", variant: "omega", children: getFieldLabel(key) }) }),
411
+ /* @__PURE__ */ jsx(RawTd, { padding: 4, children: renderContentValue(key, contentObj[key]) })
412
+ ]
413
+ },
414
+ key
415
+ )) })
416
+ ] }) : /* @__PURE__ */ jsx(JSONInput, { value: JSON.stringify(entry.content, null, 2), disabled: true }) })
417
+ ] }),
418
+ /* @__PURE__ */ jsx(Modal.Footer, { children: /* @__PURE__ */ jsx(Modal.Close, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: "Close" }) }) })
419
+ ] }) });
420
+ };
421
+ const SYSTEM_FIELDS = /* @__PURE__ */ new Set([
422
+ "id",
423
+ "documentId",
424
+ "createdAt",
425
+ "updatedAt",
426
+ "publishedAt",
427
+ "createdBy",
428
+ "updatedBy",
429
+ "created_by_id",
430
+ "updated_by_id",
431
+ "created_at",
432
+ "updated_at",
433
+ "published_at",
434
+ "locale",
435
+ "localizations",
436
+ "status"
437
+ ]);
438
+ const prettifyKey = (key) => key.replace(/([A-Z])/g, " $1").replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()).trim();
439
+ const normalize = (value) => {
440
+ if (value === null || value === void 0) return null;
441
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
442
+ return value;
443
+ }
444
+ if (Array.isArray(value)) {
445
+ if (value.length > 0 && typeof value[0] === "object" && value[0] !== null && ("documentId" in value[0] || "id" in value[0])) {
446
+ return value.map((item) => item.documentId ?? item.id ?? item).sort();
447
+ }
448
+ return value.map(normalize);
449
+ }
450
+ if (typeof value === "object") {
451
+ if ("documentId" in value || "id" in value && "__type" in value) {
452
+ return value.documentId ?? value.id ?? value;
453
+ }
454
+ if ("url" in value && "id" in value) {
455
+ return value.id;
456
+ }
457
+ const out = {};
458
+ for (const [k, v] of Object.entries(value)) {
459
+ if (!SYSTEM_FIELDS.has(k)) out[k] = normalize(v);
460
+ }
461
+ return out;
462
+ }
463
+ return value;
464
+ };
465
+ const renderCompactValue = (value) => {
466
+ if (value === null || value === void 0) {
467
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral400", children: "—" });
468
+ }
469
+ if (typeof value === "boolean") {
470
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: value ? "Yes" : "No" });
471
+ }
472
+ if (typeof value === "number") {
473
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: String(value) });
474
+ }
475
+ if (typeof value === "string") {
476
+ if (value === "") return /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral400", children: "(empty)" });
477
+ if (value.length > 120) {
478
+ return /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
479
+ value.slice(0, 120),
480
+ "…"
481
+ ] });
482
+ }
483
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: value });
484
+ }
485
+ if (Array.isArray(value)) {
486
+ if (value.length === 0) return /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral400", children: "(empty)" });
487
+ const items = value.slice(0, 5).map((item, i) => {
488
+ if (typeof item === "object" && item !== null) {
489
+ const label = item.name || item.title || item.label || item.documentId || item.id || JSON.stringify(item);
490
+ return /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral100", textColor: "neutral700", children: String(label) }, i);
491
+ }
492
+ return /* @__PURE__ */ jsx(Badge, { backgroundColor: "neutral100", textColor: "neutral700", children: String(item) }, i);
493
+ });
494
+ return /* @__PURE__ */ jsxs(Flex, { gap: 1, wrap: "wrap", alignItems: "center", children: [
495
+ items,
496
+ value.length > 5 && /* @__PURE__ */ jsxs(Typography, { variant: "omega", textColor: "neutral500", children: [
497
+ "+",
498
+ value.length - 5,
499
+ " more"
500
+ ] })
501
+ ] });
502
+ }
503
+ if (typeof value === "object") {
504
+ if (value.name || value.title) {
505
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: String(value.name || value.title) });
506
+ }
507
+ if (value.url) return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: String(value.url) });
508
+ const str = JSON.stringify(value);
509
+ if (str.length > 120) return /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
510
+ str.slice(0, 120),
511
+ "…"
512
+ ] });
513
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: str });
514
+ }
515
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", children: String(value) });
516
+ };
517
+ const RestoreDiffTable = ({
518
+ currentData,
519
+ snapshotData,
520
+ loading
521
+ }) => {
522
+ if (loading) {
523
+ return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsx(Loader, { small: true, children: "Loading current data…" }) });
524
+ }
525
+ if (!currentData) {
526
+ return /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "neutral500", children: "Could not load current document for comparison." });
527
+ }
528
+ const allKeys = /* @__PURE__ */ new Set([
529
+ ...Object.keys(currentData),
530
+ ...Object.keys(snapshotData)
531
+ ]);
532
+ const changedFields = [];
533
+ for (const key of allKeys) {
534
+ if (SYSTEM_FIELDS.has(key)) continue;
535
+ const a = normalize(currentData[key]);
536
+ const b = normalize(snapshotData[key]);
537
+ if (JSON.stringify(a) !== JSON.stringify(b)) {
538
+ changedFields.push(key);
539
+ }
540
+ }
541
+ if (changedFields.length === 0) {
542
+ return /* @__PURE__ */ jsx(Box, { padding: 3, background: "success100", hasRadius: true, children: /* @__PURE__ */ jsx(Typography, { variant: "omega", textColor: "success700", children: "No differences — content is already up to date." }) });
543
+ }
544
+ return /* @__PURE__ */ jsxs(Box, { width: "100%", children: [
545
+ /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Typography, { variant: "omega", children: [
546
+ changedFields.length,
547
+ " field",
548
+ changedFields.length !== 1 ? "s" : "",
549
+ " will change"
550
+ ] }) }),
551
+ /* @__PURE__ */ jsx(Box, { style: { maxHeight: "360px", overflow: "auto" }, children: /* @__PURE__ */ jsx(RawTable, { colCount: 2, rowCount: changedFields.length * 2 + 1, width: "100%", style: { width: "100%" }, children: /* @__PURE__ */ jsx(RawTbody, { children: changedFields.map((key) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
552
+ /* @__PURE__ */ jsxs(RawTr, { style: { borderTop: "1px solid #dcdce4" }, children: [
553
+ /* @__PURE__ */ jsx(
554
+ RawTd,
555
+ {
556
+ rowSpan: 2,
557
+ paddingTop: 4,
558
+ paddingBottom: 4,
559
+ paddingRight: 4,
560
+ style: { verticalAlign: "middle" },
561
+ children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", variant: "omega", children: prettifyKey(key) })
562
+ }
563
+ ),
564
+ /* @__PURE__ */ jsx(RawTd, { padding: 4, style: { background: "#fcecea" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, direction: "column", alignItems: "flex-start", wrap: "wrap", children: [
565
+ /* @__PURE__ */ jsx(Typography, { textColor: "danger700", fontWeight: "bold", children: "Current" }),
566
+ renderCompactValue(currentData[key])
567
+ ] }) })
568
+ ] }),
569
+ /* @__PURE__ */ jsx(RawTr, { style: { borderBottom: "1px solid #dcdce4" }, children: /* @__PURE__ */ jsx(RawTd, { paddingTop: 4, paddingBottom: 4, paddingLeft: 4, style: { background: "#eafbe7" }, children: /* @__PURE__ */ jsxs(Flex, { gap: 2, direction: "column", alignItems: "flex-start", wrap: "wrap", children: [
570
+ /* @__PURE__ */ jsx(Typography, { textColor: "success700", fontWeight: "bold", children: "Restore to" }),
571
+ renderCompactValue(snapshotData[key])
572
+ ] }) }) })
573
+ ] }, key)) }) }) })
574
+ ] });
575
+ };
576
+ const ACTION_COLORS = {
577
+ create: { bg: "success100", text: "success700" },
578
+ update: { bg: "warning100", text: "warning700" },
579
+ publish: { bg: "primary100", text: "primary700" },
580
+ delete: { bg: "danger100", text: "danger700" },
581
+ clean: { bg: "danger100", text: "danger700" },
582
+ restore: { bg: "primary100", text: "primary700" }
583
+ };
584
+ const toDateKey = (dateStr) => {
585
+ const d = new Date(dateStr);
586
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
587
+ };
588
+ const formatTime = (dateStr) => new Date(dateStr).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
589
+ const TimelinePanel = ({
590
+ documentId,
591
+ document,
592
+ model
593
+ }) => {
594
+ const { get, post } = useFetchClient();
595
+ const { toggleNotification } = useNotification();
596
+ const { user: currentUser } = useAuth("TimelinePanel", (auth) => ({ user: auth.user }));
597
+ const [entries, setEntries] = useState([]);
598
+ const [loading, setLoading] = useState(true);
599
+ const [selectedDateKey, setSelectedDateKey] = useState(null);
600
+ const [restoreTarget, setRestoreTarget] = useState(null);
601
+ const [isRestoring, setIsRestoring] = useState(false);
602
+ const [adminUsers, setAdminUsers] = useState({});
603
+ const [canReadUsers, setCanReadUsers] = useState(false);
604
+ const [viewEntry, setViewEntry] = useState(null);
605
+ const [contentTypes, setContentTypes] = useState([]);
606
+ const effectiveDocumentId = documentId || document?.documentId;
607
+ useEffect(() => {
608
+ const checkPermissions = async () => {
609
+ try {
610
+ const res = await get("/admin/permissions/check", {
611
+ params: { permissions: [{ action: "admin::users.read" }] }
612
+ });
613
+ setCanReadUsers(res?.data?.data?.[0]?.hasPermission ?? false);
614
+ } catch {
615
+ setCanReadUsers(false);
616
+ }
617
+ };
618
+ checkPermissions();
619
+ }, [get]);
620
+ useEffect(() => {
621
+ const loadContentTypes = async () => {
622
+ try {
623
+ const res = await get(`/${PLUGIN_ID}/content-types`);
624
+ setContentTypes(res.data.data || []);
625
+ } catch {
626
+ }
627
+ };
628
+ loadContentTypes();
629
+ }, [get]);
630
+ useEffect(() => {
631
+ if (!effectiveDocumentId || !model) {
632
+ setLoading(false);
633
+ return;
634
+ }
635
+ loadHistory();
636
+ }, [effectiveDocumentId, model]);
637
+ const loadHistory = async () => {
638
+ setLoading(true);
639
+ try {
640
+ const res = await get(
641
+ `/${PLUGIN_ID}/entries/${encodeURIComponent(model)}/${encodeURIComponent(effectiveDocumentId)}`
642
+ );
643
+ const all = res.data.data || [];
644
+ setEntries(all.slice(0, 20));
645
+ if (all.length > 0 && !selectedDateKey) {
646
+ setSelectedDateKey(toDateKey(all[0].createdAt));
647
+ }
648
+ const userIds = [...new Set(all.slice(0, 20).map((e) => e.userId).filter(Boolean))];
649
+ if (userIds.length > 0) {
650
+ fetchAdminUsers(userIds);
651
+ }
652
+ } catch (err) {
653
+ console.error("[Timeline Panel] Failed to load history:", err);
654
+ } finally {
655
+ setLoading(false);
656
+ }
657
+ };
658
+ const fetchAdminUsers = async (userIds) => {
659
+ const fetched = {};
660
+ await Promise.all(
661
+ userIds.map(async (uid) => {
662
+ try {
663
+ const res = await get(`/admin/users/${uid}`);
664
+ const u = res.data?.data || res.data;
665
+ if (u && u.id) {
666
+ fetched[u.id] = u;
667
+ }
668
+ } catch {
669
+ }
670
+ })
671
+ );
672
+ setAdminUsers((prev) => ({ ...prev, ...fetched }));
673
+ };
674
+ const getInitials2 = (user) => {
675
+ const f = (user.firstname || "")[0] || "";
676
+ const l = (user.lastname || "")[0] || "";
677
+ return (f + l).toUpperCase() || "?";
678
+ };
679
+ const getUserLink2 = (userId) => {
680
+ if (currentUser && userId === currentUser.id) return "/me";
681
+ if (canReadUsers) return `/settings/users/${userId}`;
682
+ return null;
683
+ };
684
+ const selectedDateValue = useMemo(() => {
685
+ if (!selectedDateKey) return void 0;
686
+ const [y, m, d] = selectedDateKey.split("-").map(Number);
687
+ return new Date(y, m - 1, d, 12, 0, 0);
688
+ }, [selectedDateKey]);
689
+ const filteredEntries = useMemo(() => {
690
+ if (!selectedDateKey) return entries;
691
+ return entries.filter((e) => toDateKey(e.createdAt) === selectedDateKey);
692
+ }, [entries, selectedDateKey]);
693
+ const handleRestore = async (target) => {
694
+ if (!effectiveDocumentId || !model) return;
695
+ setIsRestoring(true);
696
+ try {
697
+ const snapshot = target.content;
698
+ if (!snapshot || typeof snapshot !== "object") {
699
+ toggleNotification({ type: "danger", message: "Invalid snapshot data." });
700
+ return;
701
+ }
702
+ const currentDoc = document;
703
+ if (!currentDoc) {
704
+ toggleNotification({ type: "danger", message: "Current document not available." });
705
+ return;
706
+ }
707
+ const SYSTEM_FIELDS2 = /* @__PURE__ */ new Set([
708
+ "id",
709
+ "documentId",
710
+ "createdAt",
711
+ "updatedAt",
712
+ "publishedAt",
713
+ "createdBy",
714
+ "updatedBy",
715
+ "created_by_id",
716
+ "updated_by_id",
717
+ "created_at",
718
+ "updated_at",
719
+ "published_at",
720
+ "locale",
721
+ "localizations"
722
+ ]);
723
+ const restoreData = {};
724
+ for (const [key, value] of Object.entries(currentDoc)) {
725
+ if (SYSTEM_FIELDS2.has(key)) continue;
726
+ if (key in snapshot) {
727
+ restoreData[key] = snapshot[key];
728
+ } else {
729
+ restoreData[key] = getEmptyValue(value);
730
+ }
731
+ }
732
+ await post(`/${PLUGIN_ID}/entries/snapshot-before-restore`, {
733
+ contentType: model,
734
+ entryDocumentId: effectiveDocumentId,
735
+ locale: target.locale || null,
736
+ restoreData
737
+ });
738
+ toggleNotification({
739
+ type: "success",
740
+ message: "Entry restored from timeline snapshot. Refreshing…"
741
+ });
742
+ setTimeout(() => window.location.reload(), 800);
743
+ } catch (err) {
744
+ toggleNotification({
745
+ type: "danger",
746
+ message: err?.response?.data?.error?.message || err?.message || "Failed to restore entry."
747
+ });
748
+ } finally {
749
+ setIsRestoring(false);
750
+ setRestoreTarget(null);
751
+ }
752
+ };
753
+ if (!effectiveDocumentId) {
754
+ return {
755
+ title: "Timeline",
756
+ content: /* @__PURE__ */ jsx(Box, { padding: 2, children: /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Save the entry to start tracking history." }) })
757
+ };
758
+ }
759
+ if (loading) {
760
+ return {
761
+ title: "Timeline",
762
+ content: /* @__PURE__ */ jsxs(Flex, { justifyContent: "center", alignItems: "center", gap: 2, children: [
763
+ /* @__PURE__ */ jsx(Loader, { small: true }),
764
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "Loading history…" })
765
+ ] })
766
+ };
767
+ }
768
+ return {
769
+ title: `Timeline (${entries.length})`,
770
+ content: /* @__PURE__ */ jsxs(Box, { width: "100%", maxWidth: "100%", children: [
771
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: 3, children: [
772
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", gap: 2, children: [
773
+ /* @__PURE__ */ jsx(
774
+ IconButton,
775
+ {
776
+ onClick: loadHistory,
777
+ variant: "tertiary",
778
+ disabled: loading,
779
+ label: "Refresh timeline",
780
+ children: /* @__PURE__ */ jsx(ArrowsCounterClockwise, {})
781
+ }
782
+ ),
783
+ /* @__PURE__ */ jsx(Box, { width: "100%", textAlign: "center", children: entries.length > 0 ? /* @__PURE__ */ jsx(
784
+ Button,
785
+ {
786
+ width: "100%",
787
+ variant: "secondary",
788
+ size: "S",
789
+ startIcon: /* @__PURE__ */ jsx(Eye, {}),
790
+ onClick: () => {
791
+ const params = new URLSearchParams();
792
+ params.set("contentType", model);
793
+ params.set("entryDocumentId", effectiveDocumentId);
794
+ const currentLocale = document?.locale || new URLSearchParams(window.location.search).get("plugins[i18n][locale]");
795
+ if (currentLocale) params.set("locale", currentLocale);
796
+ window.location.href = `/admin/plugins/${PLUGIN_ID}?${params.toString()}`;
797
+ },
798
+ children: "View all"
799
+ }
800
+ ) : /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral600", children: "No history yet." }) })
801
+ ] }) }),
802
+ entries.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
803
+ /* @__PURE__ */ jsx(Box, { width: "100%", maxWidth: "100%", minWidth: "100%", style: { overflow: "auto" }, children: /* @__PURE__ */ jsx(
804
+ DatePicker,
805
+ {
806
+ value: selectedDateValue,
807
+ onChange: (date) => {
808
+ if (!date) {
809
+ setSelectedDateKey(null);
810
+ return;
811
+ }
812
+ const key = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
813
+ setSelectedDateKey(key);
814
+ },
815
+ onClear: () => setSelectedDateKey(null)
816
+ }
817
+ ) }),
818
+ filteredEntries.length === 0 ? /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: "No actions on this date." }) : filteredEntries.map((entry) => {
819
+ const colors = ACTION_COLORS[entry.action] || ACTION_COLORS.update;
820
+ return /* @__PURE__ */ jsx(Box, { padding: 2, background: "neutral100", hasRadius: true, style: { overflow: "hidden", width: "100%", maxWidth: "100%" }, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 1, style: { minWidth: 0 }, children: [
821
+ /* @__PURE__ */ jsxs(Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
822
+ /* @__PURE__ */ jsx(Badge, { backgroundColor: colors.bg, textColor: colors.text, size: "S", children: entry.action }),
823
+ /* @__PURE__ */ jsx(Typography, { variant: "pi", textColor: "neutral500", children: formatTime(entry.createdAt) })
824
+ ] }),
825
+ entry.userId && (() => {
826
+ const admin = adminUsers[entry.userId];
827
+ const displayName = admin ? `${admin.firstname || ""} ${admin.lastname || ""}`.trim() || entry.userName || `User #${entry.userId}` : entry.userName || `User #${entry.userId}`;
828
+ const label = `${displayName}`;
829
+ const link = getUserLink2(entry.userId);
830
+ admin ? getInitials2(admin) : (entry.userName || "?").charAt(0).toUpperCase();
831
+ return /* @__PURE__ */ jsx(Flex, { gap: 2, alignItems: "center", style: { minWidth: 0, overflow: "hidden" }, children: link ? /* @__PURE__ */ jsx(NavLink, { to: link, style: { textDecoration: "none", minWidth: 0, overflow: "hidden" }, children: /* @__PURE__ */ jsxs(
832
+ Typography,
833
+ {
834
+ variant: "pi",
835
+ textColor: "primary600",
836
+ ellipsis: true,
837
+ children: [
838
+ label,
839
+ entry.userId && /* @__PURE__ */ jsxs(Badge, { backgroundColor: "primary100", textColor: "primary500", marginLeft: 2, children: [
840
+ "ID ",
841
+ entry.userId
842
+ ] })
843
+ ]
844
+ }
845
+ ) }) : /* @__PURE__ */ jsx(
846
+ Typography,
847
+ {
848
+ variant: "pi",
849
+ textColor: "neutral600",
850
+ ellipsis: true,
851
+ style: { minWidth: 0 },
852
+ children: label
853
+ }
854
+ ) });
855
+ })(),
856
+ /* @__PURE__ */ jsx(Box, { width: "100%", children: /* @__PURE__ */ jsxs(Flex, { gap: 2, justifyContent: "center", alignItems: "center", children: [
857
+ /* @__PURE__ */ jsx(
858
+ IconButton,
859
+ {
860
+ label: "View details",
861
+ size: "S",
862
+ onClick: () => setViewEntry(entry),
863
+ children: /* @__PURE__ */ jsx(Eye, {})
864
+ }
865
+ ),
866
+ entry.action !== "delete" && /* @__PURE__ */ jsxs(Dialog.Root, { children: [
867
+ /* @__PURE__ */ jsx(Dialog.Trigger, { children: /* @__PURE__ */ jsx(
868
+ Button,
869
+ {
870
+ variant: "danger-light",
871
+ size: "S",
872
+ startIcon: /* @__PURE__ */ jsx(ArrowClockwise, {}),
873
+ onClick: () => setRestoreTarget(entry),
874
+ width: "100%",
875
+ children: "Restore"
876
+ }
877
+ ) }),
878
+ /* @__PURE__ */ jsxs(Dialog.Content, { children: [
879
+ /* @__PURE__ */ jsx(Dialog.Header, { children: "Restore from Timeline" }),
880
+ /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsx(
881
+ RestoreDiffTable,
882
+ {
883
+ currentData: document || null,
884
+ snapshotData: entry.content && typeof entry.content === "object" ? entry.content : {}
885
+ }
886
+ ) }),
887
+ /* @__PURE__ */ jsxs(Dialog.Footer, { children: [
888
+ /* @__PURE__ */ jsx(Dialog.Cancel, { children: /* @__PURE__ */ jsx(Button, { variant: "tertiary", children: "Cancel" }) }),
889
+ /* @__PURE__ */ jsx(Dialog.Action, { children: /* @__PURE__ */ jsx(
890
+ Button,
891
+ {
892
+ variant: "danger-light",
893
+ onClick: () => handleRestore(entry),
894
+ loading: isRestoring,
895
+ children: "Restore"
896
+ }
897
+ ) })
898
+ ] })
899
+ ] })
900
+ ] })
901
+ ] }) })
902
+ ] }) }, entry.id);
903
+ })
904
+ ] })
905
+ ] }),
906
+ /* @__PURE__ */ jsx(
907
+ TimelineDetailModal,
908
+ {
909
+ entry: viewEntry,
910
+ open: !!viewEntry,
911
+ onClose: () => setViewEntry(null),
912
+ contentTypes
913
+ }
914
+ )
915
+ ] })
916
+ };
917
+ };
918
+ function getEmptyValue(currentValue) {
919
+ if (currentValue === null || currentValue === void 0) return null;
920
+ if (Array.isArray(currentValue)) return [];
921
+ if (typeof currentValue === "string") return "";
922
+ if (typeof currentValue === "number") return null;
923
+ if (typeof currentValue === "boolean") return false;
924
+ if (typeof currentValue === "object") return null;
925
+ return null;
926
+ }
927
+ const index = {
928
+ register(app) {
929
+ app.addMenuLink({
930
+ to: `plugins/${PLUGIN_ID}`,
931
+ icon: PluginIcon,
932
+ intlLabel: {
933
+ id: `${PLUGIN_ID}.plugin.name`,
934
+ defaultMessage: "Timeline"
935
+ },
936
+ Component: async () => {
937
+ const { App } = await import("./App-BYK_GACN.mjs");
938
+ return App;
939
+ }
940
+ });
941
+ app.createSettingSection(
942
+ {
943
+ id: PLUGIN_ID,
944
+ intlLabel: {
945
+ id: `${PLUGIN_ID}.plugin.name`,
946
+ defaultMessage: "Timeline"
947
+ }
948
+ },
949
+ [
950
+ {
951
+ intlLabel: {
952
+ id: `${PLUGIN_ID}.settings.content-types`,
953
+ defaultMessage: "Content Types"
954
+ },
955
+ id: "content-types",
956
+ to: `${PLUGIN_ID}/content-types`,
957
+ Component: async () => {
958
+ const { SettingsPage } = await import("./SettingsPage-C87KaVB_.mjs");
959
+ return { default: SettingsPage };
960
+ }
961
+ },
962
+ {
963
+ intlLabel: {
964
+ id: `${PLUGIN_ID}.settings.schedule`,
965
+ defaultMessage: "Schedule"
966
+ },
967
+ id: "schedule",
968
+ to: `${PLUGIN_ID}/schedule`,
969
+ Component: async () => {
970
+ const { SchedulePage } = await import("./SchedulePage-Bl0OZTNc.mjs");
971
+ return { default: SchedulePage };
972
+ }
973
+ }
974
+ ]
975
+ );
976
+ app.registerPlugin({
977
+ id: PLUGIN_ID,
978
+ initializer: Initializer,
979
+ isReady: false,
980
+ name: PLUGIN_ID
981
+ });
982
+ },
983
+ bootstrap(app) {
984
+ const contentManagerApis = app.getPlugin("content-manager").apis;
985
+ contentManagerApis.addEditViewSidePanel([TimelinePanel]);
986
+ },
987
+ async registerTrads({ locales }) {
988
+ return Promise.all(
989
+ locales.map(async (locale) => {
990
+ try {
991
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-Byx4XI2L.mjs") }), `./translations/${locale}.json`, 3);
992
+ return { data, locale };
993
+ } catch {
994
+ return { data: {}, locale };
995
+ }
996
+ })
997
+ );
998
+ }
999
+ };
1000
+ export {
1001
+ PLUGIN_ID as P,
1002
+ RestoreDiffTable as R,
1003
+ TimelineDetailModal as T,
1004
+ index as i
1005
+ };