strapi-plugin-magic-mark 1.1.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.
Files changed (68) hide show
  1. package/COPYRIGHT_NOTICE.txt +20 -0
  2. package/LICENSE +28 -0
  3. package/README.md +372 -0
  4. package/dist/_chunks/App-7ZH1Reka.mjs +1390 -0
  5. package/dist/_chunks/App-CMSut1pt.js +1392 -0
  6. package/dist/_chunks/de-Bag-366k.mjs +49 -0
  7. package/dist/_chunks/de-Dic_hhjg.js +49 -0
  8. package/dist/_chunks/en-C5BvHqNo.js +54 -0
  9. package/dist/_chunks/en-zokEerzt.mjs +54 -0
  10. package/dist/_chunks/es-BlSQpU1z.js +54 -0
  11. package/dist/_chunks/es-Br1ucP3h.mjs +54 -0
  12. package/dist/_chunks/fr-BHciYPYG.js +54 -0
  13. package/dist/_chunks/fr-Dzo3kt_q.mjs +54 -0
  14. package/dist/_chunks/index-B-Cc7QNW.mjs +322 -0
  15. package/dist/_chunks/index-B11QBtag.js +324 -0
  16. package/dist/_chunks/index-DYHEyGJr.mjs +2020 -0
  17. package/dist/_chunks/index-DkkmdRgb.js +2024 -0
  18. package/dist/_chunks/pt-DQoGyzyD.mjs +54 -0
  19. package/dist/_chunks/pt-Dawo5aUA.js +54 -0
  20. package/dist/admin/index.js +3 -0
  21. package/dist/admin/index.mjs +4 -0
  22. package/dist/admin/src/components/AdvancedFilterButton.d.ts +3 -0
  23. package/dist/admin/src/components/AdvancedFilterModal.d.ts +21 -0
  24. package/dist/admin/src/components/CreateEditModal.d.ts +11 -0
  25. package/dist/admin/src/components/CreateViewModal.d.ts +9 -0
  26. package/dist/admin/src/components/CustomSelect.d.ts +13 -0
  27. package/dist/admin/src/components/FilterPreview.d.ts +6 -0
  28. package/dist/admin/src/components/HomePage.d.ts +0 -0
  29. package/dist/admin/src/components/Initializer.d.ts +6 -0
  30. package/dist/admin/src/components/LicenseGuard.d.ts +6 -0
  31. package/dist/admin/src/components/PluginIcon.d.ts +3 -0
  32. package/dist/admin/src/components/QueryBuilder.d.ts +23 -0
  33. package/dist/admin/src/components/SimpleAdvancedFilterModal.d.ts +15 -0
  34. package/dist/admin/src/components/ViewsListPopover.d.ts +2 -0
  35. package/dist/admin/src/components/ViewsWidget.d.ts +8 -0
  36. package/dist/admin/src/index.d.ts +15 -0
  37. package/dist/admin/src/pages/App.d.ts +3 -0
  38. package/dist/admin/src/pages/HomePage.d.ts +3 -0
  39. package/dist/admin/src/pages/HomePageModern.d.ts +3 -0
  40. package/dist/admin/src/pages/License/index.d.ts +3 -0
  41. package/dist/admin/src/permissions.d.ts +15 -0
  42. package/dist/admin/src/pluginId.d.ts +2 -0
  43. package/dist/admin/src/utils/queryGenerator.d.ts +28 -0
  44. package/dist/admin/src/utils/queryParser.d.ts +25 -0
  45. package/dist/admin/src/utils/queryToStructure.d.ts +20 -0
  46. package/dist/server/index.js +1309 -0
  47. package/dist/server/index.mjs +1307 -0
  48. package/dist/server/src/bootstrap.d.ts +5 -0
  49. package/dist/server/src/config/index.d.ts +5 -0
  50. package/dist/server/src/content-types/index.d.ts +82 -0
  51. package/dist/server/src/controllers/controller.d.ts +11 -0
  52. package/dist/server/src/controllers/index.d.ts +20 -0
  53. package/dist/server/src/controllers/license.d.ts +27 -0
  54. package/dist/server/src/destroy.d.ts +5 -0
  55. package/dist/server/src/index.d.ts +195 -0
  56. package/dist/server/src/middlewares/index.d.ts +4 -0
  57. package/dist/server/src/middlewares/license-check.d.ts +6 -0
  58. package/dist/server/src/policies/index.d.ts +4 -0
  59. package/dist/server/src/policies/license-check.d.ts +6 -0
  60. package/dist/server/src/register.d.ts +5 -0
  61. package/dist/server/src/routes/admin.d.ts +12 -0
  62. package/dist/server/src/routes/content-api.d.ts +5 -0
  63. package/dist/server/src/routes/index.d.ts +18 -0
  64. package/dist/server/src/services/index.d.ts +57 -0
  65. package/dist/server/src/services/license-guard.d.ts +103 -0
  66. package/dist/server/src/services/service.d.ts +33 -0
  67. package/package.json +108 -0
  68. package/strapi-server.js +4 -0
@@ -0,0 +1,1392 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const React = require("react");
5
+ const reactIntl = require("react-intl");
6
+ const reactRouterDom = require("react-router-dom");
7
+ const styled = require("styled-components");
8
+ const designSystem = require("@strapi/design-system");
9
+ const icons = require("@strapi/icons");
10
+ const admin = require("@strapi/strapi/admin");
11
+ const index = require("./index-DkkmdRgb.js");
12
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
14
+ const theme = {
15
+ colors: {
16
+ primary: {
17
+ 50: "#F0F9FF",
18
+ 100: "#E0F2FE",
19
+ 500: "#0EA5E9",
20
+ 600: "#0284C7",
21
+ 700: "#0369A1"
22
+ },
23
+ secondary: {
24
+ 600: "#9333EA"
25
+ },
26
+ success: {
27
+ 100: "#DCFCE7",
28
+ 500: "#22C55E",
29
+ 600: "#16A34A"
30
+ },
31
+ warning: {
32
+ 100: "#FEF3C7",
33
+ 500: "#F59E0B",
34
+ 600: "#D97706"
35
+ },
36
+ neutral: {
37
+ 0: "#FFFFFF",
38
+ 50: "#F9FAFB",
39
+ 100: "#F3F4F6",
40
+ 200: "#E5E7EB",
41
+ 600: "#4B5563",
42
+ 700: "#374151",
43
+ 800: "#1F2937"
44
+ }
45
+ },
46
+ shadows: {
47
+ sm: "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)",
48
+ xl: "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)"
49
+ },
50
+ transitions: {
51
+ fast: "150ms cubic-bezier(0.4, 0, 0.2, 1)",
52
+ normal: "300ms cubic-bezier(0.4, 0, 0.2, 1)",
53
+ slow: "500ms cubic-bezier(0.4, 0, 0.2, 1)"
54
+ },
55
+ spacing: {
56
+ xs: "4px",
57
+ sm: "8px",
58
+ md: "16px",
59
+ lg: "24px",
60
+ xl: "32px",
61
+ "2xl": "48px"
62
+ },
63
+ borderRadius: {
64
+ md: "8px",
65
+ lg: "12px",
66
+ xl: "16px"
67
+ }
68
+ };
69
+ const fadeIn$1 = styled.keyframes`
70
+ from { opacity: 0; transform: translateY(10px); }
71
+ to { opacity: 1; transform: translateY(0); }
72
+ `;
73
+ styled.keyframes`
74
+ from { opacity: 0; transform: translateX(-10px); }
75
+ to { opacity: 1; transform: translateX(0); }
76
+ `;
77
+ const shimmer = styled.keyframes`
78
+ 0% { background-position: -200% 0; }
79
+ 100% { background-position: 200% 0; }
80
+ `;
81
+ const float = styled.keyframes`
82
+ 0%, 100% { transform: translateY(0px); }
83
+ 50% { transform: translateY(-5px); }
84
+ `;
85
+ const breakpoints = {
86
+ mobile: "768px"
87
+ };
88
+ const Container = styled__default.default(designSystem.Box)`
89
+ animation: ${fadeIn$1} ${theme.transitions.slow};
90
+ min-height: 100vh;
91
+ max-width: 1440px;
92
+ margin: 0 auto;
93
+ padding: ${theme.spacing.xl} ${theme.spacing.lg} 0;
94
+
95
+ @media screen and (max-width: ${breakpoints.mobile}) {
96
+ padding: ${theme.spacing.md} ${theme.spacing.sm} 0;
97
+ }
98
+ `;
99
+ const Header = styled__default.default(designSystem.Box)`
100
+ background: linear-gradient(135deg,
101
+ ${theme.colors.primary[600]} 0%,
102
+ ${theme.colors.secondary[600]} 100%
103
+ );
104
+ border-radius: ${theme.borderRadius.xl};
105
+ padding: ${theme.spacing.xl} ${theme.spacing["2xl"]};
106
+ margin-bottom: ${theme.spacing.xl};
107
+ position: relative;
108
+ overflow: hidden;
109
+ box-shadow: ${theme.shadows.xl};
110
+
111
+ @media screen and (max-width: ${breakpoints.mobile}) {
112
+ padding: ${theme.spacing.lg} ${theme.spacing.md} !important;
113
+ border-radius: ${theme.borderRadius.lg} !important;
114
+ }
115
+
116
+ &::before {
117
+ content: '';
118
+ position: absolute;
119
+ top: 0;
120
+ left: -100%;
121
+ width: 200%;
122
+ height: 100%;
123
+ background: linear-gradient(
124
+ 90deg,
125
+ transparent,
126
+ rgba(255, 255, 255, 0.1),
127
+ transparent
128
+ );
129
+ animation: ${shimmer} 3s infinite;
130
+ }
131
+
132
+ &::after {
133
+ content: '';
134
+ position: absolute;
135
+ top: 0;
136
+ right: 0;
137
+ width: 100%;
138
+ height: 100%;
139
+ background-image: radial-gradient(circle at 20% 80%, transparent 50%, rgba(255, 255, 255, 0.1) 50%);
140
+ background-size: 15px 15px;
141
+ opacity: 0.3;
142
+ }
143
+ `;
144
+ const HeaderContent = styled__default.default(designSystem.Flex)`
145
+ position: relative;
146
+ z-index: 1;
147
+ `;
148
+ const Title = styled__default.default(designSystem.Typography)`
149
+ color: ${theme.colors.neutral[0]};
150
+ font-size: 2rem;
151
+ font-weight: 700;
152
+ letter-spacing: -0.025em;
153
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
154
+ display: flex;
155
+ align-items: center;
156
+ gap: ${theme.spacing.sm};
157
+
158
+ svg {
159
+ width: 28px;
160
+ height: 28px;
161
+ animation: ${float} 3s ease-in-out infinite;
162
+ }
163
+ `;
164
+ const Subtitle = styled__default.default(designSystem.Typography)`
165
+ color: rgba(255, 255, 255, 0.9);
166
+ font-size: 0.95rem;
167
+ font-weight: 400;
168
+ margin-top: ${theme.spacing.xs};
169
+ letter-spacing: 0.01em;
170
+ `;
171
+ const StatsGrid = styled__default.default.div`
172
+ margin-bottom: ${theme.spacing.xl};
173
+ display: grid;
174
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
175
+ gap: ${theme.spacing.md};
176
+ justify-content: center;
177
+ max-width: 1200px;
178
+ margin-left: auto;
179
+ margin-right: auto;
180
+
181
+ @media screen and (max-width: ${breakpoints.mobile}) {
182
+ grid-template-columns: repeat(2, 1fr) !important;
183
+ gap: 12px !important;
184
+ margin-bottom: 24px !important;
185
+ }
186
+ `;
187
+ const StatCard = styled__default.default(designSystem.Box)`
188
+ background: ${theme.colors.neutral[0]};
189
+ border-radius: ${theme.borderRadius.lg};
190
+ padding: ${theme.spacing.lg};
191
+ position: relative;
192
+ overflow: hidden;
193
+ transition: all ${theme.transitions.normal};
194
+ animation: ${fadeIn$1} ${theme.transitions.slow} backwards;
195
+ animation-delay: ${(props) => props.$delay || "0s"};
196
+ box-shadow: ${theme.shadows.sm};
197
+ border: 1px solid ${theme.colors.neutral[200]};
198
+ min-width: 200px;
199
+ flex: 1;
200
+ text-align: center;
201
+ display: flex;
202
+ flex-direction: column;
203
+ align-items: center;
204
+ justify-content: center;
205
+
206
+ &:hover {
207
+ transform: translateY(-4px);
208
+ box-shadow: ${theme.shadows.xl};
209
+ border-color: ${(props) => props.$color || theme.colors.primary[500]};
210
+
211
+ .stat-icon {
212
+ transform: scale(1.1);
213
+ }
214
+
215
+ .stat-value {
216
+ transform: scale(1.05);
217
+ }
218
+ }
219
+
220
+ @media screen and (max-width: ${breakpoints.mobile}) {
221
+ min-width: unset !important;
222
+ padding: ${theme.spacing.md} !important;
223
+
224
+ &:hover {
225
+ transform: none !important;
226
+ }
227
+ }
228
+ `;
229
+ const StatIcon = styled__default.default(designSystem.Box)`
230
+ width: 64px;
231
+ height: 64px;
232
+ border-radius: ${theme.borderRadius.md};
233
+ display: flex;
234
+ align-items: center;
235
+ justify-content: center;
236
+ background: ${(props) => props.$bg || theme.colors.primary[100]};
237
+ transition: all ${theme.transitions.normal};
238
+ margin-bottom: ${theme.spacing.md};
239
+
240
+ svg {
241
+ width: 32px;
242
+ height: 32px;
243
+ color: ${(props) => props.$color || theme.colors.primary[600]};
244
+ }
245
+ `;
246
+ const StatValue = styled__default.default(designSystem.Typography)`
247
+ font-size: 2.5rem;
248
+ font-weight: 700;
249
+ color: ${theme.colors.neutral[800]};
250
+ line-height: 1;
251
+ margin-bottom: ${theme.spacing.xs};
252
+ transition: transform ${theme.transitions.normal};
253
+ `;
254
+ const StatLabel = styled__default.default(designSystem.Typography)`
255
+ font-size: 0.875rem;
256
+ color: ${theme.colors.neutral[600]};
257
+ font-weight: 500;
258
+ letter-spacing: 0.025em;
259
+ text-transform: capitalize;
260
+ `;
261
+ const DataTable = styled__default.default(designSystem.Box)`
262
+ background: ${theme.colors.neutral[0]};
263
+ border-radius: ${theme.borderRadius.lg};
264
+ overflow: hidden;
265
+ box-shadow: ${theme.shadows.sm};
266
+ border: 1px solid ${theme.colors.neutral[200]};
267
+ margin-bottom: ${theme.spacing.xl};
268
+ `;
269
+ const StyledTable = styled__default.default(designSystem.Table)`
270
+ thead {
271
+ background: ${theme.colors.neutral[0]};
272
+ border-bottom: 2px solid ${theme.colors.neutral[100]};
273
+
274
+ th {
275
+ font-weight: 600;
276
+ color: ${theme.colors.neutral[700]};
277
+ font-size: 0.875rem;
278
+ text-transform: uppercase;
279
+ letter-spacing: 0.025em;
280
+ padding: ${theme.spacing.lg} ${theme.spacing.lg};
281
+ }
282
+ }
283
+
284
+ tbody tr {
285
+ transition: all ${theme.transitions.fast};
286
+ border-bottom: 1px solid ${theme.colors.neutral[100]};
287
+
288
+ &:last-child {
289
+ border-bottom: none;
290
+ }
291
+
292
+ &:hover {
293
+ background: ${theme.colors.neutral[50]};
294
+
295
+ .action-buttons {
296
+ opacity: 1;
297
+ }
298
+ }
299
+
300
+ td {
301
+ padding: ${theme.spacing.lg} ${theme.spacing.lg};
302
+ color: ${theme.colors.neutral[700]};
303
+ vertical-align: middle;
304
+ }
305
+ }
306
+ `;
307
+ styled__default.default(designSystem.Box)`
308
+ width: 4px;
309
+ height: 40px;
310
+ background: ${(props) => props.$isPinned ? theme.colors.warning[500] : "transparent"};
311
+ border-radius: 2px;
312
+ transition: all ${theme.transitions.normal};
313
+ `;
314
+ styled__default.default.div`
315
+ font-size: 32px;
316
+ line-height: 1;
317
+ text-align: center;
318
+ `;
319
+ const ActionButtons = styled__default.default(designSystem.Flex)`
320
+ opacity: 1;
321
+ transition: all ${theme.transitions.fast};
322
+ gap: ${theme.spacing.xs};
323
+ justify-content: flex-end;
324
+ `;
325
+ const FloatingEmoji = styled__default.default.div`
326
+ position: absolute;
327
+ bottom: 40px;
328
+ right: 40px;
329
+ font-size: 72px;
330
+ opacity: 0.08;
331
+ animation: ${float} 4s ease-in-out infinite;
332
+ `;
333
+ const FilterBar = styled__default.default(designSystem.Flex)`
334
+ background: ${theme.colors.neutral[0]};
335
+ padding: ${theme.spacing.md} ${theme.spacing.lg};
336
+ border-radius: ${theme.borderRadius.lg};
337
+ margin-bottom: ${theme.spacing.lg};
338
+ box-shadow: ${theme.shadows.sm};
339
+ border: 1px solid ${theme.colors.neutral[200]};
340
+ gap: ${theme.spacing.md};
341
+ align-items: center;
342
+ `;
343
+ const FilterSelect = styled__default.default(designSystem.Box)`
344
+ min-width: 120px;
345
+ `;
346
+ const SearchInputWrapper = styled__default.default.div`
347
+ position: relative;
348
+ width: 100%;
349
+ display: flex;
350
+ align-items: center;
351
+ `;
352
+ const SearchIcon = styled__default.default(icons.Search)`
353
+ position: absolute;
354
+ left: 12px;
355
+ width: 16px;
356
+ height: 16px;
357
+ color: ${theme.colors.neutral[600]};
358
+ pointer-events: none;
359
+ `;
360
+ const StyledSearchInput = styled__default.default.input`
361
+ width: 100%;
362
+ padding: ${theme.spacing.sm} ${theme.spacing.sm} ${theme.spacing.sm} 36px;
363
+ border: 1px solid ${theme.colors.neutral[200]};
364
+ border-radius: ${theme.borderRadius.md};
365
+ font-size: 0.875rem;
366
+ transition: all ${theme.transitions.fast};
367
+ background: ${theme.colors.neutral[0]};
368
+ color: ${theme.colors.neutral[800]};
369
+
370
+ &:focus {
371
+ outline: none;
372
+ border-color: ${theme.colors.primary[500]};
373
+ box-shadow: 0 0 0 3px ${theme.colors.primary[100]};
374
+ }
375
+
376
+ &::placeholder {
377
+ color: ${theme.colors.neutral[600]};
378
+ }
379
+ `;
380
+ const HomePageModern = () => {
381
+ const { formatMessage } = reactIntl.useIntl();
382
+ const navigate = reactRouterDom.useNavigate();
383
+ const { get, post, del } = admin.useFetchClient();
384
+ const [currentUser, setCurrentUser] = React.useState(null);
385
+ const [bookmarks, setBookmarks] = React.useState([]);
386
+ const [loading, setLoading] = React.useState(false);
387
+ const [showModal, setShowModal] = React.useState(false);
388
+ const [editingBookmark, setEditingBookmark] = React.useState(null);
389
+ const [searchQuery, setSearchQuery] = React.useState("");
390
+ const [filterType, setFilterType] = React.useState("all");
391
+ const [entriesPerPage, setEntriesPerPage] = React.useState("10");
392
+ const [availableRoles, setAvailableRoles] = React.useState([]);
393
+ const [availableUsers, setAvailableUsers] = React.useState([]);
394
+ React.useEffect(() => {
395
+ fetchBookmarks();
396
+ fetchCurrentUser();
397
+ fetchRoles();
398
+ fetchUsers();
399
+ }, []);
400
+ const fetchRoles = async () => {
401
+ try {
402
+ const response = await get(`/${index.pluginId}/roles`);
403
+ const roles = response.data?.data?.data || response.data?.data || [];
404
+ console.log("[Magic-Mark] Available roles in HomePage:", roles);
405
+ setAvailableRoles(roles);
406
+ } catch (error) {
407
+ console.error("[Magic-Mark] Error fetching roles:", error);
408
+ }
409
+ };
410
+ const fetchUsers = async () => {
411
+ try {
412
+ const response = await get("/admin/users?pageSize=100&page=1&sort=firstname");
413
+ console.log("[Magic-Mark] Admin users API response:", response);
414
+ const allUsers = response.data?.data?.results || response.data?.results || [];
415
+ console.log("[Magic-Mark] All users from API in HomePage:", allUsers);
416
+ const users = currentUser && Array.isArray(allUsers) ? allUsers.filter((u) => u.id !== currentUser.id) : allUsers;
417
+ console.log("[Magic-Mark] Available users in HomePage:", users);
418
+ setAvailableUsers(users);
419
+ } catch (error) {
420
+ console.error("[Magic-Mark] Error fetching users from admin API:", error);
421
+ try {
422
+ const response = await get(`/${index.pluginId}/users`);
423
+ const users = response.data?.data?.data || response.data?.data || [];
424
+ setAvailableUsers(users);
425
+ } catch (fallbackError) {
426
+ console.warn("[Magic-Mark] Both user endpoints failed");
427
+ setAvailableUsers([]);
428
+ }
429
+ }
430
+ };
431
+ const fetchCurrentUser = async () => {
432
+ try {
433
+ const response = await get("/admin/users/me");
434
+ console.log("[Magic-Mark] Current user response:", response);
435
+ const userData = response.data?.data || response.data || response;
436
+ setCurrentUser(userData);
437
+ console.log("[Magic-Mark] Current user set to:", userData);
438
+ } catch (error) {
439
+ console.error("[Magic-Mark] Error fetching current user:", error);
440
+ }
441
+ };
442
+ const fetchBookmarks = async () => {
443
+ setLoading(true);
444
+ try {
445
+ const { data } = await get(`/${index.pluginId}/bookmarks`);
446
+ console.log("[Magic-Mark HomePage] Bookmarks loaded:", data);
447
+ console.log("[Magic-Mark HomePage] Current user state:", currentUser);
448
+ setBookmarks(data.data || []);
449
+ } catch (error) {
450
+ console.error("[Magic-Mark HomePage] Error fetching bookmarks:", error);
451
+ } finally {
452
+ setLoading(false);
453
+ }
454
+ };
455
+ const handleDelete = async (id) => {
456
+ if (!confirm(formatMessage({
457
+ id: `${index.pluginId}.confirm.delete`,
458
+ defaultMessage: "Are you sure you want to delete this bookmark?"
459
+ }))) {
460
+ return;
461
+ }
462
+ try {
463
+ await del(`/${index.pluginId}/bookmarks/${id}`);
464
+ console.log("[Magic-Mark HomePage] Bookmark deleted:", id);
465
+ fetchBookmarks();
466
+ } catch (error) {
467
+ console.error("[Magic-Mark HomePage] Error deleting bookmark:", error);
468
+ }
469
+ };
470
+ const handlePin = async (bookmark) => {
471
+ try {
472
+ await post(`/${index.pluginId}/bookmarks/${bookmark.id}/pin`, {
473
+ isPinned: !bookmark.isPinned
474
+ });
475
+ console.log("[Magic-Mark HomePage] Bookmark pinned:", bookmark.id);
476
+ fetchBookmarks();
477
+ } catch (error) {
478
+ console.error("[Magic-Mark HomePage] Error pinning bookmark:", error);
479
+ }
480
+ };
481
+ const handleEdit = (bookmark) => {
482
+ setEditingBookmark(bookmark);
483
+ setShowModal(true);
484
+ };
485
+ const handleModalClose = () => {
486
+ setShowModal(false);
487
+ setEditingBookmark(null);
488
+ };
489
+ const handleModalSuccess = () => {
490
+ setShowModal(false);
491
+ setEditingBookmark(null);
492
+ fetchBookmarks();
493
+ };
494
+ const handleBookmarkClick = (bookmark) => {
495
+ if (bookmark.path) {
496
+ let path = bookmark.path;
497
+ if (path.startsWith("/admin/")) {
498
+ path = path.substring(6);
499
+ }
500
+ const url = bookmark.query ? `${path}?${bookmark.query}` : path;
501
+ console.log("[HomePage] Navigating to:", url);
502
+ navigate(url);
503
+ }
504
+ };
505
+ const filteredBookmarks = bookmarks.filter((bookmark) => {
506
+ if (filterType === "pinned" && !bookmark.isPinned) return false;
507
+ if (filterType === "unpinned" && bookmark.isPinned) return false;
508
+ if (!searchQuery) return true;
509
+ const query = searchQuery.toLowerCase();
510
+ return bookmark.name.toLowerCase().includes(query) || bookmark.description && bookmark.description.toLowerCase().includes(query) || bookmark.path.toLowerCase().includes(query);
511
+ }).slice(0, parseInt(entriesPerPage));
512
+ const pinnedBookmarks = bookmarks.filter((b) => b.isPinned);
513
+ const myBookmarks = bookmarks.filter((b) => b.createdBy?.id === currentUser?.id);
514
+ const sharedWithMe = bookmarks.filter((b) => b.createdBy?.id !== currentUser?.id && b.createdBy?.id);
515
+ return /* @__PURE__ */ jsxRuntime.jsxs(Container, { padding: 8, children: [
516
+ /* @__PURE__ */ jsxRuntime.jsx(Header, { children: /* @__PURE__ */ jsxRuntime.jsxs(HeaderContent, { direction: "column", alignItems: "flex-start", gap: 2, children: [
517
+ /* @__PURE__ */ jsxRuntime.jsxs(Title, { children: [
518
+ /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkle, {}),
519
+ " MagicMark"
520
+ ] }),
521
+ /* @__PURE__ */ jsxRuntime.jsx(Subtitle, { children: "Save filtered views and navigate with one click" })
522
+ ] }) }),
523
+ /* @__PURE__ */ jsxRuntime.jsxs(StatsGrid, { children: [
524
+ /* @__PURE__ */ jsxRuntime.jsxs(StatCard, { $delay: "0.1s", $color: theme.colors.primary[500], children: [
525
+ /* @__PURE__ */ jsxRuntime.jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.primary[100], $color: theme.colors.primary[600], children: /* @__PURE__ */ jsxRuntime.jsx(icons.User, {}) }),
526
+ /* @__PURE__ */ jsxRuntime.jsx(StatValue, { className: "stat-value", children: myBookmarks.length }),
527
+ /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: "My Bookmarks" })
528
+ ] }),
529
+ /* @__PURE__ */ jsxRuntime.jsxs(StatCard, { $delay: "0.2s", $color: theme.colors.success[500], children: [
530
+ /* @__PURE__ */ jsxRuntime.jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.success[100], $color: theme.colors.success[600], children: /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkle, {}) }),
531
+ /* @__PURE__ */ jsxRuntime.jsx(StatValue, { className: "stat-value", children: sharedWithMe.length }),
532
+ /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: "Shared with Me" })
533
+ ] }),
534
+ /* @__PURE__ */ jsxRuntime.jsxs(StatCard, { $delay: "0.3s", $color: theme.colors.warning[500], children: [
535
+ /* @__PURE__ */ jsxRuntime.jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.warning[100], $color: theme.colors.warning[600], children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pin, {}) }),
536
+ /* @__PURE__ */ jsxRuntime.jsx(StatValue, { className: "stat-value", children: pinnedBookmarks.length }),
537
+ /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: "Pinned" })
538
+ ] }),
539
+ /* @__PURE__ */ jsxRuntime.jsxs(StatCard, { $delay: "0.4s", $color: theme.colors.neutral[600], children: [
540
+ /* @__PURE__ */ jsxRuntime.jsx(StatIcon, { className: "stat-icon", $bg: theme.colors.neutral[100], $color: theme.colors.neutral[600], children: /* @__PURE__ */ jsxRuntime.jsx(icons.Link, {}) }),
541
+ /* @__PURE__ */ jsxRuntime.jsx(StatValue, { className: "stat-value", children: bookmarks.length }),
542
+ /* @__PURE__ */ jsxRuntime.jsx(StatLabel, { children: "Total Available" })
543
+ ] })
544
+ ] }),
545
+ loading && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading bookmarks..." }) }),
546
+ !loading && sharedWithMe.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: {
547
+ padding: theme.spacing.lg,
548
+ background: theme.colors.primary[50],
549
+ borderRadius: theme.borderRadius.lg,
550
+ border: `1px solid ${theme.colors.primary[200]}`
551
+ }, children: [
552
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "beta", style: { marginBottom: theme.spacing.sm, color: theme.colors.primary[700] }, children: "🤝 Shared with You" }),
553
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", style: { color: theme.colors.primary[600] }, children: [
554
+ sharedWithMe.length,
555
+ " bookmark",
556
+ sharedWithMe.length > 1 ? "s" : "",
557
+ " have been shared with you"
558
+ ] }),
559
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginTop: 2, children: [...new Map(sharedWithMe.map((b) => [b.createdBy?.id, b.createdBy])).values()].map((creator) => {
560
+ const creatorBookmarks = sharedWithMe.filter((b) => b.createdBy?.id === creator?.id);
561
+ return creator && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", marginTop: 1, children: [
562
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", fontWeight: "semiBold", children: [
563
+ creator.firstname || "Unknown",
564
+ " ",
565
+ creator.lastname || "",
566
+ ":"
567
+ ] }),
568
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", marginLeft: 1, children: [
569
+ creatorBookmarks.length,
570
+ " bookmark",
571
+ creatorBookmarks.length > 1 ? "s" : ""
572
+ ] })
573
+ ] }, creator.id);
574
+ }) })
575
+ ] }) }),
576
+ !loading && bookmarks.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
577
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { marginBottom: theme.spacing.md }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "delta", style: { marginBottom: theme.spacing.md, color: theme.colors.neutral[700] }, children: "🔖 All Available Bookmarks" }) }),
578
+ /* @__PURE__ */ jsxRuntime.jsxs(FilterBar, { children: [
579
+ /* @__PURE__ */ jsxRuntime.jsxs(SearchInputWrapper, { style: { flex: 1 }, children: [
580
+ /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, {}),
581
+ /* @__PURE__ */ jsxRuntime.jsx(
582
+ StyledSearchInput,
583
+ {
584
+ value: searchQuery,
585
+ onChange: (e) => setSearchQuery(e.target.value),
586
+ placeholder: "Search by name, description...",
587
+ type: "text"
588
+ }
589
+ )
590
+ ] }),
591
+ /* @__PURE__ */ jsxRuntime.jsx(FilterSelect, { children: /* @__PURE__ */ jsxRuntime.jsxs(
592
+ designSystem.SingleSelect,
593
+ {
594
+ value: filterType,
595
+ onChange: setFilterType,
596
+ placeholder: "Filter",
597
+ size: "S",
598
+ children: [
599
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "all", children: "Show All" }),
600
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "pinned", children: "Pinned Only" }),
601
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "unpinned", children: "Unpinned Only" })
602
+ ]
603
+ }
604
+ ) }),
605
+ /* @__PURE__ */ jsxRuntime.jsx(FilterSelect, { children: /* @__PURE__ */ jsxRuntime.jsxs(
606
+ designSystem.SingleSelect,
607
+ {
608
+ value: entriesPerPage,
609
+ onChange: setEntriesPerPage,
610
+ placeholder: "Entries",
611
+ size: "S",
612
+ children: [
613
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "10", children: "10 entries" }),
614
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "25", children: "25 entries" }),
615
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "50", children: "50 entries" }),
616
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "100", children: "100 entries" })
617
+ ]
618
+ }
619
+ ) })
620
+ ] }),
621
+ /* @__PURE__ */ jsxRuntime.jsx(DataTable, { children: /* @__PURE__ */ jsxRuntime.jsxs(StyledTable, { children: [
622
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
623
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage({
624
+ id: `${index.pluginId}.table.name`,
625
+ defaultMessage: "Name"
626
+ }) }),
627
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: formatMessage({
628
+ id: `${index.pluginId}.table.description`,
629
+ defaultMessage: "Description"
630
+ }) }),
631
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: "Actions" }) })
632
+ ] }) }),
633
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: filteredBookmarks.map((bookmark) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { style: {
634
+ background: bookmark.isPinned ? theme.colors.warning[50] : "transparent"
635
+ }, children: [
636
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: () => handleBookmarkClick(bookmark), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 3, children: [
637
+ /* @__PURE__ */ jsxRuntime.jsx(
638
+ designSystem.Box,
639
+ {
640
+ style: {
641
+ width: "36px",
642
+ height: "36px",
643
+ display: "flex",
644
+ alignItems: "center",
645
+ justifyContent: "center",
646
+ flexShrink: 0,
647
+ fontSize: "24px"
648
+ },
649
+ children: bookmark.emoji
650
+ }
651
+ ),
652
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "flex-start", gap: 0, children: [
653
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { fontWeight: "semiBold", ellipsis: true, style: { fontSize: "1.125rem", lineHeight: "1.4" }, children: bookmark.name }),
654
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, alignItems: "center", children: [
655
+ bookmark.createdBy?.id && currentUser?.id && bookmark.createdBy.id === currentUser.id ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", style: { fontSize: "0.75rem", color: theme.colors.primary[600], fontWeight: 500 }, children: "• My Bookmark" }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", style: { fontSize: "0.75rem", color: theme.colors.neutral[600] }, children: [
656
+ "• Shared by ",
657
+ bookmark.createdBy?.firstname || "Unknown"
658
+ ] }),
659
+ bookmark.isPublic && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", style: { fontSize: "0.75rem", color: theme.colors.success[600], marginLeft: "8px" }, children: "• Public" }),
660
+ bookmark.sharedWithRoles && bookmark.sharedWithRoles.length > 0 && !bookmark.isPublic && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", style: { fontSize: "0.75rem", color: theme.colors.warning[600], marginLeft: "8px" }, children: [
661
+ "• Roles: ",
662
+ bookmark.sharedWithRoles.map((roleId) => {
663
+ const role = availableRoles.find((r) => r.id === roleId);
664
+ return role?.name || `Role ${roleId}`;
665
+ }).join(", ")
666
+ ] }),
667
+ bookmark.sharedWithUsers && bookmark.sharedWithUsers.length > 0 && !bookmark.isPublic && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", style: { fontSize: "0.75rem", color: theme.colors.primary[600], marginLeft: "8px" }, children: [
668
+ "• Users: ",
669
+ bookmark.sharedWithUsers.map((userId) => {
670
+ const user = availableUsers.find((u) => u.id === userId);
671
+ return user ? `${user.firstname} ${user.lastname}` : `User ${userId}`;
672
+ }).join(", ")
673
+ ] })
674
+ ] })
675
+ ] })
676
+ ] }) }),
677
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { onClick: () => handleBookmarkClick(bookmark), style: { cursor: "pointer" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral600", ellipsis: true, style: { fontSize: "1rem", lineHeight: "1.6", fontWeight: 400 }, children: bookmark.description || "-" }) }),
678
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(
679
+ ActionButtons,
680
+ {
681
+ className: "action-buttons",
682
+ children: [
683
+ /* @__PURE__ */ jsxRuntime.jsx(
684
+ designSystem.Button,
685
+ {
686
+ variant: bookmark.isPinned ? "secondary" : "ghost",
687
+ size: "S",
688
+ onClick: (e) => {
689
+ e.stopPropagation();
690
+ handlePin(bookmark);
691
+ },
692
+ style: {
693
+ color: bookmark.isPinned ? theme.colors.warning[600] : "inherit",
694
+ background: bookmark.isPinned ? theme.colors.warning[100] : "transparent"
695
+ },
696
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pin, {})
697
+ }
698
+ ),
699
+ /* @__PURE__ */ jsxRuntime.jsx(
700
+ designSystem.Button,
701
+ {
702
+ variant: "ghost",
703
+ size: "S",
704
+ onClick: (e) => {
705
+ e.stopPropagation();
706
+ handleBookmarkClick(bookmark);
707
+ },
708
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Eye, {})
709
+ }
710
+ ),
711
+ bookmark.createdBy && /* @__PURE__ */ jsxRuntime.jsx(
712
+ designSystem.Button,
713
+ {
714
+ variant: "ghost",
715
+ size: "S",
716
+ onClick: (e) => {
717
+ e.stopPropagation();
718
+ handleEdit(bookmark);
719
+ },
720
+ disabled: bookmark.createdBy?.id !== currentUser?.id,
721
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
722
+ }
723
+ ),
724
+ bookmark.createdBy?.id && currentUser?.id && bookmark.createdBy.id === currentUser.id && /* @__PURE__ */ jsxRuntime.jsx(
725
+ designSystem.Button,
726
+ {
727
+ variant: "ghost",
728
+ size: "S",
729
+ onClick: (e) => {
730
+ e.stopPropagation();
731
+ handleDelete(bookmark.id);
732
+ },
733
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
734
+ }
735
+ )
736
+ ]
737
+ }
738
+ ) })
739
+ ] }, bookmark.id)) })
740
+ ] }) })
741
+ ] }),
742
+ !loading && bookmarks.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs(
743
+ designSystem.Box,
744
+ {
745
+ style: {
746
+ background: theme.colors.neutral[0],
747
+ borderRadius: theme.borderRadius.xl,
748
+ border: `2px dashed ${theme.colors.neutral[200]}`,
749
+ padding: theme.spacing["3xl"],
750
+ textAlign: "center",
751
+ position: "relative",
752
+ overflow: "hidden",
753
+ minHeight: "400px",
754
+ display: "flex",
755
+ alignItems: "center",
756
+ justifyContent: "center"
757
+ },
758
+ children: [
759
+ /* @__PURE__ */ jsxRuntime.jsx(
760
+ designSystem.Box,
761
+ {
762
+ style: {
763
+ content: "",
764
+ position: "absolute",
765
+ top: 0,
766
+ left: 0,
767
+ right: 0,
768
+ bottom: 0,
769
+ background: `linear-gradient(135deg, ${theme.colors.primary[50]} 0%, ${theme.colors.secondary[50]} 100%)`,
770
+ opacity: 0.3,
771
+ zIndex: 0
772
+ }
773
+ }
774
+ ),
775
+ /* @__PURE__ */ jsxRuntime.jsx(FloatingEmoji, { children: "✨" }),
776
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "center", gap: 6, style: { position: "relative", zIndex: 1 }, children: [
777
+ /* @__PURE__ */ jsxRuntime.jsx(
778
+ designSystem.Box,
779
+ {
780
+ style: {
781
+ width: "120px",
782
+ height: "120px",
783
+ borderRadius: "50%",
784
+ background: `linear-gradient(135deg, ${theme.colors.primary[100]} 0%, ${theme.colors.secondary[100]} 100%)`,
785
+ display: "flex",
786
+ alignItems: "center",
787
+ justifyContent: "center",
788
+ boxShadow: theme.shadows.xl
789
+ },
790
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Sparkle, { style: { width: "60px", height: "60px", color: theme.colors.primary[600] } })
791
+ }
792
+ ),
793
+ /* @__PURE__ */ jsxRuntime.jsx(
794
+ designSystem.Typography,
795
+ {
796
+ variant: "alpha",
797
+ style: {
798
+ fontSize: "1.75rem",
799
+ fontWeight: "700",
800
+ color: theme.colors.neutral[800],
801
+ marginBottom: "8px"
802
+ },
803
+ children: "No bookmarks yet"
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsxRuntime.jsxs(
807
+ designSystem.Typography,
808
+ {
809
+ variant: "omega",
810
+ textColor: "neutral600",
811
+ style: {
812
+ fontSize: "1rem",
813
+ maxWidth: "500px",
814
+ lineHeight: "1.6"
815
+ },
816
+ children: [
817
+ "Navigate to any Content Manager view, apply filters, and click ",
818
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: '"Save Bookmark"' }),
819
+ " to create quick access shortcuts"
820
+ ]
821
+ }
822
+ )
823
+ ] })
824
+ ]
825
+ }
826
+ ),
827
+ showModal && /* @__PURE__ */ jsxRuntime.jsx(
828
+ index.CreateEditModal,
829
+ {
830
+ bookmark: editingBookmark,
831
+ pluginId: index.pluginId,
832
+ onClose: handleModalClose,
833
+ onSuccess: handleModalSuccess,
834
+ currentPath: "",
835
+ currentQuery: ""
836
+ }
837
+ )
838
+ ] });
839
+ };
840
+ const fadeIn = styled.keyframes`
841
+ from { opacity: 0; }
842
+ to { opacity: 1; }
843
+ `;
844
+ const slideUp = styled.keyframes`
845
+ from {
846
+ opacity: 0;
847
+ transform: translateY(30px);
848
+ }
849
+ to {
850
+ opacity: 1;
851
+ transform: translateY(0);
852
+ }
853
+ `;
854
+ const ModalOverlay = styled__default.default.div`
855
+ position: fixed;
856
+ top: 0;
857
+ left: 0;
858
+ right: 0;
859
+ bottom: 0;
860
+ background: rgba(4, 28, 47, 0.85);
861
+ backdrop-filter: blur(8px);
862
+ z-index: 9999;
863
+ display: flex;
864
+ align-items: center;
865
+ justify-content: center;
866
+ animation: ${fadeIn} 0.3s ease-out;
867
+ padding: 20px;
868
+ `;
869
+ const ModalContent = styled__default.default(designSystem.Box)`
870
+ background: white;
871
+ border-radius: 16px;
872
+ width: 100%;
873
+ max-width: 580px;
874
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3);
875
+ animation: ${slideUp} 0.4s cubic-bezier(0.4, 0, 0.2, 1);
876
+ overflow: hidden;
877
+ `;
878
+ const GradientHeader = styled__default.default(designSystem.Box)`
879
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
880
+ padding: 32px 40px;
881
+ position: relative;
882
+ overflow: hidden;
883
+
884
+ &::before {
885
+ content: '';
886
+ position: absolute;
887
+ top: -50%;
888
+ right: -50%;
889
+ width: 200%;
890
+ height: 200%;
891
+ background: radial-gradient(circle, rgba(255,255,255,0.1) 0%, transparent 70%);
892
+ }
893
+ `;
894
+ const IconWrapper = styled__default.default.div`
895
+ width: 72px;
896
+ height: 72px;
897
+ border-radius: 50%;
898
+ background: rgba(255, 255, 255, 0.2);
899
+ display: flex;
900
+ align-items: center;
901
+ justify-content: center;
902
+ margin: 0 auto 16px;
903
+ backdrop-filter: blur(10px);
904
+ border: 2px solid rgba(255, 255, 255, 0.3);
905
+
906
+ svg {
907
+ width: 36px;
908
+ height: 36px;
909
+ color: white;
910
+ }
911
+ `;
912
+ const CloseButton = styled__default.default.button`
913
+ position: absolute;
914
+ top: 16px;
915
+ right: 16px;
916
+ background: rgba(255, 255, 255, 0.2);
917
+ border: 2px solid rgba(255, 255, 255, 0.3);
918
+ border-radius: 50%;
919
+ width: 36px;
920
+ height: 36px;
921
+ display: flex;
922
+ align-items: center;
923
+ justify-content: center;
924
+ cursor: pointer;
925
+ transition: all 0.2s;
926
+ z-index: 10;
927
+
928
+ svg {
929
+ width: 20px;
930
+ height: 20px;
931
+ color: white;
932
+ }
933
+
934
+ &:hover {
935
+ background: rgba(255, 255, 255, 0.3);
936
+ transform: scale(1.1);
937
+ }
938
+
939
+ &:active {
940
+ transform: scale(0.95);
941
+ }
942
+ `;
943
+ const ToggleButton = styled__default.default.button`
944
+ background: none;
945
+ border: none;
946
+ color: #667eea;
947
+ font-size: 13px;
948
+ font-weight: 600;
949
+ cursor: pointer;
950
+ padding: 8px 0;
951
+ text-decoration: underline;
952
+ transition: color 0.2s;
953
+
954
+ &:hover {
955
+ color: #764ba2;
956
+ }
957
+ `;
958
+ const LicenseGuard = ({ children }) => {
959
+ const { get, post } = admin.useFetchClient();
960
+ const { toggleNotification } = admin.useNotification();
961
+ const navigate = reactRouterDom.useNavigate();
962
+ const [isChecking, setIsChecking] = React.useState(true);
963
+ const [needsLicense, setNeedsLicense] = React.useState(false);
964
+ const [isCreating, setIsCreating] = React.useState(false);
965
+ const [useExistingKey, setUseExistingKey] = React.useState(false);
966
+ const [useAutoCreate, setUseAutoCreate] = React.useState(true);
967
+ const [existingLicenseKey, setExistingLicenseKey] = React.useState("");
968
+ const [existingEmail, setExistingEmail] = React.useState("");
969
+ const [adminUser, setAdminUser] = React.useState(null);
970
+ const [formData, setFormData] = React.useState({
971
+ email: "",
972
+ firstName: "",
973
+ lastName: ""
974
+ });
975
+ React.useEffect(() => {
976
+ checkLicenseStatus();
977
+ fetchAdminUser();
978
+ }, []);
979
+ const fetchAdminUser = async () => {
980
+ try {
981
+ const response = await get("/admin/users/me");
982
+ console.log("Admin user response:", response);
983
+ const userData = response.data?.data || response.data;
984
+ console.log("User data extracted:", userData);
985
+ if (userData) {
986
+ setAdminUser(userData);
987
+ setFormData({
988
+ email: userData.email || "",
989
+ firstName: userData.firstname || "",
990
+ lastName: userData.lastname || ""
991
+ });
992
+ console.log("Admin user set:", {
993
+ email: userData.email,
994
+ firstname: userData.firstname,
995
+ lastname: userData.lastname
996
+ });
997
+ }
998
+ } catch (error) {
999
+ console.error("Error fetching admin user:", error);
1000
+ }
1001
+ };
1002
+ const checkLicenseStatus = async () => {
1003
+ setIsChecking(true);
1004
+ try {
1005
+ const response = await get("/magic-mark/license/status");
1006
+ if (response.data.valid) {
1007
+ setNeedsLicense(false);
1008
+ } else {
1009
+ setNeedsLicense(true);
1010
+ }
1011
+ } catch (error) {
1012
+ console.error("Error checking license:", error);
1013
+ setNeedsLicense(true);
1014
+ } finally {
1015
+ setIsChecking(false);
1016
+ }
1017
+ };
1018
+ const handleAutoCreateLicense = async (e) => {
1019
+ e.preventDefault();
1020
+ setIsCreating(true);
1021
+ try {
1022
+ console.log("Auto-creating license with admin user data...");
1023
+ const response = await post("/magic-mark/license/auto-create", {});
1024
+ if (response.data && response.data.success) {
1025
+ toggleNotification({
1026
+ type: "success",
1027
+ message: `License automatically created! Reloading...`
1028
+ });
1029
+ setNeedsLicense(false);
1030
+ setTimeout(() => {
1031
+ window.location.reload();
1032
+ }, 500);
1033
+ } else {
1034
+ throw new Error("Failed to auto-create license");
1035
+ }
1036
+ } catch (error) {
1037
+ console.error("Error auto-creating license:", error);
1038
+ toggleNotification({
1039
+ type: "danger",
1040
+ message: error?.response?.data?.error?.message || "Failed to auto-create license. Try manual creation."
1041
+ });
1042
+ setIsCreating(false);
1043
+ setUseAutoCreate(false);
1044
+ }
1045
+ };
1046
+ const handleCreateLicense = async (e) => {
1047
+ e.preventDefault();
1048
+ if (!formData.email || !formData.firstName || !formData.lastName) {
1049
+ toggleNotification({
1050
+ type: "warning",
1051
+ message: "Please fill in all fields"
1052
+ });
1053
+ return;
1054
+ }
1055
+ setIsCreating(true);
1056
+ try {
1057
+ console.log("Creating license with data:", formData);
1058
+ const response = await post("/magic-mark/license/create", formData);
1059
+ console.log("License creation response:", response);
1060
+ if (response.data && response.data.success) {
1061
+ toggleNotification({
1062
+ type: "success",
1063
+ message: `License created successfully! Reloading...`
1064
+ });
1065
+ setNeedsLicense(false);
1066
+ setTimeout(() => {
1067
+ window.location.reload();
1068
+ }, 500);
1069
+ } else {
1070
+ throw new Error("Failed to create license");
1071
+ }
1072
+ } catch (error) {
1073
+ console.error("Error creating license:", error);
1074
+ toggleNotification({
1075
+ type: "danger",
1076
+ message: error?.response?.data?.error?.message || "Failed to create license. Please try again."
1077
+ });
1078
+ setIsCreating(false);
1079
+ }
1080
+ };
1081
+ const handleValidateExistingKey = async (e) => {
1082
+ e.preventDefault();
1083
+ if (!existingLicenseKey.trim() || !existingEmail.trim()) {
1084
+ toggleNotification({
1085
+ type: "warning",
1086
+ message: "Please enter both license key and email address"
1087
+ });
1088
+ return;
1089
+ }
1090
+ setIsCreating(true);
1091
+ try {
1092
+ console.log("Validating existing license key...");
1093
+ const pluginStore = await post("/magic-mark/license/store-key", {
1094
+ licenseKey: existingLicenseKey.trim(),
1095
+ email: existingEmail.trim()
1096
+ });
1097
+ if (pluginStore.data && pluginStore.data.success) {
1098
+ toggleNotification({
1099
+ type: "success",
1100
+ message: "License key validated successfully! Reloading..."
1101
+ });
1102
+ setNeedsLicense(false);
1103
+ setTimeout(() => {
1104
+ window.location.reload();
1105
+ }, 500);
1106
+ } else {
1107
+ throw new Error("Invalid license key or email");
1108
+ }
1109
+ } catch (error) {
1110
+ console.error("Error validating license key:", error);
1111
+ toggleNotification({
1112
+ type: "danger",
1113
+ message: error?.response?.data?.error?.message || "Invalid license key or email address. Please check and try again."
1114
+ });
1115
+ setIsCreating(false);
1116
+ }
1117
+ };
1118
+ const handleClose = () => {
1119
+ navigate("/content-manager");
1120
+ };
1121
+ if (isChecking) {
1122
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 8, style: { textAlign: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Checking license..." }) });
1123
+ }
1124
+ if (needsLicense) {
1125
+ return /* @__PURE__ */ jsxRuntime.jsx(ModalOverlay, { children: /* @__PURE__ */ jsxRuntime.jsxs(ModalContent, { children: [
1126
+ /* @__PURE__ */ jsxRuntime.jsxs(GradientHeader, { children: [
1127
+ /* @__PURE__ */ jsxRuntime.jsx(CloseButton, { onClick: handleClose, type: "button", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Cross, {}) }),
1128
+ /* @__PURE__ */ jsxRuntime.jsx(IconWrapper, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Key, {}) }),
1129
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { textAlign: "center", position: "relative" }, children: [
1130
+ /* @__PURE__ */ jsxRuntime.jsx(
1131
+ designSystem.Typography,
1132
+ {
1133
+ variant: "alpha",
1134
+ style: {
1135
+ color: "white",
1136
+ fontSize: "24px",
1137
+ fontWeight: "700",
1138
+ marginBottom: "12px",
1139
+ display: "block"
1140
+ },
1141
+ children: "🔐 Activate MagicMark Plugin"
1142
+ }
1143
+ ),
1144
+ /* @__PURE__ */ jsxRuntime.jsx(
1145
+ designSystem.Typography,
1146
+ {
1147
+ variant: "epsilon",
1148
+ style: {
1149
+ color: "rgba(255, 255, 255, 0.9)",
1150
+ fontSize: "14px",
1151
+ display: "block"
1152
+ },
1153
+ children: useExistingKey ? "Enter your existing license key" : "Create a license to start using the plugin"
1154
+ }
1155
+ )
1156
+ ] })
1157
+ ] }),
1158
+ /* @__PURE__ */ jsxRuntime.jsx("form", { onSubmit: useExistingKey ? handleValidateExistingKey : useAutoCreate ? handleAutoCreateLicense : handleCreateLicense, children: /* @__PURE__ */ jsxRuntime.jsx(
1159
+ designSystem.Box,
1160
+ {
1161
+ padding: 6,
1162
+ paddingLeft: 8,
1163
+ paddingRight: 8,
1164
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, style: { width: "100%" }, children: [
1165
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { textAlign: "center", width: "100%" }, children: [
1166
+ !useExistingKey && /* @__PURE__ */ jsxRuntime.jsx(
1167
+ ToggleButton,
1168
+ {
1169
+ type: "button",
1170
+ onClick: () => setUseAutoCreate(!useAutoCreate),
1171
+ disabled: isCreating,
1172
+ style: { marginBottom: "8px", display: "block", margin: "0 auto" },
1173
+ children: useAutoCreate ? "→ Manual entry" : "← Auto-create with my account"
1174
+ }
1175
+ ),
1176
+ /* @__PURE__ */ jsxRuntime.jsx(
1177
+ ToggleButton,
1178
+ {
1179
+ type: "button",
1180
+ onClick: () => {
1181
+ setUseExistingKey(!useExistingKey);
1182
+ if (!useExistingKey) setUseAutoCreate(false);
1183
+ },
1184
+ disabled: isCreating,
1185
+ children: useExistingKey ? "← Create new license" : "Have a license key? →"
1186
+ }
1187
+ )
1188
+ ] }),
1189
+ /* @__PURE__ */ jsxRuntime.jsx(
1190
+ designSystem.Box,
1191
+ {
1192
+ background: "primary100",
1193
+ padding: 4,
1194
+ style: {
1195
+ borderRadius: "8px",
1196
+ border: "2px solid #BAE6FD",
1197
+ width: "100%"
1198
+ },
1199
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", style: { fontSize: "13px", lineHeight: "1.6" }, children: useExistingKey ? "🔑 Enter your email and license key to activate." : useAutoCreate && adminUser && adminUser.email ? `✨ Click "Activate" to auto-create a license with your account (${adminUser.email})` : useAutoCreate ? '✨ Click "Activate" to auto-create a license with your admin account' : "💡 A license will be created with the details below." })
1200
+ }
1201
+ ),
1202
+ useExistingKey ? (
1203
+ // Existing License Key Input
1204
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1205
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { width: "100%" }, children: [
1206
+ /* @__PURE__ */ jsxRuntime.jsx(
1207
+ designSystem.Typography,
1208
+ {
1209
+ variant: "pi",
1210
+ fontWeight: "bold",
1211
+ style: { marginBottom: "8px", display: "block" },
1212
+ children: "Email Address *"
1213
+ }
1214
+ ),
1215
+ /* @__PURE__ */ jsxRuntime.jsx(
1216
+ designSystem.TextInput,
1217
+ {
1218
+ placeholder: "admin@example.com",
1219
+ type: "email",
1220
+ value: existingEmail,
1221
+ onChange: (e) => setExistingEmail(e.target.value),
1222
+ required: true,
1223
+ disabled: isCreating
1224
+ }
1225
+ ),
1226
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: { fontSize: "11px", marginTop: "4px" }, children: "Enter the email address associated with this license" })
1227
+ ] }),
1228
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { width: "100%" }, children: [
1229
+ /* @__PURE__ */ jsxRuntime.jsx(
1230
+ designSystem.Typography,
1231
+ {
1232
+ variant: "pi",
1233
+ fontWeight: "bold",
1234
+ style: { marginBottom: "8px", display: "block" },
1235
+ children: "License Key *"
1236
+ }
1237
+ ),
1238
+ /* @__PURE__ */ jsxRuntime.jsx(
1239
+ designSystem.TextInput,
1240
+ {
1241
+ placeholder: "67C5-40D2-7695-718C",
1242
+ value: existingLicenseKey,
1243
+ onChange: (e) => setExistingLicenseKey(e.target.value),
1244
+ required: true,
1245
+ disabled: isCreating
1246
+ }
1247
+ ),
1248
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", textColor: "neutral600", style: { fontSize: "11px", marginTop: "4px" }, children: "Enter the license key in the format: XXXX-XXXX-XXXX-XXXX" })
1249
+ ] })
1250
+ ] })
1251
+ ) : useAutoCreate && adminUser ? (
1252
+ // Auto-create mode - Show user info
1253
+ /* @__PURE__ */ jsxRuntime.jsxs(
1254
+ designSystem.Box,
1255
+ {
1256
+ background: "success100",
1257
+ padding: 5,
1258
+ style: {
1259
+ borderRadius: "8px",
1260
+ border: "2px solid #DCFCE7",
1261
+ textAlign: "center"
1262
+ },
1263
+ children: [
1264
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "bold", style: { marginBottom: "12px", display: "block" }, children: "Ready to activate with your account:" }),
1265
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", style: { marginBottom: "4px", display: "block" }, children: [
1266
+ "👤 ",
1267
+ adminUser.firstname || "Admin",
1268
+ " ",
1269
+ adminUser.lastname || "User"
1270
+ ] }),
1271
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
1272
+ "📧 ",
1273
+ adminUser.email || "Loading..."
1274
+ ] })
1275
+ ]
1276
+ }
1277
+ )
1278
+ ) : (
1279
+ // Manual Create License Fields
1280
+ /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1281
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { width: "100%" }, children: [
1282
+ /* @__PURE__ */ jsxRuntime.jsx(
1283
+ designSystem.Typography,
1284
+ {
1285
+ variant: "pi",
1286
+ fontWeight: "bold",
1287
+ style: { marginBottom: "8px", display: "block" },
1288
+ children: "Email Address *"
1289
+ }
1290
+ ),
1291
+ /* @__PURE__ */ jsxRuntime.jsx(
1292
+ designSystem.TextInput,
1293
+ {
1294
+ placeholder: "admin@example.com",
1295
+ type: "email",
1296
+ value: formData.email,
1297
+ onChange: (e) => setFormData({ ...formData, email: e.target.value }),
1298
+ required: true,
1299
+ disabled: isCreating
1300
+ }
1301
+ )
1302
+ ] }),
1303
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { width: "100%" }, children: [
1304
+ /* @__PURE__ */ jsxRuntime.jsx(
1305
+ designSystem.Typography,
1306
+ {
1307
+ variant: "pi",
1308
+ fontWeight: "bold",
1309
+ style: { marginBottom: "8px", display: "block" },
1310
+ children: "First Name *"
1311
+ }
1312
+ ),
1313
+ /* @__PURE__ */ jsxRuntime.jsx(
1314
+ designSystem.TextInput,
1315
+ {
1316
+ placeholder: "John",
1317
+ value: formData.firstName,
1318
+ onChange: (e) => setFormData({ ...formData, firstName: e.target.value }),
1319
+ required: true,
1320
+ disabled: isCreating
1321
+ }
1322
+ )
1323
+ ] }),
1324
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { style: { width: "100%" }, children: [
1325
+ /* @__PURE__ */ jsxRuntime.jsx(
1326
+ designSystem.Typography,
1327
+ {
1328
+ variant: "pi",
1329
+ fontWeight: "bold",
1330
+ style: { marginBottom: "8px", display: "block" },
1331
+ children: "Last Name *"
1332
+ }
1333
+ ),
1334
+ /* @__PURE__ */ jsxRuntime.jsx(
1335
+ designSystem.TextInput,
1336
+ {
1337
+ placeholder: "Doe",
1338
+ value: formData.lastName,
1339
+ onChange: (e) => setFormData({ ...formData, lastName: e.target.value }),
1340
+ required: true,
1341
+ disabled: isCreating
1342
+ }
1343
+ )
1344
+ ] })
1345
+ ] })
1346
+ ),
1347
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 3, justifyContent: "center", style: { marginTop: "16px" }, children: useExistingKey ? /* @__PURE__ */ jsxRuntime.jsx(
1348
+ designSystem.Button,
1349
+ {
1350
+ type: "submit",
1351
+ size: "L",
1352
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
1353
+ loading: isCreating,
1354
+ disabled: isCreating || !existingLicenseKey.trim() || !existingEmail.trim(),
1355
+ style: {
1356
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
1357
+ color: "white",
1358
+ fontWeight: "600",
1359
+ border: "none",
1360
+ boxShadow: "0 4px 12px rgba(102, 126, 234, 0.4)"
1361
+ },
1362
+ children: "Validate License"
1363
+ }
1364
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
1365
+ designSystem.Button,
1366
+ {
1367
+ type: "submit",
1368
+ size: "L",
1369
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Check, {}),
1370
+ loading: isCreating,
1371
+ disabled: isCreating || !useAutoCreate && (!formData.email || !formData.firstName || !formData.lastName),
1372
+ style: {
1373
+ background: "linear-gradient(135deg, #667eea 0%, #764ba2 100%)",
1374
+ color: "white",
1375
+ fontWeight: "600",
1376
+ border: "none",
1377
+ boxShadow: "0 4px 12px rgba(102, 126, 234, 0.4)"
1378
+ },
1379
+ children: useAutoCreate ? "Activate License" : "Create License"
1380
+ }
1381
+ ) })
1382
+ ] })
1383
+ }
1384
+ ) })
1385
+ ] }) });
1386
+ }
1387
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
1388
+ };
1389
+ const App = () => {
1390
+ return /* @__PURE__ */ jsxRuntime.jsx(LicenseGuard, { children: /* @__PURE__ */ jsxRuntime.jsx(HomePageModern, {}) });
1391
+ };
1392
+ exports.default = App;