@sonicjs-cms/core 2.5.0 → 2.6.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.
- package/dist/{app-Db0AfT5F.d.cts → app-DV27cjPy.d.cts} +1 -1
- package/dist/{app-Db0AfT5F.d.ts → app-DV27cjPy.d.ts} +1 -1
- package/dist/{chunk-YIXSSJWD.cjs → chunk-63K7XXRX.cjs} +5 -5
- package/dist/{chunk-YIXSSJWD.cjs.map → chunk-63K7XXRX.cjs.map} +1 -1
- package/dist/{chunk-BHNDALCA.js → chunk-7DL5SPPX.js} +6 -4
- package/dist/chunk-7DL5SPPX.js.map +1 -0
- package/dist/{chunk-AZLU3ROK.cjs → chunk-BZC4FYW7.cjs} +4 -4
- package/dist/chunk-BZC4FYW7.cjs.map +1 -0
- package/dist/chunk-CLIH2T74.js +403 -0
- package/dist/chunk-CLIH2T74.js.map +1 -0
- package/dist/{chunk-VEL7QRYI.js → chunk-EVZOVYLO.js} +9 -2
- package/dist/chunk-EVZOVYLO.js.map +1 -0
- package/dist/{chunk-TJTWRO4G.js → chunk-EYWR6UA2.js} +4 -4
- package/dist/{chunk-TJTWRO4G.js.map → chunk-EYWR6UA2.js.map} +1 -1
- package/dist/{chunk-3YUHXWSG.js → chunk-F332TENF.js} +3 -3
- package/dist/{chunk-3YUHXWSG.js.map → chunk-F332TENF.js.map} +1 -1
- package/dist/{chunk-OJZ45OJD.js → chunk-F6GZURXJ.js} +2243 -539
- package/dist/chunk-F6GZURXJ.js.map +1 -0
- package/dist/{chunk-I4V3VZWF.cjs → chunk-IIRVZSP2.cjs} +9 -2
- package/dist/chunk-IIRVZSP2.cjs.map +1 -0
- package/dist/{chunk-V5LBQN3I.js → chunk-KA2PDJNB.js} +4 -4
- package/dist/chunk-KA2PDJNB.js.map +1 -0
- package/dist/{chunk-AVPUX57O.js → chunk-KAOWRIFD.js} +3 -3
- package/dist/{chunk-AVPUX57O.js.map → chunk-KAOWRIFD.js.map} +1 -1
- package/dist/{chunk-ILZ3DP4I.cjs → chunk-MPT5PA6U.cjs} +24 -2
- package/dist/chunk-MPT5PA6U.cjs.map +1 -0
- package/dist/{chunk-UAQL2VWX.cjs → chunk-N7TDLOUE.cjs} +2406 -703
- package/dist/chunk-N7TDLOUE.cjs.map +1 -0
- package/dist/{chunk-YYV3XQOQ.cjs → chunk-T3YIKW2A.cjs} +7 -7
- package/dist/{chunk-YYV3XQOQ.cjs.map → chunk-T3YIKW2A.cjs.map} +1 -1
- package/dist/{chunk-LWG2MWDA.cjs → chunk-Y72M3MVX.cjs} +4 -4
- package/dist/{chunk-LWG2MWDA.cjs.map → chunk-Y72M3MVX.cjs.map} +1 -1
- package/dist/{chunk-SGAG6FD3.js → chunk-YFJJU26H.js} +24 -2
- package/dist/chunk-YFJJU26H.js.map +1 -0
- package/dist/chunk-YHW27CBV.cjs +406 -0
- package/dist/chunk-YHW27CBV.cjs.map +1 -0
- package/dist/{chunk-ZWV3EBZ7.cjs → chunk-YMTTGHEK.cjs} +6 -4
- package/dist/chunk-YMTTGHEK.cjs.map +1 -0
- package/dist/{collection-config-B6gMPunn.d.cts → collection-config-BF95LgQb.d.cts} +1 -1
- package/dist/{collection-config-B6gMPunn.d.ts → collection-config-BF95LgQb.d.ts} +1 -1
- package/dist/index.cjs +2156 -300
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +503 -8
- package/dist/index.d.ts +503 -8
- package/dist/index.js +1893 -44
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +24 -24
- package/dist/middleware.d.cts +1 -1
- package/dist/middleware.d.ts +1 -1
- package/dist/middleware.js +3 -3
- package/dist/migrations-QNYAWQLB.cjs +13 -0
- package/dist/{migrations-NIEUFG44.cjs.map → migrations-QNYAWQLB.cjs.map} +1 -1
- package/dist/migrations-R6NQBKQV.js +4 -0
- package/dist/{migrations-TGZKJKV4.js.map → migrations-R6NQBKQV.js.map} +1 -1
- package/dist/{plugin-bootstrap-dYhD9fQR.d.ts → plugin-bootstrap-CB-xaBfK.d.ts} +2 -2
- package/dist/{plugin-bootstrap-SHsdjE6X.d.cts → plugin-bootstrap-U-cw9jn3.d.cts} +2 -2
- package/dist/plugins.cjs +11 -11
- package/dist/plugins.js +2 -2
- package/dist/routes.cjs +27 -27
- package/dist/routes.d.cts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/routes.js +7 -7
- package/dist/services.cjs +16 -16
- package/dist/services.d.cts +2 -2
- package/dist/services.d.ts +2 -2
- package/dist/services.js +2 -2
- package/dist/templates.cjs +17 -17
- package/dist/templates.js +2 -2
- package/dist/types.d.cts +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/utils.cjs +14 -14
- package/dist/utils.d.cts +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/migrations/029_ai_search_plugin.sql +45 -0
- package/package.json +4 -2
- package/dist/chunk-AI2JJIJX.cjs +0 -211
- package/dist/chunk-AI2JJIJX.cjs.map +0 -1
- package/dist/chunk-AZLU3ROK.cjs.map +0 -1
- package/dist/chunk-BHNDALCA.js.map +0 -1
- package/dist/chunk-I4V3VZWF.cjs.map +0 -1
- package/dist/chunk-ILZ3DP4I.cjs.map +0 -1
- package/dist/chunk-OJZ45OJD.js.map +0 -1
- package/dist/chunk-QDBNW7KQ.js +0 -209
- package/dist/chunk-QDBNW7KQ.js.map +0 -1
- package/dist/chunk-SGAG6FD3.js.map +0 -1
- package/dist/chunk-UAQL2VWX.cjs.map +0 -1
- package/dist/chunk-V5LBQN3I.js.map +0 -1
- package/dist/chunk-VEL7QRYI.js.map +0 -1
- package/dist/chunk-ZWV3EBZ7.cjs.map +0 -1
- package/dist/migrations-NIEUFG44.cjs +0 -13
- package/dist/migrations-TGZKJKV4.js +0 -4
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var chunk7FOAMNTI_cjs = require('./chunk-7FOAMNTI.cjs');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
6
|
-
var
|
|
7
|
-
var
|
|
8
|
-
var
|
|
9
|
-
var
|
|
4
|
+
var chunkT3YIKW2A_cjs = require('./chunk-T3YIKW2A.cjs');
|
|
5
|
+
var chunkMPT5PA6U_cjs = require('./chunk-MPT5PA6U.cjs');
|
|
6
|
+
var chunkIIRVZSP2_cjs = require('./chunk-IIRVZSP2.cjs');
|
|
7
|
+
var chunkBZC4FYW7_cjs = require('./chunk-BZC4FYW7.cjs');
|
|
8
|
+
var chunkYHW27CBV_cjs = require('./chunk-YHW27CBV.cjs');
|
|
9
|
+
var chunkYMTTGHEK_cjs = require('./chunk-YMTTGHEK.cjs');
|
|
10
10
|
var chunkRCQ2HIQD_cjs = require('./chunk-RCQ2HIQD.cjs');
|
|
11
11
|
var hono = require('hono');
|
|
12
12
|
var cors = require('hono/cors');
|
|
@@ -76,7 +76,7 @@ apiContentCrudRoutes.get("/:id", async (c) => {
|
|
|
76
76
|
}, 500);
|
|
77
77
|
}
|
|
78
78
|
});
|
|
79
|
-
apiContentCrudRoutes.post("/",
|
|
79
|
+
apiContentCrudRoutes.post("/", chunkT3YIKW2A_cjs.requireAuth(), async (c) => {
|
|
80
80
|
try {
|
|
81
81
|
const db = c.env.DB;
|
|
82
82
|
const user = c.get("user");
|
|
@@ -142,7 +142,7 @@ apiContentCrudRoutes.post("/", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
|
142
142
|
}, 500);
|
|
143
143
|
}
|
|
144
144
|
});
|
|
145
|
-
apiContentCrudRoutes.put("/:id",
|
|
145
|
+
apiContentCrudRoutes.put("/:id", chunkT3YIKW2A_cjs.requireAuth(), async (c) => {
|
|
146
146
|
try {
|
|
147
147
|
const id = c.req.param("id");
|
|
148
148
|
const db = c.env.DB;
|
|
@@ -206,7 +206,7 @@ apiContentCrudRoutes.put("/:id", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
|
206
206
|
}, 500);
|
|
207
207
|
}
|
|
208
208
|
});
|
|
209
|
-
apiContentCrudRoutes.delete("/:id",
|
|
209
|
+
apiContentCrudRoutes.delete("/:id", chunkT3YIKW2A_cjs.requireAuth(), async (c) => {
|
|
210
210
|
try {
|
|
211
211
|
const id = c.req.param("id");
|
|
212
212
|
const db = c.env.DB;
|
|
@@ -242,7 +242,7 @@ apiRoutes.use("*", async (c, next) => {
|
|
|
242
242
|
c.header("X-Response-Time", `${totalTime}ms`);
|
|
243
243
|
});
|
|
244
244
|
apiRoutes.use("*", async (c, next) => {
|
|
245
|
-
const cacheEnabled = await
|
|
245
|
+
const cacheEnabled = await chunkT3YIKW2A_cjs.isPluginActive(c.env.DB, "core-cache");
|
|
246
246
|
c.set("cacheEnabled", cacheEnabled);
|
|
247
247
|
await next();
|
|
248
248
|
});
|
|
@@ -367,12 +367,12 @@ apiRoutes.get("/content", async (c) => {
|
|
|
367
367
|
});
|
|
368
368
|
}
|
|
369
369
|
}
|
|
370
|
-
const filter =
|
|
370
|
+
const filter = chunkYMTTGHEK_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
371
371
|
if (!filter.limit) {
|
|
372
372
|
filter.limit = 50;
|
|
373
373
|
}
|
|
374
374
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
375
|
-
const builder3 = new
|
|
375
|
+
const builder3 = new chunkYMTTGHEK_cjs.QueryFilterBuilder();
|
|
376
376
|
const queryResult = builder3.build("content", filter);
|
|
377
377
|
if (queryResult.errors.length > 0) {
|
|
378
378
|
return c.json({
|
|
@@ -459,7 +459,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
459
459
|
if (!collectionResult) {
|
|
460
460
|
return c.json({ error: "Collection not found" }, 404);
|
|
461
461
|
}
|
|
462
|
-
const filter =
|
|
462
|
+
const filter = chunkYMTTGHEK_cjs.QueryFilterBuilder.parseFromQuery(queryParams);
|
|
463
463
|
if (!filter.where) {
|
|
464
464
|
filter.where = { and: [] };
|
|
465
465
|
}
|
|
@@ -475,7 +475,7 @@ apiRoutes.get("/collections/:collection/content", async (c) => {
|
|
|
475
475
|
filter.limit = 50;
|
|
476
476
|
}
|
|
477
477
|
filter.limit = Math.min(filter.limit, 1e3);
|
|
478
|
-
const builder3 = new
|
|
478
|
+
const builder3 = new chunkYMTTGHEK_cjs.QueryFilterBuilder();
|
|
479
479
|
const queryResult = builder3.build("content", filter);
|
|
480
480
|
if (queryResult.errors.length > 0) {
|
|
481
481
|
return c.json({
|
|
@@ -600,7 +600,7 @@ var fileValidationSchema = zod.z.object({
|
|
|
600
600
|
// 50MB max
|
|
601
601
|
});
|
|
602
602
|
var apiMediaRoutes = new hono.Hono();
|
|
603
|
-
apiMediaRoutes.use("*",
|
|
603
|
+
apiMediaRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
604
604
|
apiMediaRoutes.post("/upload", async (c) => {
|
|
605
605
|
try {
|
|
606
606
|
const user = c.get("user");
|
|
@@ -1344,8 +1344,8 @@ apiSystemRoutes.get("/env", (c) => {
|
|
|
1344
1344
|
});
|
|
1345
1345
|
var api_system_default = apiSystemRoutes;
|
|
1346
1346
|
var adminApiRoutes = new hono.Hono();
|
|
1347
|
-
adminApiRoutes.use("*",
|
|
1348
|
-
adminApiRoutes.use("*",
|
|
1347
|
+
adminApiRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
1348
|
+
adminApiRoutes.use("*", chunkT3YIKW2A_cjs.requireRole(["admin", "editor"]));
|
|
1349
1349
|
adminApiRoutes.get("/stats", async (c) => {
|
|
1350
1350
|
try {
|
|
1351
1351
|
const db = c.env.DB;
|
|
@@ -1585,6 +1585,107 @@ adminApiRoutes.get("/collections/:id", async (c) => {
|
|
|
1585
1585
|
return c.json({ error: "Failed to fetch collection" }, 500);
|
|
1586
1586
|
}
|
|
1587
1587
|
});
|
|
1588
|
+
adminApiRoutes.get("/references", async (c) => {
|
|
1589
|
+
try {
|
|
1590
|
+
const db = c.env.DB;
|
|
1591
|
+
const url = new URL(c.req.url);
|
|
1592
|
+
const collectionParams = url.searchParams.getAll("collection").flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean);
|
|
1593
|
+
const search = c.req.query("search") || "";
|
|
1594
|
+
const id = c.req.query("id") || "";
|
|
1595
|
+
const limit = Math.min(Number.parseInt(c.req.query("limit") || "20", 10) || 20, 100);
|
|
1596
|
+
if (collectionParams.length === 0) {
|
|
1597
|
+
return c.json({ error: "Collection is required" }, 400);
|
|
1598
|
+
}
|
|
1599
|
+
const placeholders = collectionParams.map(() => "?").join(", ");
|
|
1600
|
+
const collectionStmt = db.prepare(`
|
|
1601
|
+
SELECT id, name, display_name
|
|
1602
|
+
FROM collections
|
|
1603
|
+
WHERE id IN (${placeholders}) OR name IN (${placeholders})
|
|
1604
|
+
`);
|
|
1605
|
+
const collectionResults = await collectionStmt.bind(...collectionParams, ...collectionParams).all();
|
|
1606
|
+
const collections = collectionResults.results || [];
|
|
1607
|
+
if (collections.length === 0) {
|
|
1608
|
+
return c.json({ error: "Collection not found" }, 404);
|
|
1609
|
+
}
|
|
1610
|
+
const collectionById = Object.fromEntries(
|
|
1611
|
+
collections.map((entry) => [
|
|
1612
|
+
entry.id,
|
|
1613
|
+
{
|
|
1614
|
+
id: entry.id,
|
|
1615
|
+
name: entry.name,
|
|
1616
|
+
display_name: entry.display_name
|
|
1617
|
+
}
|
|
1618
|
+
])
|
|
1619
|
+
);
|
|
1620
|
+
const collectionIds = collections.map((entry) => entry.id);
|
|
1621
|
+
if (id) {
|
|
1622
|
+
const idPlaceholders = collectionIds.map(() => "?").join(", ");
|
|
1623
|
+
const itemStmt = db.prepare(`
|
|
1624
|
+
SELECT id, title, slug, collection_id
|
|
1625
|
+
FROM content
|
|
1626
|
+
WHERE id = ? AND collection_id IN (${idPlaceholders})
|
|
1627
|
+
LIMIT 1
|
|
1628
|
+
`);
|
|
1629
|
+
const item = await itemStmt.bind(id, ...collectionIds).first();
|
|
1630
|
+
if (!item) {
|
|
1631
|
+
return c.json({ error: "Reference not found" }, 404);
|
|
1632
|
+
}
|
|
1633
|
+
return c.json({
|
|
1634
|
+
data: {
|
|
1635
|
+
id: item.id,
|
|
1636
|
+
title: item.title,
|
|
1637
|
+
slug: item.slug,
|
|
1638
|
+
collection: collectionById[item.collection_id]
|
|
1639
|
+
}
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
let stmt;
|
|
1643
|
+
let results;
|
|
1644
|
+
const listPlaceholders = collectionIds.map(() => "?").join(", ");
|
|
1645
|
+
const statusFilterValues = ["published"];
|
|
1646
|
+
const statusClause = ` AND status IN (${statusFilterValues.map(() => "?").join(", ")})`;
|
|
1647
|
+
if (search) {
|
|
1648
|
+
const searchParam = `%${search}%`;
|
|
1649
|
+
stmt = db.prepare(`
|
|
1650
|
+
SELECT id, title, slug, status, updated_at, collection_id
|
|
1651
|
+
FROM content
|
|
1652
|
+
WHERE collection_id IN (${listPlaceholders})
|
|
1653
|
+
AND (title LIKE ? OR slug LIKE ?)
|
|
1654
|
+
${statusClause}
|
|
1655
|
+
ORDER BY updated_at DESC
|
|
1656
|
+
LIMIT ?
|
|
1657
|
+
`);
|
|
1658
|
+
const queryResults = await stmt.bind(...collectionIds, searchParam, searchParam, ...statusFilterValues, limit).all();
|
|
1659
|
+
results = queryResults.results;
|
|
1660
|
+
} else {
|
|
1661
|
+
stmt = db.prepare(`
|
|
1662
|
+
SELECT id, title, slug, status, updated_at, collection_id
|
|
1663
|
+
FROM content
|
|
1664
|
+
WHERE collection_id IN (${listPlaceholders})
|
|
1665
|
+
${statusClause}
|
|
1666
|
+
ORDER BY updated_at DESC
|
|
1667
|
+
LIMIT ?
|
|
1668
|
+
`);
|
|
1669
|
+
const queryResults = await stmt.bind(...collectionIds, ...statusFilterValues, limit).all();
|
|
1670
|
+
results = queryResults.results;
|
|
1671
|
+
}
|
|
1672
|
+
const items = (results || []).map((row) => ({
|
|
1673
|
+
id: row.id,
|
|
1674
|
+
title: row.title,
|
|
1675
|
+
slug: row.slug,
|
|
1676
|
+
status: row.status,
|
|
1677
|
+
updated_at: row.updated_at ? Number(row.updated_at) : null,
|
|
1678
|
+
collection: collectionById[row.collection_id]
|
|
1679
|
+
}));
|
|
1680
|
+
return c.json({
|
|
1681
|
+
data: items,
|
|
1682
|
+
count: items.length
|
|
1683
|
+
});
|
|
1684
|
+
} catch (error) {
|
|
1685
|
+
console.error("Error fetching reference options:", error);
|
|
1686
|
+
return c.json({ error: "Failed to fetch references" }, 500);
|
|
1687
|
+
}
|
|
1688
|
+
});
|
|
1588
1689
|
adminApiRoutes.post("/collections", async (c) => {
|
|
1589
1690
|
try {
|
|
1590
1691
|
const contentType = c.req.header("Content-Type");
|
|
@@ -1754,7 +1855,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1754
1855
|
});
|
|
1755
1856
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
1756
1857
|
try {
|
|
1757
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1858
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-QNYAWQLB.cjs');
|
|
1758
1859
|
const db = c.env.DB;
|
|
1759
1860
|
const migrationService = new MigrationService2(db);
|
|
1760
1861
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -1779,7 +1880,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1779
1880
|
error: "Unauthorized. Admin access required."
|
|
1780
1881
|
}, 403);
|
|
1781
1882
|
}
|
|
1782
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1883
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-QNYAWQLB.cjs');
|
|
1783
1884
|
const db = c.env.DB;
|
|
1784
1885
|
const migrationService = new MigrationService2(db);
|
|
1785
1886
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -1798,7 +1899,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1798
1899
|
});
|
|
1799
1900
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
1800
1901
|
try {
|
|
1801
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1902
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-QNYAWQLB.cjs');
|
|
1802
1903
|
const db = c.env.DB;
|
|
1803
1904
|
const migrationService = new MigrationService2(db);
|
|
1804
1905
|
const validation = await migrationService.validateSchema();
|
|
@@ -1825,7 +1926,7 @@ function renderLoginPage(data, demoLoginActive = false) {
|
|
|
1825
1926
|
<meta charset="UTF-8">
|
|
1826
1927
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1827
1928
|
<title>Login - SonicJS AI</title>
|
|
1828
|
-
<link rel="icon" type="image/
|
|
1929
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
1829
1930
|
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
|
|
1830
1931
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
1831
1932
|
<script>
|
|
@@ -1873,8 +1974,8 @@ function renderLoginPage(data, demoLoginActive = false) {
|
|
|
1873
1974
|
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
1874
1975
|
<div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
|
|
1875
1976
|
<!-- Alerts -->
|
|
1876
|
-
${data.error ? `<div class="mb-6">${
|
|
1877
|
-
${data.message ? `<div class="mb-6">${
|
|
1977
|
+
${data.error ? `<div class="mb-6">${chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
|
|
1978
|
+
${data.message ? `<div class="mb-6">${chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.message })}</div>` : ""}
|
|
1878
1979
|
|
|
1879
1980
|
<!-- Form Response (HTMX target) -->
|
|
1880
1981
|
<div id="form-response" class="mb-6"></div>
|
|
@@ -2002,7 +2103,7 @@ function renderRegisterPage(data) {
|
|
|
2002
2103
|
<meta charset="UTF-8">
|
|
2003
2104
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2004
2105
|
<title>Register - SonicJS AI</title>
|
|
2005
|
-
<link rel="icon" type="image/
|
|
2106
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
2006
2107
|
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
|
|
2007
2108
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
2008
2109
|
<script>
|
|
@@ -2025,42 +2126,20 @@ function renderRegisterPage(data) {
|
|
|
2025
2126
|
<div class="flex min-h-full flex-col justify-center py-12 sm:px-6 lg:px-8">
|
|
2026
2127
|
<!-- Logo Section -->
|
|
2027
2128
|
<div class="sm:mx-auto sm:w-full sm:max-w-md text-center">
|
|
2028
|
-
<div class="mx-auto w-
|
|
2029
|
-
<svg class="w-
|
|
2030
|
-
<path
|
|
2031
|
-
<path fill="#F1F2F2" d="M974.78,1398.211c-5.016,6.574-10.034,13.146-15.048,19.721c-1.828,2.398-3.657,4.796-5.487,7.194 c1.994,1.719,3.958,3.51,5.873,5.424c18.724,18.731,28.089,41.216,28.089,67.459c0,26.251-9.366,48.658-28.089,67.237 c-18.731,18.579-41.215,27.868-67.459,27.868c-9.848,0-19.156-1.308-27.923-3.923l-4.185,3.354 c-8.587,6.885-17.154,13.796-25.725,20.702c17.52,8.967,36.86,13.487,58.054,13.487c35.533,0,65.91-12.608,91.124-37.821 c25.214-25.215,37.821-55.584,37.821-91.125c0-35.534-12.607-65.911-37.821-91.126 C981.004,1403.663,977.926,1400.854,974.78,1398.211z"></path>
|
|
2032
|
-
<path fill="#F1F2F2" d="M1364.644,1439.619c-4.72,0-8.702,1.624-11.943,4.865c-3.249,3.249-4.866,7.23-4.866,11.944v138.014 l-167.651-211.003c-0.297-0.586-0.74-1.03-1.327-1.326c-4.721-4.714-10.249-7.742-16.588-9.069 c-6.346-1.326-12.608-0.732-18.801,1.77c-6.192,2.509-11.059,6.49-14.598,11.944c-3.539,5.46-5.308,11.577-5.308,18.357v208.348 c0,4.721,1.618,8.703,4.866,11.944c3.241,3.241,7.222,4.865,11.943,4.865c2.945,0,5.751-0.738,8.405-2.211 c2.654-1.472,4.713-3.463,6.193-5.971c1.473-2.503,2.212-5.378,2.212-8.627v-205.251l166.325,209.675 c2.06,2.654,4.423,4.865,7.078,6.635c5.308,3.829,11.349,5.75,18.137,5.75c5.308,0,10.464-1.182,15.482-3.538 c3.539-1.769,6.56-4.127,9.069-7.078c2.502-2.945,4.491-6.338,5.971-10.175c1.473-3.829,2.212-7.664,2.212-11.501v-141.552 c0-4.714-1.624-8.695-4.865-11.944C1373.339,1441.243,1369.359,1439.619,1364.644,1439.619z"></path>
|
|
2033
|
-
<path fill="#F1F2F2" d="M1508.406,1432.983c-2.654-1.472-5.46-2.212-8.404-2.212c-4.721,0-8.703,1.7-11.944,5.087 c-3.249,3.395-4.865,7.3-4.865,11.723v163.228c0,4.721,1.616,8.702,4.865,11.943c3.241,3.249,7.223,4.866,11.944,4.866 c2.944,0,5.751-0.732,8.404-2.212c2.655-1.472,4.714-3.539,6.193-6.194c1.473-2.654,2.213-5.453,2.213-8.404V1447.58 c0-2.945-0.74-5.75-2.213-8.405C1513.12,1436.522,1511.06,1434.462,1508.406,1432.983z"></path>
|
|
2034
|
-
<path fill="#F1F2F2" d="M1499.78,1367.957c-4.575,0-8.481,1.625-11.722,4.866c-3.249,3.249-4.865,7.23-4.865,11.943 c0,2.951,0.732,5.75,2.212,8.405c1.472,2.654,3.463,4.721,5.971,6.193c2.503,1.479,5.378,2.212,8.627,2.212 c4.423,0,8.328-1.618,11.721-4.865c3.387-3.243,5.088-7.224,5.088-11.944c0-4.713-1.701-8.694-5.088-11.943 C1508.33,1369.582,1504.349,1367.957,1499.78,1367.957z"></path>
|
|
2035
|
-
<path fill="#F1F2F2" d="M1859.627,1369.727H1747.27c-35.388,0-65.69,12.607-90.904,37.821 c-25.213,25.215-37.82,55.591-37.82,91.125c0,35.54,12.607,65.911,37.82,91.125c25.215,25.215,55.516,37.821,90.904,37.821h56.178 c4.714,0,8.695-1.618,11.944-4.866c3.241-3.241,4.865-7.222,4.865-11.943c0-4.714-1.624-8.695-4.865-11.943 c-3.249-3.243-7.23-4.866-11.944-4.866h-56.178c-26.251,0-48.659-9.359-67.237-28.09c-18.579-18.723-27.868-41.207-27.868-67.459 c0-26.243,9.29-48.659,27.868-67.237c18.579-18.579,40.987-27.868,67.237-27.868h112.357c4.714,0,8.696-1.693,11.944-5.087 c3.241-3.387,4.865-7.368,4.865-11.943c0-4.569-1.624-8.475-4.865-11.723C1868.322,1371.351,1864.341,1369.727,1859.627,1369.727z "></path>
|
|
2036
|
-
<path fill="#06b6d4" d="M2219.256,1371.054h-112.357c-4.423,0-8.336,1.624-11.723,4.865c-3.393,3.249-5.087,7.23-5.087,11.944 c0,4.721,1.694,8.702,5.087,11.943c3.387,3.249,7.3,4.866,11.723,4.866h95.547v95.105c0,26.251-9.365,48.659-28.088,67.237 c-18.731,18.579-41.215,27.868-67.459,27.868c-26.251,0-48.659-9.289-67.237-27.868c-18.579-18.579-27.868-40.987-27.868-67.237 c0-4.713-1.701-8.771-5.088-12.165c-3.393-3.387-7.374-5.087-11.943-5.087c-4.575,0-8.481,1.7-11.722,5.087 c-3.249,3.393-4.865,7.451-4.865,12.165c0,35.388,12.607,65.69,37.82,90.904c25.215,25.213,55.584,37.82,91.126,37.82 c35.532,0,65.91-12.607,91.125-37.82c25.214-25.215,37.82-55.516,37.82-90.904v-111.915c0-4.714-1.624-8.695-4.865-11.944 C2227.951,1372.678,2223.971,1371.054,2219.256,1371.054z"></path>
|
|
2037
|
-
<path fill="#06b6d4" d="M2574.24,1502.875c-14.306-14.156-31.483-21.234-51.533-21.234H2410.35 c-10.617,0-19.762-3.829-27.426-11.501c-7.672-7.664-11.501-16.954-11.501-27.868c0-10.907,3.829-20.196,11.501-27.868 c7.664-7.664,16.809-11.501,27.426-11.501h112.357c4.714,0,8.695-1.617,11.944-4.866c3.241-3.241,4.865-7.222,4.865-11.943 c0-4.714-1.624-8.695-4.865-11.944c-3.249-3.241-7.23-4.865-11.944-4.865H2410.35c-20.058,0-37.158,7.154-51.313,21.454 c-14.156,14.308-21.232,31.483-21.232,51.534c0,20.058,7.077,37.234,21.232,51.534c14.156,14.308,31.255,21.454,51.313,21.454 h112.357c7.078,0,13.637,1.77,19.684,5.308c6.042,3.539,10.838,8.336,14.377,14.377c3.538,6.047,5.307,12.607,5.307,19.685 c0,10.616-3.835,19.76-11.501,27.425c-7.672,7.673-16.961,11.502-27.868,11.502h-168.094c-4.721,0-8.703,1.7-11.944,5.087 c-3.249,3.393-4.865,7.374-4.865,11.943c0,4.576,1.616,8.481,4.865,11.723c3.241,3.249,7.223,4.866,11.944,4.866h168.094 c20.051,0,37.227-7.078,51.533-21.234c14.302-14.155,21.454-31.331,21.454-51.534 C2595.695,1534.213,2588.542,1517.03,2574.24,1502.875z"></path>
|
|
2038
|
-
<path fill="#06b6d4" d="M854.024,1585.195l20.001-16.028c16.616-13.507,33.04-27.265,50.086-40.251 c1.13-0.861,2.9-1.686,2.003-3.516c-0.843-1.716-2.481-2.302-4.484-2.123c-8.514,0.765-17.016-0.538-25.537-0.353 c-1.124,0.024-2.768,0.221-3.163-1.25c-0.371-1.369,1.088-2.063,1.919-2.894c6.26-6.242,12.574-12.43,18.816-18.691 c9.303-9.327,18.565-18.714,27.851-28.066c1.848-1.859,3.701-3.713,5.549-5.572c2.655-2.661,5.309-5.315,7.958-7.982 c0.574-0.579,1.259-1.141,1.246-1.94c-0.004-0.257-0.078-0.538-0.254-0.853c-0.556-0.981-1.441-1.1-2.469-0.957 c-0.658,0.096-1.315,0.185-1.973,0.275c-3.844,0.538-7.689,1.076-11.533,1.608c-3.641,0.505-7.281,1.02-10.922,1.529 c-4.162,0.582-8.324,1.158-12.486,1.748c-1.142,0.161-2.409,1.662-3.354,0.508c-0.419-0.508-0.431-1.028-0.251-1.531 c0.269-0.741,0.957-1.441,1.387-2.021c3.414-4.58,6.882-9.124,10.356-13.662c1.74-2.272,3.48-4.544,5.214-6.822 c4.682-6.141,9.369-12.281,14.051-18.422c0.09-0.119,0.181-0.237,0.271-0.355c6.848-8.98,13.7-17.958,20.553-26.936 c0.488-0.64,0.977-1.28,1.465-1.92c2.159-2.828,4.315-5.658,6.476-8.486c4.197-5.501,8.454-10.954,12.67-16.442 c0.263-0.347,0.538-0.718,0.717-1.106c0.269-0.586,0.299-1.196-0.335-1.776c-0.825-0.753-1.8-0.15-2.595,0.419 c-0.67,0.472-1.333,0.957-1.955,1.489c-2.206,1.889-4.401,3.797-6.595,5.698c-3.958,3.438-7.922,6.876-11.976,10.194 c-2.443,2.003-4.865,4.028-7.301,6.038c-18.689-10.581-39.53-15.906-62.549-15.906c-35.54,0-65.911,12.607-91.125,37.82 c-25.214,25.215-37.821,55.592-37.821,91.126c0,35.54,12.607,65.91,37.821,91.125c4.146,4.146,8.445,7.916,12.87,11.381 c-9.015,11.14-18.036,22.277-27.034,33.429c-1.208,1.489-3.755,3.151-2.745,4.891c0.078,0.144,0.173,0.281,0.305,0.425 c1.321,1.429,3.492-1.303,4.933-2.457c6.673-5.333,13.333-10.685,19.982-16.042c3.707-2.984,7.417-5.965,11.124-8.952 c1.474-1.188,2.951-2.373,4.425-3.561c6.41-5.164,12.816-10.333,19.238-15.481L854.024,1585.195z M797.552,1498.009 c0-26.243,9.29-48.728,27.868-67.459c18.579-18.723,40.987-28.089,67.238-28.089c12.273,0,23.712,2.075,34.34,6.171 c-3.37,2.905-6.734,5.816-10.069,8.762c-6.075,5.351-12.365,10.469-18.667,15.564c-4.179,3.378-8.371,6.744-12.514,10.164 c-7.54,6.23-15.037,12.52-22.529,18.804c-7.091,5.955-14.182,11.904-21.19,17.949c-1.136,0.974-3.055,1.907-2.135,3.94 c0.831,1.836,2.774,1.417,4.341,1.578l12.145-0.599l14.151-0.698c1.031-0.102,2.192-0.257,2.89,0.632 c0.034,0.044,0.073,0.078,0.106,0.127c1.017,1.561-0.67,2.105-1.387,2.942c-6.308,7.318-12.616,14.637-18.978,21.907 c-8.161,9.339-16.353,18.649-24.544,27.958c-2.146,2.433-4.275,4.879-6.422,7.312c-1.034,1.172-2.129,2.272-1.238,3.922 c0.933,1.728,2.685,1.752,4.323,1.602c4.134-0.367,8.263-0.489,12.396-0.492c0.242,0,0.485-0.005,0.728-0.004 c2.711,0.009,5.422,0.068,8.134,0.145c2.582,0.074,5.166,0.165,7.752,0.249c0.275,1.62-0.879,2.356-1.62,3.259 c-1.333,1.626-2.667,3.247-4,4.867c-4.315,5.252-8.62,10.514-12.928,15.772c-3.562-2.725-7.007-5.733-10.324-9.051 C806.842,1546.667,797.552,1524.26,797.552,1498.009z"></path>
|
|
2129
|
+
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-lg bg-white">
|
|
2130
|
+
<svg class="h-7 w-7 text-zinc-950" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2131
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
|
2039
2132
|
</svg>
|
|
2040
2133
|
</div>
|
|
2041
|
-
<
|
|
2042
|
-
|
|
2134
|
+
<h1 class="mt-6 text-3xl font-semibold tracking-tight text-white">SonicJS AI</h1>
|
|
2135
|
+
<p class="mt-2 text-sm text-zinc-400">Create your account and get started</p>
|
|
2043
2136
|
</div>
|
|
2044
2137
|
|
|
2045
2138
|
<!-- Form Container -->
|
|
2046
2139
|
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
|
2047
2140
|
<div class="bg-zinc-900 shadow-sm ring-1 ring-white/10 rounded-xl px-6 py-8 sm:px-10">
|
|
2048
|
-
<!-- Setup Banner -->
|
|
2049
|
-
${data.isSetup ? `
|
|
2050
|
-
<div class="mb-6 rounded-lg bg-blue-500/10 p-4 ring-1 ring-blue-500/20">
|
|
2051
|
-
<div class="flex items-start gap-x-3">
|
|
2052
|
-
<svg class="h-5 w-5 text-blue-400 shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
2053
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
2054
|
-
</svg>
|
|
2055
|
-
<div class="flex-1">
|
|
2056
|
-
<p class="text-sm font-medium text-blue-300">First-time Setup</p>
|
|
2057
|
-
<p class="text-sm text-blue-400/80 mt-1">This account will be the administrator with full access to manage your SonicJS installation.</p>
|
|
2058
|
-
</div>
|
|
2059
|
-
</div>
|
|
2060
|
-
</div>
|
|
2061
|
-
` : ""}
|
|
2062
2141
|
<!-- Alerts -->
|
|
2063
|
-
${data.error ? `<div class="mb-6">${
|
|
2142
|
+
${data.error ? `<div class="mb-6">${chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error })}</div>` : ""}
|
|
2064
2143
|
|
|
2065
2144
|
<!-- Form -->
|
|
2066
2145
|
<form
|
|
@@ -2173,7 +2252,6 @@ function renderRegisterPage(data) {
|
|
|
2173
2252
|
</html>
|
|
2174
2253
|
`;
|
|
2175
2254
|
}
|
|
2176
|
-
var adminExistsCache = null;
|
|
2177
2255
|
async function isRegistrationEnabled(db) {
|
|
2178
2256
|
try {
|
|
2179
2257
|
const plugin = await db.prepare("SELECT settings FROM plugins WHERE id = ?").bind("core-auth").first();
|
|
@@ -2195,21 +2273,6 @@ async function isFirstUserRegistration(db) {
|
|
|
2195
2273
|
return false;
|
|
2196
2274
|
}
|
|
2197
2275
|
}
|
|
2198
|
-
async function checkAdminUserExists(db) {
|
|
2199
|
-
if (adminExistsCache !== null) {
|
|
2200
|
-
return adminExistsCache;
|
|
2201
|
-
}
|
|
2202
|
-
try {
|
|
2203
|
-
const result = await db.prepare("SELECT id FROM users WHERE role = ?").bind("admin").first();
|
|
2204
|
-
adminExistsCache = !!result;
|
|
2205
|
-
return adminExistsCache;
|
|
2206
|
-
} catch {
|
|
2207
|
-
return false;
|
|
2208
|
-
}
|
|
2209
|
-
}
|
|
2210
|
-
function setAdminExists() {
|
|
2211
|
-
adminExistsCache = true;
|
|
2212
|
-
}
|
|
2213
2276
|
var baseRegistrationSchema = zod.z.object({
|
|
2214
2277
|
email: zod.z.string().email("Valid email is required"),
|
|
2215
2278
|
password: zod.z.string().min(8, "Password must be at least 8 characters"),
|
|
@@ -2271,11 +2334,8 @@ authRoutes.get("/register", async (c) => {
|
|
|
2271
2334
|
}
|
|
2272
2335
|
}
|
|
2273
2336
|
const error = c.req.query("error");
|
|
2274
|
-
const isSetup = c.req.query("setup") === "true";
|
|
2275
2337
|
const pageData = {
|
|
2276
|
-
error: error || void 0
|
|
2277
|
-
isSetup: isSetup && isFirstUser
|
|
2278
|
-
// Only show setup message if truly first user
|
|
2338
|
+
error: error || void 0
|
|
2279
2339
|
};
|
|
2280
2340
|
return c.html(renderRegisterPage(pageData));
|
|
2281
2341
|
});
|
|
@@ -2321,7 +2381,7 @@ authRoutes.post(
|
|
|
2321
2381
|
if (existingUser) {
|
|
2322
2382
|
return c.json({ error: "User with this email or username already exists" }, 400);
|
|
2323
2383
|
}
|
|
2324
|
-
const passwordHash = await
|
|
2384
|
+
const passwordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(password);
|
|
2325
2385
|
const userId = crypto.randomUUID();
|
|
2326
2386
|
const now = /* @__PURE__ */ new Date();
|
|
2327
2387
|
await db.prepare(`
|
|
@@ -2341,7 +2401,7 @@ authRoutes.post(
|
|
|
2341
2401
|
now.getTime(),
|
|
2342
2402
|
now.getTime()
|
|
2343
2403
|
).run();
|
|
2344
|
-
const token = await
|
|
2404
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(userId, normalizedEmail, "viewer");
|
|
2345
2405
|
cookie.setCookie(c, "auth_token", token, {
|
|
2346
2406
|
httpOnly: true,
|
|
2347
2407
|
secure: true,
|
|
@@ -2394,11 +2454,11 @@ authRoutes.post("/login", async (c) => {
|
|
|
2394
2454
|
if (!user) {
|
|
2395
2455
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2396
2456
|
}
|
|
2397
|
-
const isValidPassword = await
|
|
2457
|
+
const isValidPassword = await chunkT3YIKW2A_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2398
2458
|
if (!isValidPassword) {
|
|
2399
2459
|
return c.json({ error: "Invalid email or password" }, 401);
|
|
2400
2460
|
}
|
|
2401
|
-
const token = await
|
|
2461
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2402
2462
|
cookie.setCookie(c, "auth_token", token, {
|
|
2403
2463
|
httpOnly: true,
|
|
2404
2464
|
secure: true,
|
|
@@ -2447,7 +2507,7 @@ authRoutes.get("/logout", (c) => {
|
|
|
2447
2507
|
});
|
|
2448
2508
|
return c.redirect("/auth/login?message=You have been logged out successfully");
|
|
2449
2509
|
});
|
|
2450
|
-
authRoutes.get("/me",
|
|
2510
|
+
authRoutes.get("/me", chunkT3YIKW2A_cjs.requireAuth(), async (c) => {
|
|
2451
2511
|
try {
|
|
2452
2512
|
const user = c.get("user");
|
|
2453
2513
|
if (!user) {
|
|
@@ -2464,13 +2524,13 @@ authRoutes.get("/me", chunkYYV3XQOQ_cjs.requireAuth(), async (c) => {
|
|
|
2464
2524
|
return c.json({ error: "Failed to get user" }, 500);
|
|
2465
2525
|
}
|
|
2466
2526
|
});
|
|
2467
|
-
authRoutes.post("/refresh",
|
|
2527
|
+
authRoutes.post("/refresh", chunkT3YIKW2A_cjs.requireAuth(), async (c) => {
|
|
2468
2528
|
try {
|
|
2469
2529
|
const user = c.get("user");
|
|
2470
2530
|
if (!user) {
|
|
2471
2531
|
return c.json({ error: "Not authenticated" }, 401);
|
|
2472
2532
|
}
|
|
2473
|
-
const token = await
|
|
2533
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(user.userId, user.email, user.role);
|
|
2474
2534
|
cookie.setCookie(c, "auth_token", token, {
|
|
2475
2535
|
httpOnly: true,
|
|
2476
2536
|
secure: true,
|
|
@@ -2530,7 +2590,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2530
2590
|
</div>
|
|
2531
2591
|
`);
|
|
2532
2592
|
}
|
|
2533
|
-
const passwordHash = await
|
|
2593
|
+
const passwordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(password);
|
|
2534
2594
|
const role = isFirstUser ? "admin" : "viewer";
|
|
2535
2595
|
const userId = crypto.randomUUID();
|
|
2536
2596
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2550,10 +2610,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2550
2610
|
now.getTime(),
|
|
2551
2611
|
now.getTime()
|
|
2552
2612
|
).run();
|
|
2553
|
-
|
|
2554
|
-
setAdminExists();
|
|
2555
|
-
}
|
|
2556
|
-
const token = await chunkYYV3XQOQ_cjs.AuthManager.generateToken(userId, normalizedEmail, role);
|
|
2613
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(userId, normalizedEmail, role);
|
|
2557
2614
|
cookie.setCookie(c, "auth_token", token, {
|
|
2558
2615
|
httpOnly: true,
|
|
2559
2616
|
secure: false,
|
|
@@ -2605,7 +2662,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2605
2662
|
</div>
|
|
2606
2663
|
`);
|
|
2607
2664
|
}
|
|
2608
|
-
const isValidPassword = await
|
|
2665
|
+
const isValidPassword = await chunkT3YIKW2A_cjs.AuthManager.verifyPassword(password, user.password_hash);
|
|
2609
2666
|
if (!isValidPassword) {
|
|
2610
2667
|
return c.html(html.html`
|
|
2611
2668
|
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
@@ -2613,7 +2670,7 @@ authRoutes.post("/login/form", async (c) => {
|
|
|
2613
2670
|
</div>
|
|
2614
2671
|
`);
|
|
2615
2672
|
}
|
|
2616
|
-
const token = await
|
|
2673
|
+
const token = await chunkT3YIKW2A_cjs.AuthManager.generateToken(user.id, user.email, user.role);
|
|
2617
2674
|
cookie.setCookie(c, "auth_token", token, {
|
|
2618
2675
|
httpOnly: true,
|
|
2619
2676
|
secure: false,
|
|
@@ -2672,9 +2729,8 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2672
2729
|
`).run();
|
|
2673
2730
|
const existingAdmin = await db.prepare("SELECT id FROM users WHERE email = ? OR username = ?").bind("admin@sonicjs.com", "admin").first();
|
|
2674
2731
|
if (existingAdmin) {
|
|
2675
|
-
const passwordHash2 = await
|
|
2732
|
+
const passwordHash2 = await chunkT3YIKW2A_cjs.AuthManager.hashPassword("sonicjs!");
|
|
2676
2733
|
await db.prepare("UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?").bind(passwordHash2, Date.now(), existingAdmin.id).run();
|
|
2677
|
-
setAdminExists();
|
|
2678
2734
|
return c.json({
|
|
2679
2735
|
message: "Admin user already exists (password updated)",
|
|
2680
2736
|
user: {
|
|
@@ -2685,7 +2741,7 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2685
2741
|
}
|
|
2686
2742
|
});
|
|
2687
2743
|
}
|
|
2688
|
-
const passwordHash = await
|
|
2744
|
+
const passwordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword("sonicjs!");
|
|
2689
2745
|
const userId = "admin-user-id";
|
|
2690
2746
|
const now = Date.now();
|
|
2691
2747
|
const adminEmail = "admin@sonicjs.com".toLowerCase();
|
|
@@ -2705,7 +2761,6 @@ authRoutes.post("/seed-admin", async (c) => {
|
|
|
2705
2761
|
now,
|
|
2706
2762
|
now
|
|
2707
2763
|
).run();
|
|
2708
|
-
setAdminExists();
|
|
2709
2764
|
return c.json({
|
|
2710
2765
|
message: "Admin user created successfully",
|
|
2711
2766
|
user: {
|
|
@@ -2906,7 +2961,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2906
2961
|
if (existingUsername) {
|
|
2907
2962
|
return c.json({ error: "Username is already taken" }, 400);
|
|
2908
2963
|
}
|
|
2909
|
-
const passwordHash = await
|
|
2964
|
+
const passwordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(password);
|
|
2910
2965
|
const updateStmt = db.prepare(`
|
|
2911
2966
|
UPDATE users SET
|
|
2912
2967
|
username = ?,
|
|
@@ -2925,7 +2980,7 @@ authRoutes.post("/accept-invitation", async (c) => {
|
|
|
2925
2980
|
Date.now(),
|
|
2926
2981
|
invitedUser.id
|
|
2927
2982
|
).run();
|
|
2928
|
-
const authToken = await
|
|
2983
|
+
const authToken = await chunkT3YIKW2A_cjs.AuthManager.generateToken(invitedUser.id, invitedUser.email, invitedUser.role);
|
|
2929
2984
|
cookie.setCookie(c, "auth_token", authToken, {
|
|
2930
2985
|
httpOnly: true,
|
|
2931
2986
|
secure: true,
|
|
@@ -3155,7 +3210,7 @@ authRoutes.post("/reset-password", async (c) => {
|
|
|
3155
3210
|
if (Date.now() > user.password_reset_expires) {
|
|
3156
3211
|
return c.json({ error: "Reset token has expired" }, 400);
|
|
3157
3212
|
}
|
|
3158
|
-
const newPasswordHash = await
|
|
3213
|
+
const newPasswordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(password);
|
|
3159
3214
|
try {
|
|
3160
3215
|
const historyStmt = db.prepare(`
|
|
3161
3216
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
@@ -3413,9 +3468,166 @@ app.post("/test-cleanup/content", async (c) => {
|
|
|
3413
3468
|
var test_cleanup_default = app;
|
|
3414
3469
|
|
|
3415
3470
|
// src/templates/pages/admin-content-form.template.ts
|
|
3416
|
-
|
|
3471
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
3472
|
+
|
|
3473
|
+
// src/templates/components/drag-sortable.template.ts
|
|
3474
|
+
function getDragSortableScript() {
|
|
3475
|
+
return `
|
|
3476
|
+
<script>
|
|
3477
|
+
if (!window.__sonicDragSortableInit) {
|
|
3478
|
+
window.__sonicDragSortableInit = true;
|
|
3479
|
+
|
|
3480
|
+
window.initializeDragSortable = function(container, options) {
|
|
3481
|
+
if (!container || container.dataset.dragSortableInit === 'true') {
|
|
3482
|
+
return;
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3485
|
+
container.dataset.dragSortableInit = 'true';
|
|
3486
|
+
const itemSelector = options && options.itemSelector ? options.itemSelector : '.sortable-item';
|
|
3487
|
+
const handleSelector = options && options.handleSelector ? options.handleSelector : '[data-action="drag-handle"]';
|
|
3488
|
+
const onUpdate = options && typeof options.onUpdate === 'function' ? options.onUpdate : function() {};
|
|
3489
|
+
let activeDragItem = null;
|
|
3490
|
+
|
|
3491
|
+
const getDragAfterElement = function(list, y) {
|
|
3492
|
+
const items = Array.from(list.querySelectorAll(itemSelector + ':not(.is-dragging)'));
|
|
3493
|
+
let closest = { offset: Number.NEGATIVE_INFINITY, element: null };
|
|
3494
|
+
items.forEach(function(item) {
|
|
3495
|
+
const box = item.getBoundingClientRect();
|
|
3496
|
+
const offset = y - box.top - box.height / 2;
|
|
3497
|
+
if (offset < 0 && offset > closest.offset) {
|
|
3498
|
+
closest = { offset: offset, element: item };
|
|
3499
|
+
}
|
|
3500
|
+
});
|
|
3501
|
+
return closest.element;
|
|
3502
|
+
};
|
|
3503
|
+
|
|
3504
|
+
const activateDragItem = function(event) {
|
|
3505
|
+
const target = event.target;
|
|
3506
|
+
if (!(target instanceof Element)) return;
|
|
3507
|
+
const handle = target.closest(handleSelector);
|
|
3508
|
+
if (!handle) return;
|
|
3509
|
+
const item = handle.closest(itemSelector);
|
|
3510
|
+
if (!item) return;
|
|
3511
|
+
activeDragItem = item;
|
|
3512
|
+
};
|
|
3513
|
+
|
|
3514
|
+
const clearActiveDragItem = function() {
|
|
3515
|
+
activeDragItem = null;
|
|
3516
|
+
};
|
|
3517
|
+
|
|
3518
|
+
container.addEventListener('pointerdown', activateDragItem);
|
|
3519
|
+
container.addEventListener('mousedown', activateDragItem);
|
|
3520
|
+
container.addEventListener('pointerup', clearActiveDragItem);
|
|
3521
|
+
container.addEventListener('mouseup', clearActiveDragItem);
|
|
3522
|
+
|
|
3523
|
+
container.addEventListener('dragstart', function(event) {
|
|
3524
|
+
const target = event.target;
|
|
3525
|
+
if (!(target instanceof Element)) return;
|
|
3526
|
+
const item = target.closest(itemSelector);
|
|
3527
|
+
if (!item || item !== activeDragItem) {
|
|
3528
|
+
event.preventDefault();
|
|
3529
|
+
return;
|
|
3530
|
+
}
|
|
3531
|
+
item.classList.add('is-dragging');
|
|
3532
|
+
if (event.dataTransfer) {
|
|
3533
|
+
event.dataTransfer.setData('text/plain', '');
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
|
|
3537
|
+
container.addEventListener('dragend', function(event) {
|
|
3538
|
+
const target = event.target;
|
|
3539
|
+
if (target instanceof Element) {
|
|
3540
|
+
const item = target.closest(itemSelector);
|
|
3541
|
+
if (item) {
|
|
3542
|
+
item.classList.remove('is-dragging');
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
activeDragItem = null;
|
|
3546
|
+
onUpdate();
|
|
3547
|
+
});
|
|
3548
|
+
|
|
3549
|
+
container.addEventListener('dragover', function(event) {
|
|
3550
|
+
event.preventDefault();
|
|
3551
|
+
const dragging = container.querySelector(itemSelector + '.is-dragging');
|
|
3552
|
+
if (!dragging) return;
|
|
3553
|
+
const afterElement = getDragAfterElement(container, event.clientY);
|
|
3554
|
+
if (afterElement === null) {
|
|
3555
|
+
container.appendChild(dragging);
|
|
3556
|
+
} else {
|
|
3557
|
+
container.insertBefore(dragging, afterElement);
|
|
3558
|
+
}
|
|
3559
|
+
});
|
|
3560
|
+
|
|
3561
|
+
container.addEventListener('drop', function() {
|
|
3562
|
+
onUpdate();
|
|
3563
|
+
});
|
|
3564
|
+
};
|
|
3565
|
+
}
|
|
3566
|
+
</script>
|
|
3567
|
+
`;
|
|
3568
|
+
}
|
|
3417
3569
|
|
|
3418
3570
|
// src/templates/components/dynamic-field.template.ts
|
|
3571
|
+
function getReadFieldValueScript() {
|
|
3572
|
+
return `
|
|
3573
|
+
<script>
|
|
3574
|
+
if (!window.__sonicReadFieldValueInit) {
|
|
3575
|
+
window.__sonicReadFieldValueInit = true;
|
|
3576
|
+
|
|
3577
|
+
window.sonicReadFieldValue = function(fieldWrapper) {
|
|
3578
|
+
const fieldType = fieldWrapper.dataset.fieldType;
|
|
3579
|
+
const select = fieldWrapper.querySelector('select');
|
|
3580
|
+
const textarea = fieldWrapper.querySelector('textarea');
|
|
3581
|
+
const inputs = Array.from(fieldWrapper.querySelectorAll('input'));
|
|
3582
|
+
const checkbox = inputs.find((input) => input.type === 'checkbox');
|
|
3583
|
+
const nonHiddenInput = inputs.find((input) => input.type !== 'hidden' && input.type !== 'checkbox');
|
|
3584
|
+
const hiddenInput = inputs.find((input) => input.type === 'hidden');
|
|
3585
|
+
|
|
3586
|
+
if (fieldType === 'object' || fieldType === 'array') {
|
|
3587
|
+
if (!hiddenInput) {
|
|
3588
|
+
return fieldType === 'array' ? [] : {};
|
|
3589
|
+
}
|
|
3590
|
+
const rawValue = hiddenInput.value || '';
|
|
3591
|
+
if (!rawValue.trim()) {
|
|
3592
|
+
return fieldType === 'array' ? [] : {};
|
|
3593
|
+
}
|
|
3594
|
+
try {
|
|
3595
|
+
return JSON.parse(rawValue);
|
|
3596
|
+
} catch {
|
|
3597
|
+
return fieldType === 'array' ? [] : {};
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
if (fieldType === 'boolean' && checkbox) {
|
|
3602
|
+
return checkbox.checked;
|
|
3603
|
+
}
|
|
3604
|
+
|
|
3605
|
+
if (select) {
|
|
3606
|
+
if (select.multiple) {
|
|
3607
|
+
return Array.from(select.selectedOptions).map((option) => option.value);
|
|
3608
|
+
}
|
|
3609
|
+
return select.value;
|
|
3610
|
+
}
|
|
3611
|
+
|
|
3612
|
+
if (fieldType === 'quill' || fieldType === 'media') {
|
|
3613
|
+
return hiddenInput ? hiddenInput.value : '';
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
const textSource = textarea || nonHiddenInput || hiddenInput;
|
|
3617
|
+
if (!textSource) {
|
|
3618
|
+
return '';
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
if (fieldType === 'number') {
|
|
3622
|
+
return textSource.value === '' ? null : Number(textSource.value);
|
|
3623
|
+
}
|
|
3624
|
+
|
|
3625
|
+
return textSource.value;
|
|
3626
|
+
};
|
|
3627
|
+
}
|
|
3628
|
+
</script>
|
|
3629
|
+
`;
|
|
3630
|
+
}
|
|
3419
3631
|
function renderDynamicField(field, options = {}) {
|
|
3420
3632
|
const { value = "", errors = [], disabled = false, className = "", pluginStatuses = {}, collectionId = "", contentId = "" } = options;
|
|
3421
3633
|
const opts = field.field_options || {};
|
|
@@ -3871,43 +4083,124 @@ function renderDynamicField(field, options = {}) {
|
|
|
3871
4083
|
` : ""}
|
|
3872
4084
|
`;
|
|
3873
4085
|
break;
|
|
4086
|
+
case "reference":
|
|
4087
|
+
let referenceCollections = [];
|
|
4088
|
+
if (Array.isArray(opts.collection)) {
|
|
4089
|
+
referenceCollections = opts.collection.filter(Boolean);
|
|
4090
|
+
} else if (typeof opts.collection === "string" && opts.collection) {
|
|
4091
|
+
referenceCollections = [opts.collection];
|
|
4092
|
+
}
|
|
4093
|
+
const referenceCollectionsAttr = referenceCollections.join(",");
|
|
4094
|
+
const hasReferenceCollection = referenceCollections.length > 0;
|
|
4095
|
+
const hasReferenceValue = Boolean(value);
|
|
4096
|
+
fieldHTML = `
|
|
4097
|
+
<div class="reference-field-container space-y-3" data-reference-field data-field-name="${escapeHtml2(fieldName)}" data-reference-collection="${escapeHtml2(referenceCollections[0] || "")}" data-reference-collections="${escapeHtml2(referenceCollectionsAttr)}">
|
|
4098
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml2(value)}">
|
|
4099
|
+
<div class="rounded-lg border border-zinc-200 bg-white/60 px-3 py-2 text-sm text-zinc-600 dark:border-white/10 dark:bg-white/5 dark:text-zinc-300" data-reference-display>
|
|
4100
|
+
${hasReferenceCollection ? hasReferenceValue ? "Loading selection..." : "No reference selected." : "Reference collection not configured."}
|
|
4101
|
+
</div>
|
|
4102
|
+
<div class="flex flex-wrap gap-2">
|
|
4103
|
+
<button
|
|
4104
|
+
type="button"
|
|
4105
|
+
onclick="openReferenceSelector('${fieldId}')"
|
|
4106
|
+
class="inline-flex items-center justify-center rounded-lg bg-zinc-900 px-3 py-2 text-sm font-semibold text-white hover:bg-zinc-800 dark:bg-white/10 dark:hover:bg-white/20"
|
|
4107
|
+
${hasReferenceCollection ? "" : "disabled"}
|
|
4108
|
+
>
|
|
4109
|
+
Select reference
|
|
4110
|
+
</button>
|
|
4111
|
+
<button
|
|
4112
|
+
type="button"
|
|
4113
|
+
onclick="clearReferenceField('${fieldId}')"
|
|
4114
|
+
class="inline-flex items-center justify-center rounded-lg border border-zinc-200 px-3 py-2 text-sm font-semibold text-zinc-700 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-200 dark:hover:bg-white/10"
|
|
4115
|
+
data-reference-clear
|
|
4116
|
+
${hasReferenceValue ? "" : "disabled"}
|
|
4117
|
+
>
|
|
4118
|
+
Remove
|
|
4119
|
+
</button>
|
|
4120
|
+
</div>
|
|
4121
|
+
</div>
|
|
4122
|
+
`;
|
|
4123
|
+
break;
|
|
3874
4124
|
case "media":
|
|
4125
|
+
const isMultiple = opts.multiple === true;
|
|
4126
|
+
const mediaValues = isMultiple && value ? Array.isArray(value) ? value : String(value).split(",").filter(Boolean) : [];
|
|
4127
|
+
const singleValue = !isMultiple ? value : "";
|
|
4128
|
+
const isVideoUrl = (url) => {
|
|
4129
|
+
const videoExtensions = [".mp4", ".webm", ".ogg", ".mov", ".avi"];
|
|
4130
|
+
return videoExtensions.some((ext) => url.toLowerCase().endsWith(ext));
|
|
4131
|
+
};
|
|
4132
|
+
const renderMediaPreview = (url, alt, classes) => {
|
|
4133
|
+
if (isVideoUrl(url)) {
|
|
4134
|
+
return `<video src="${url}" class="${classes}" muted></video>`;
|
|
4135
|
+
}
|
|
4136
|
+
return `<img src="${url}" alt="${alt}" class="${classes}">`;
|
|
4137
|
+
};
|
|
3875
4138
|
fieldHTML = `
|
|
3876
4139
|
<div class="media-field-container">
|
|
3877
|
-
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
4140
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${isMultiple ? mediaValues.join(",") : singleValue}" data-multiple="${isMultiple}">
|
|
4141
|
+
|
|
4142
|
+
${isMultiple ? `
|
|
4143
|
+
<div class="media-preview-grid grid grid-cols-4 gap-2 mb-2 ${mediaValues.length === 0 ? "hidden" : ""}" id="${fieldId}-preview">
|
|
4144
|
+
${mediaValues.map((url, idx) => `
|
|
4145
|
+
<div class="relative media-preview-item" data-url="${url}">
|
|
4146
|
+
${renderMediaPreview(url, `Media ${idx + 1}`, "w-full h-24 object-cover rounded-lg border border-white/20")}
|
|
4147
|
+
<button
|
|
4148
|
+
type="button"
|
|
4149
|
+
onclick="removeMediaFromMultiple('${fieldId}', '${url}')"
|
|
4150
|
+
class="absolute top-1 right-1 bg-red-600 text-white rounded-full p-1 hover:bg-red-700"
|
|
4151
|
+
${disabled ? "disabled" : ""}
|
|
4152
|
+
>
|
|
4153
|
+
<svg class="w-3 h-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
4154
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
4155
|
+
</svg>
|
|
4156
|
+
</button>
|
|
4157
|
+
</div>
|
|
4158
|
+
`).join("")}
|
|
4159
|
+
</div>
|
|
4160
|
+
` : `
|
|
4161
|
+
<div class="media-preview ${singleValue ? "" : "hidden"}" id="${fieldId}-preview">
|
|
4162
|
+
${singleValue ? renderMediaPreview(singleValue, "Selected media", "w-32 h-32 object-cover rounded-lg border border-white/20") : ""}
|
|
4163
|
+
</div>
|
|
4164
|
+
`}
|
|
4165
|
+
|
|
3881
4166
|
<div class="media-actions mt-2 space-x-2">
|
|
3882
4167
|
<button
|
|
3883
4168
|
type="button"
|
|
3884
|
-
onclick="openMediaSelector('${fieldId}')"
|
|
4169
|
+
onclick="openMediaSelector('${fieldId}', ${isMultiple})"
|
|
3885
4170
|
class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-xl hover:bg-blue-700 transition-all"
|
|
3886
4171
|
${disabled ? "disabled" : ""}
|
|
3887
4172
|
>
|
|
3888
4173
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
3889
4174
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path>
|
|
3890
4175
|
</svg>
|
|
3891
|
-
Select Media
|
|
4176
|
+
${isMultiple ? "Select Media (Multiple)" : "Select Media"}
|
|
3892
4177
|
</button>
|
|
3893
|
-
${
|
|
4178
|
+
${(isMultiple ? mediaValues.length > 0 : singleValue) ? `
|
|
3894
4179
|
<button
|
|
3895
4180
|
type="button"
|
|
3896
4181
|
onclick="clearMediaField('${fieldId}')"
|
|
3897
4182
|
class="inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-xl hover:bg-red-700 transition-all"
|
|
3898
4183
|
${disabled ? "disabled" : ""}
|
|
3899
4184
|
>
|
|
3900
|
-
Remove
|
|
4185
|
+
${isMultiple ? "Clear All" : "Remove"}
|
|
3901
4186
|
</button>
|
|
3902
4187
|
` : ""}
|
|
3903
4188
|
</div>
|
|
3904
4189
|
</div>
|
|
3905
4190
|
`;
|
|
3906
4191
|
break;
|
|
4192
|
+
case "object":
|
|
4193
|
+
return renderStructuredObjectField(field, options2);
|
|
4194
|
+
case "array":
|
|
4195
|
+
const itemsConfig = opts.items && typeof opts.items === "object" ? opts.items : {};
|
|
4196
|
+
if (itemsConfig.blocks && typeof itemsConfig.blocks === "object") {
|
|
4197
|
+
return renderBlocksField(field, options2, baseClasses, errorClasses);
|
|
4198
|
+
}
|
|
4199
|
+
return renderStructuredArrayField(field, options2);
|
|
3907
4200
|
default:
|
|
3908
4201
|
fieldHTML = `
|
|
3909
|
-
<input
|
|
3910
|
-
type="text"
|
|
4202
|
+
<input
|
|
4203
|
+
type="text"
|
|
3911
4204
|
id="${fieldId}"
|
|
3912
4205
|
name="${fieldName}"
|
|
3913
4206
|
value="${escapeHtml2(value)}"
|
|
@@ -3957,77 +4250,825 @@ function renderFieldGroup(title, fields, collapsible = false) {
|
|
|
3957
4250
|
</div>
|
|
3958
4251
|
`;
|
|
3959
4252
|
}
|
|
3960
|
-
function
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
4253
|
+
function renderBlocksField(field, options, baseClasses, errorClasses) {
|
|
4254
|
+
const { value = [], pluginStatuses = {} } = options;
|
|
4255
|
+
const opts = field.field_options || {};
|
|
4256
|
+
const itemsConfig = opts.items && typeof opts.items === "object" ? opts.items : {};
|
|
4257
|
+
const blocks = normalizeBlockDefinitions(itemsConfig.blocks);
|
|
4258
|
+
const discriminator = typeof itemsConfig.discriminator === "string" && itemsConfig.discriminator ? itemsConfig.discriminator : "blockType";
|
|
4259
|
+
const blockValues = normalizeBlocksValue(value, discriminator);
|
|
4260
|
+
const fieldId = `field-${field.field_name}`;
|
|
4261
|
+
const fieldName = field.field_name;
|
|
4262
|
+
const emptyState = blockValues.length === 0 ? `
|
|
4263
|
+
<div class="rounded-lg border border-dashed border-zinc-200 dark:border-white/10 px-4 py-6 text-center text-sm text-zinc-500 dark:text-zinc-400" data-blocks-empty>
|
|
4264
|
+
No blocks yet. Add your first block to get started.
|
|
4265
|
+
</div>
|
|
4266
|
+
` : "";
|
|
4267
|
+
const blockOptions = blocks.map((block) => `<option value="${escapeHtml2(block.name)}">${escapeHtml2(block.label)}</option>`).join("");
|
|
4268
|
+
const blockItems = blockValues.map(
|
|
4269
|
+
(blockValue, index) => renderBlockItem(field, blockValue, blocks, discriminator, index, pluginStatuses)
|
|
4270
|
+
).join("");
|
|
4271
|
+
const templates = blocks.map((block) => renderBlockTemplate(field, block, discriminator, pluginStatuses)).join("");
|
|
4272
|
+
return `
|
|
4273
|
+
<div
|
|
4274
|
+
class="blocks-field space-y-4"
|
|
4275
|
+
data-blocks='${escapeHtml2(JSON.stringify(blocks))}'
|
|
4276
|
+
data-blocks-discriminator="${escapeHtml2(discriminator)}"
|
|
4277
|
+
data-field-name="${escapeHtml2(fieldName)}"
|
|
4278
|
+
>
|
|
4279
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml2(JSON.stringify(blockValues))}">
|
|
3970
4280
|
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
4281
|
+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
4282
|
+
<div class="flex-1">
|
|
4283
|
+
<select
|
|
4284
|
+
class="${baseClasses} ${errorClasses}"
|
|
4285
|
+
data-role="block-type-select"
|
|
4286
|
+
>
|
|
4287
|
+
<option value="">Choose a block...</option>
|
|
4288
|
+
${blockOptions}
|
|
4289
|
+
</select>
|
|
4290
|
+
</div>
|
|
4291
|
+
<button
|
|
4292
|
+
type="button"
|
|
4293
|
+
data-action="add-block"
|
|
4294
|
+
class="inline-flex items-center justify-center rounded-lg bg-zinc-900 px-4 py-2 text-sm font-semibold text-white hover:bg-zinc-800 dark:bg-white/10 dark:hover:bg-white/20"
|
|
4295
|
+
>
|
|
4296
|
+
Add Block
|
|
4297
|
+
</button>
|
|
4298
|
+
</div>
|
|
4299
|
+
|
|
4300
|
+
<div class="space-y-4" data-blocks-list>
|
|
4301
|
+
${blockItems || emptyState}
|
|
4302
|
+
</div>
|
|
4303
|
+
|
|
4304
|
+
${templates}
|
|
4305
|
+
</div>
|
|
4306
|
+
${getDragSortableScript()}
|
|
4307
|
+
${getBlocksFieldScript()}
|
|
4308
|
+
`;
|
|
3996
4309
|
}
|
|
3997
|
-
function
|
|
3998
|
-
const
|
|
3999
|
-
const
|
|
4000
|
-
const
|
|
4310
|
+
function renderStructuredObjectField(field, options, baseClasses, errorClasses) {
|
|
4311
|
+
const { value = {}, pluginStatuses = {} } = options;
|
|
4312
|
+
const opts = field.field_options || {};
|
|
4313
|
+
const properties = opts.properties && typeof opts.properties === "object" ? opts.properties : {};
|
|
4314
|
+
const fieldId = `field-${field.field_name}`;
|
|
4315
|
+
const fieldName = field.field_name;
|
|
4316
|
+
const objectValue = normalizeStructuredObjectValue(value);
|
|
4317
|
+
const subfields = Object.entries(properties).map(
|
|
4318
|
+
([propertyName, propertyConfig]) => renderStructuredSubfield(
|
|
4319
|
+
field,
|
|
4320
|
+
propertyName,
|
|
4321
|
+
propertyConfig,
|
|
4322
|
+
objectValue,
|
|
4323
|
+
pluginStatuses,
|
|
4324
|
+
field.field_name
|
|
4325
|
+
)
|
|
4326
|
+
).join("");
|
|
4001
4327
|
return `
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4328
|
+
<div class="space-y-4" data-structured-object data-field-name="${escapeHtml2(fieldName)}">
|
|
4329
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml2(JSON.stringify(objectValue))}">
|
|
4330
|
+
<div class="space-y-4" data-structured-object-fields>
|
|
4331
|
+
${subfields}
|
|
4332
|
+
</div>
|
|
4333
|
+
</div>
|
|
4334
|
+
${getStructuredFieldScript()}
|
|
4335
|
+
`;
|
|
4336
|
+
}
|
|
4337
|
+
function renderStructuredArrayField(field, options, baseClasses, errorClasses) {
|
|
4338
|
+
const { value = [], pluginStatuses = {} } = options;
|
|
4339
|
+
const opts = field.field_options || {};
|
|
4340
|
+
const itemsConfig = opts.items && typeof opts.items === "object" ? opts.items : {};
|
|
4341
|
+
const fieldId = `field-${field.field_name}`;
|
|
4342
|
+
const fieldName = field.field_name;
|
|
4343
|
+
const arrayValue = normalizeStructuredArrayValue(value);
|
|
4344
|
+
const items = arrayValue.map(
|
|
4345
|
+
(itemValue, index) => renderStructuredArrayItem(field, itemsConfig, String(index), itemValue, pluginStatuses)
|
|
4346
|
+
).join("");
|
|
4347
|
+
const emptyState = arrayValue.length === 0 ? `
|
|
4348
|
+
<div class="rounded-lg border border-dashed border-zinc-200 dark:border-white/10 px-4 py-6 text-center text-sm text-zinc-500 dark:text-zinc-400" data-structured-empty>
|
|
4349
|
+
No items yet. Add the first item to get started.
|
|
4350
|
+
</div>
|
|
4351
|
+
` : "";
|
|
4352
|
+
return `
|
|
4353
|
+
<div class="space-y-4" data-structured-array data-field-name="${escapeHtml2(fieldName)}">
|
|
4354
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${escapeHtml2(JSON.stringify(arrayValue))}">
|
|
4011
4355
|
|
|
4012
|
-
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4356
|
+
<div class="flex items-center justify-between gap-3">
|
|
4357
|
+
<div class="text-sm text-zinc-500 dark:text-zinc-400">
|
|
4358
|
+
${escapeHtml2(opts.itemLabel || "Items")}
|
|
4359
|
+
</div>
|
|
4360
|
+
<button
|
|
4361
|
+
type="button"
|
|
4362
|
+
data-action="add-item"
|
|
4363
|
+
class="inline-flex items-center justify-center rounded-lg bg-zinc-900 px-3 py-2 text-sm font-semibold text-white hover:bg-zinc-800 dark:bg-white/10 dark:hover:bg-white/20"
|
|
4364
|
+
>
|
|
4365
|
+
Add item
|
|
4366
|
+
</button>
|
|
4367
|
+
</div>
|
|
4016
4368
|
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
|
|
4369
|
+
<div class="space-y-4" data-structured-array-list>
|
|
4370
|
+
${items || emptyState}
|
|
4371
|
+
</div>
|
|
4372
|
+
|
|
4373
|
+
<template data-structured-array-template>
|
|
4374
|
+
${renderStructuredArrayItem(field, itemsConfig, "__INDEX__", {}, pluginStatuses)}
|
|
4375
|
+
</template>
|
|
4376
|
+
</div>
|
|
4377
|
+
${getDragSortableScript()}
|
|
4378
|
+
${getStructuredFieldScript()}
|
|
4379
|
+
`;
|
|
4380
|
+
}
|
|
4381
|
+
function renderStructuredArrayItem(field, itemConfig, index, itemValue, pluginStatuses) {
|
|
4382
|
+
const itemFields = renderStructuredItemFields(field, itemConfig, index, itemValue, pluginStatuses);
|
|
4383
|
+
return `
|
|
4384
|
+
<div class="structured-array-item rounded-lg border border-zinc-200 dark:border-white/10 bg-white/60 dark:bg-white/5 p-4 shadow-sm" data-array-index="${escapeHtml2(index)}" draggable="true">
|
|
4385
|
+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
4386
|
+
<div class="flex items-center gap-3">
|
|
4387
|
+
<div class="drag-handle cursor-move text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-400" data-action="drag-handle" title="Drag to reorder">
|
|
4388
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
4389
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/>
|
|
4390
|
+
</svg>
|
|
4391
|
+
</div>
|
|
4392
|
+
<div class="text-sm font-semibold text-zinc-900 dark:text-white">
|
|
4393
|
+
Item <span class="ml-2 text-xs font-normal text-zinc-500 dark:text-zinc-400" data-array-order-label></span>
|
|
4394
|
+
</div>
|
|
4395
|
+
</div>
|
|
4396
|
+
<div class="flex flex-wrap gap-2 text-xs">
|
|
4397
|
+
<button type="button" data-action="move-up" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move item up" title="Move up">
|
|
4398
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
4399
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6l-4 4m4-4l4 4m-4-4v12"/>
|
|
4400
|
+
</svg>
|
|
4401
|
+
</button>
|
|
4402
|
+
<button type="button" data-action="move-down" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move item down" title="Move down">
|
|
4403
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
4404
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18l4-4m-4 4l-4-4m4 4V6"/>
|
|
4405
|
+
</svg>
|
|
4406
|
+
</button>
|
|
4407
|
+
<button type="button" data-action="remove-item" class="inline-flex items-center gap-x-1 px-2.5 py-1.5 text-xs font-medium text-pink-700 dark:text-pink-300 hover:bg-pink-50 dark:hover:bg-pink-900/20 rounded-lg transition-colors">
|
|
4408
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
|
|
4409
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 0 00-7.5 0"/>
|
|
4410
|
+
</svg>
|
|
4411
|
+
Delete item
|
|
4412
|
+
</button>
|
|
4413
|
+
</div>
|
|
4414
|
+
</div>
|
|
4415
|
+
<div class="mt-4 space-y-4" data-array-item-fields>
|
|
4416
|
+
${itemFields}
|
|
4417
|
+
</div>
|
|
4418
|
+
</div>
|
|
4419
|
+
`;
|
|
4420
|
+
}
|
|
4421
|
+
function renderStructuredItemFields(field, itemConfig, index, itemValue, pluginStatuses) {
|
|
4422
|
+
const itemType = itemConfig?.type || "string";
|
|
4423
|
+
if (itemType === "object" && itemConfig?.properties && typeof itemConfig.properties === "object") {
|
|
4424
|
+
const fieldPrefix = `array-${field.field_name}-${index}`;
|
|
4425
|
+
return Object.entries(itemConfig.properties).map(
|
|
4426
|
+
([propertyName, propertyConfig]) => renderStructuredSubfield(
|
|
4427
|
+
field,
|
|
4428
|
+
propertyName,
|
|
4429
|
+
propertyConfig,
|
|
4430
|
+
itemValue || {},
|
|
4431
|
+
pluginStatuses,
|
|
4432
|
+
fieldPrefix
|
|
4433
|
+
)
|
|
4434
|
+
).join("");
|
|
4435
|
+
}
|
|
4436
|
+
const normalizedField = normalizeBlockField(itemConfig, "Item");
|
|
4437
|
+
const fieldValue = itemValue ?? normalizedField.defaultValue ?? "";
|
|
4438
|
+
const fieldDefinition = {
|
|
4439
|
+
id: `array-${field.field_name}-${index}-value`,
|
|
4440
|
+
field_name: `array-${field.field_name}-${index}-value`,
|
|
4441
|
+
field_type: normalizedField.type,
|
|
4442
|
+
field_label: normalizedField.label,
|
|
4443
|
+
field_options: normalizedField.options,
|
|
4444
|
+
is_required: normalizedField.required};
|
|
4445
|
+
return `
|
|
4446
|
+
<div class="structured-subfield" data-structured-field="__value" data-field-type="${escapeHtml2(normalizedField.type)}">
|
|
4447
|
+
${renderDynamicField(fieldDefinition, { value: fieldValue, pluginStatuses })}
|
|
4448
|
+
</div>
|
|
4449
|
+
`;
|
|
4450
|
+
}
|
|
4451
|
+
function renderStructuredSubfield(field, propertyName, propertyConfig, objectValue, pluginStatuses, fieldPrefix) {
|
|
4452
|
+
const normalizedField = normalizeBlockField(propertyConfig, propertyName);
|
|
4453
|
+
const fieldValue = objectValue?.[propertyName] ?? normalizedField.defaultValue ?? "";
|
|
4454
|
+
const fieldDefinition = {
|
|
4455
|
+
field_name: `${fieldPrefix}__${propertyName}`,
|
|
4456
|
+
field_type: normalizedField.type,
|
|
4457
|
+
field_label: normalizedField.label,
|
|
4458
|
+
field_options: normalizedField.options,
|
|
4459
|
+
is_required: normalizedField.required};
|
|
4460
|
+
return `
|
|
4461
|
+
<div class="structured-subfield" data-structured-field="${escapeHtml2(propertyName)}" data-field-type="${escapeHtml2(normalizedField.type)}">
|
|
4462
|
+
${renderDynamicField(fieldDefinition, { value: fieldValue, pluginStatuses })}
|
|
4463
|
+
</div>
|
|
4464
|
+
`;
|
|
4465
|
+
}
|
|
4466
|
+
function normalizeStructuredObjectValue(value) {
|
|
4467
|
+
if (!value) return {};
|
|
4468
|
+
if (typeof value === "string") {
|
|
4469
|
+
try {
|
|
4470
|
+
const parsed = JSON.parse(value);
|
|
4471
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
4472
|
+
} catch {
|
|
4473
|
+
return {};
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
if (typeof value === "object" && !Array.isArray(value)) return value;
|
|
4477
|
+
return {};
|
|
4478
|
+
}
|
|
4479
|
+
function normalizeStructuredArrayValue(value) {
|
|
4480
|
+
if (!value) return [];
|
|
4481
|
+
if (Array.isArray(value)) return value;
|
|
4482
|
+
if (typeof value === "string") {
|
|
4483
|
+
try {
|
|
4484
|
+
const parsed = JSON.parse(value);
|
|
4485
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
4486
|
+
} catch {
|
|
4487
|
+
return [];
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
return [];
|
|
4491
|
+
}
|
|
4492
|
+
function normalizeBlockDefinitions(rawBlocks) {
|
|
4493
|
+
if (!rawBlocks || typeof rawBlocks !== "object") return [];
|
|
4494
|
+
return Object.entries(rawBlocks).filter(([name, block]) => typeof name === "string" && block && typeof block === "object").map(([name, block]) => ({
|
|
4495
|
+
name,
|
|
4496
|
+
label: block.label || name,
|
|
4497
|
+
description: block.description,
|
|
4498
|
+
properties: block.properties && typeof block.properties === "object" ? block.properties : {}
|
|
4499
|
+
}));
|
|
4500
|
+
}
|
|
4501
|
+
function normalizeBlocksValue(value, discriminator) {
|
|
4502
|
+
const normalizeItem = (item) => {
|
|
4503
|
+
if (!item || typeof item !== "object") return null;
|
|
4504
|
+
if (item[discriminator]) return item;
|
|
4505
|
+
if (item.blockType && item.data && typeof item.data === "object") {
|
|
4506
|
+
return { [discriminator]: item.blockType, ...item.data };
|
|
4507
|
+
}
|
|
4508
|
+
return item;
|
|
4509
|
+
};
|
|
4510
|
+
const fromArray = (items) => items.map(normalizeItem).filter((item) => item && typeof item === "object");
|
|
4511
|
+
if (Array.isArray(value)) return fromArray(value);
|
|
4512
|
+
if (typeof value === "string" && value.trim()) {
|
|
4513
|
+
try {
|
|
4514
|
+
const parsed = JSON.parse(value);
|
|
4515
|
+
return Array.isArray(parsed) ? fromArray(parsed) : [];
|
|
4516
|
+
} catch {
|
|
4517
|
+
return [];
|
|
4518
|
+
}
|
|
4519
|
+
}
|
|
4520
|
+
return [];
|
|
4521
|
+
}
|
|
4522
|
+
function renderBlockTemplate(field, block, discriminator, pluginStatuses) {
|
|
4523
|
+
return `
|
|
4524
|
+
<template data-block-template="${escapeHtml2(block.name)}">
|
|
4525
|
+
${renderBlockCard(field, block, discriminator, "__INDEX__", {}, pluginStatuses)}
|
|
4526
|
+
</template>
|
|
4527
|
+
`;
|
|
4528
|
+
}
|
|
4529
|
+
function renderBlockItem(field, blockValue, blocks, discriminator, index, pluginStatuses) {
|
|
4530
|
+
const blockType = blockValue?.[discriminator] || blockValue?.blockType;
|
|
4531
|
+
const blockDefinition = blocks.find((block) => block.name === blockType);
|
|
4532
|
+
if (!blockDefinition) {
|
|
4533
|
+
return `
|
|
4534
|
+
<div class="rounded-lg border border-amber-200 bg-amber-50/50 px-4 py-3 text-sm text-amber-700 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200" data-block-raw="${escapeHtml2(JSON.stringify(blockValue || {}))}">
|
|
4535
|
+
Unknown block type: <strong>${escapeHtml2(String(blockType || "unknown"))}</strong>. This block will be preserved as-is.
|
|
4536
|
+
</div>
|
|
4537
|
+
`;
|
|
4538
|
+
}
|
|
4539
|
+
const data = blockValue && typeof blockValue === "object" ? Object.fromEntries(Object.entries(blockValue).filter(([key]) => key !== discriminator)) : {};
|
|
4540
|
+
return renderBlockCard(field, blockDefinition, discriminator, String(index), data, pluginStatuses);
|
|
4541
|
+
}
|
|
4542
|
+
function renderBlockCard(field, block, discriminator, index, data, pluginStatuses) {
|
|
4543
|
+
const blockFields = Object.entries(block.properties).map(([fieldName, fieldConfig]) => {
|
|
4544
|
+
if (fieldConfig?.type === "array" && fieldConfig?.items?.blocks) {
|
|
4545
|
+
return `
|
|
4546
|
+
<div class="rounded-lg border border-dashed border-amber-200 bg-amber-50/50 px-4 py-3 text-xs text-amber-700 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200">
|
|
4547
|
+
Nested blocks are not supported yet for "${escapeHtml2(fieldName)}".
|
|
4548
|
+
</div>
|
|
4549
|
+
`;
|
|
4550
|
+
}
|
|
4551
|
+
const normalizedField = normalizeBlockField(fieldConfig, fieldName);
|
|
4552
|
+
const fieldValue = data?.[fieldName] ?? normalizedField.defaultValue ?? "";
|
|
4553
|
+
const fieldDefinition = {
|
|
4554
|
+
id: `block-${field.field_name}-${index}-${fieldName}`,
|
|
4555
|
+
field_name: `block-${field.field_name}-${index}-${fieldName}`,
|
|
4556
|
+
field_type: normalizedField.type,
|
|
4557
|
+
field_label: normalizedField.label,
|
|
4558
|
+
field_options: normalizedField.options,
|
|
4559
|
+
is_required: normalizedField.required};
|
|
4560
|
+
return `
|
|
4561
|
+
<div class="blocks-subfield" data-block-field="${escapeHtml2(fieldName)}" data-field-type="${escapeHtml2(normalizedField.type)}">
|
|
4562
|
+
${renderDynamicField(fieldDefinition, { value: fieldValue, pluginStatuses })}
|
|
4563
|
+
</div>
|
|
4564
|
+
`;
|
|
4565
|
+
}).join("");
|
|
4566
|
+
return `
|
|
4567
|
+
<div class="blocks-item rounded-lg border border-zinc-200 dark:border-white/10 bg-white/60 dark:bg-white/5 p-4 shadow-sm" data-block-type="${escapeHtml2(block.name)}" data-block-discriminator="${escapeHtml2(discriminator)}" draggable="true">
|
|
4568
|
+
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
4569
|
+
<div class="flex items-start gap-3">
|
|
4570
|
+
<div class="drag-handle cursor-move text-zinc-400 dark:text-zinc-500 hover:text-zinc-600 dark:hover:text-zinc-400" data-action="drag-handle" title="Drag to reorder">
|
|
4571
|
+
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
4572
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M4 8h16M4 16h16"/>
|
|
4573
|
+
</svg>
|
|
4574
|
+
</div>
|
|
4575
|
+
<div>
|
|
4576
|
+
<div class="text-sm font-semibold text-zinc-900 dark:text-white">
|
|
4577
|
+
${escapeHtml2(block.label)}
|
|
4578
|
+
<span class="ml-2 text-xs font-normal text-zinc-500 dark:text-zinc-400" data-block-order-label></span>
|
|
4579
|
+
</div>
|
|
4580
|
+
${block.description ? `<p class="text-xs text-zinc-500 dark:text-zinc-400">${escapeHtml2(block.description)}</p>` : ""}
|
|
4581
|
+
</div>
|
|
4582
|
+
</div>
|
|
4583
|
+
<div class="flex flex-wrap gap-2 text-xs">
|
|
4584
|
+
<button type="button" data-action="move-up" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move block up" title="Move up">
|
|
4585
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
4586
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6l-4 4m4-4l4 4m-4-4v12"/>
|
|
4587
|
+
</svg>
|
|
4588
|
+
</button>
|
|
4589
|
+
<button type="button" data-action="move-down" class="inline-flex items-center justify-center rounded-md border border-zinc-200 px-2 py-1 text-zinc-600 hover:bg-zinc-100 dark:border-white/10 dark:text-zinc-300 dark:hover:bg-white/10 disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:bg-transparent dark:disabled:hover:bg-transparent" aria-label="Move block down" title="Move down">
|
|
4590
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="4">
|
|
4591
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 18l4-4m-4 4l-4-4m4 4V6"/>
|
|
4592
|
+
</svg>
|
|
4593
|
+
</button>
|
|
4594
|
+
<button type="button" data-action="remove-block" class="inline-flex items-center gap-x-1 px-2.5 py-1.5 text-xs font-medium text-pink-700 dark:text-pink-300 hover:bg-pink-50 dark:hover:bg-pink-900/20 rounded-lg transition-colors">
|
|
4595
|
+
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
|
|
4596
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"/>
|
|
4597
|
+
</svg>
|
|
4598
|
+
Delete block
|
|
4599
|
+
</button>
|
|
4600
|
+
</div>
|
|
4601
|
+
</div>
|
|
4602
|
+
<div class="mt-4 space-y-4">
|
|
4603
|
+
${blockFields}
|
|
4604
|
+
</div>
|
|
4605
|
+
</div>
|
|
4606
|
+
`;
|
|
4607
|
+
}
|
|
4608
|
+
function normalizeBlockField(fieldConfig, fieldName) {
|
|
4609
|
+
const type = fieldConfig?.type || "text";
|
|
4610
|
+
const label = fieldConfig?.title || fieldName;
|
|
4611
|
+
const required = fieldConfig?.required === true;
|
|
4612
|
+
const options = { ...fieldConfig };
|
|
4613
|
+
if (type === "select" && Array.isArray(fieldConfig?.enum)) {
|
|
4614
|
+
options.options = fieldConfig.enum.map((value, index) => ({
|
|
4615
|
+
value,
|
|
4616
|
+
label: fieldConfig.enumLabels?.[index] || value
|
|
4617
|
+
}));
|
|
4618
|
+
}
|
|
4619
|
+
return {
|
|
4620
|
+
type,
|
|
4621
|
+
label,
|
|
4622
|
+
required,
|
|
4623
|
+
defaultValue: fieldConfig?.default,
|
|
4624
|
+
options
|
|
4625
|
+
};
|
|
4626
|
+
}
|
|
4627
|
+
function getStructuredFieldScript() {
|
|
4628
|
+
return `
|
|
4629
|
+
${getReadFieldValueScript()}
|
|
4630
|
+
<script>
|
|
4631
|
+
if (!window.__sonicStructuredFieldInit) {
|
|
4632
|
+
window.__sonicStructuredFieldInit = true;
|
|
4633
|
+
|
|
4634
|
+
function initializeStructuredFields() {
|
|
4635
|
+
const readFieldValue = window.sonicReadFieldValue;
|
|
4636
|
+
|
|
4637
|
+
const readStructuredValue = (container) => {
|
|
4638
|
+
const fields = Array.from(container.querySelectorAll('.structured-subfield'));
|
|
4639
|
+
if (fields.length === 1 && fields[0].dataset.structuredField === '__value') {
|
|
4640
|
+
return readFieldValue(fields[0]);
|
|
4641
|
+
}
|
|
4642
|
+
|
|
4643
|
+
return fields.reduce((acc, fieldWrapper) => {
|
|
4644
|
+
const fieldName = fieldWrapper.dataset.structuredField;
|
|
4645
|
+
if (!fieldName || fieldName === '__value') return acc;
|
|
4646
|
+
acc[fieldName] = readFieldValue(fieldWrapper);
|
|
4647
|
+
return acc;
|
|
4648
|
+
}, {});
|
|
4649
|
+
};
|
|
4650
|
+
|
|
4651
|
+
document.querySelectorAll('[data-structured-object]').forEach((container) => {
|
|
4652
|
+
if (container.dataset.structuredInitialized === 'true') {
|
|
4653
|
+
return;
|
|
4654
|
+
}
|
|
4655
|
+
container.dataset.structuredInitialized = 'true';
|
|
4656
|
+
const hiddenInput = container.querySelector('input[type="hidden"]');
|
|
4657
|
+
|
|
4658
|
+
const updateHiddenInput = () => {
|
|
4659
|
+
if (!hiddenInput) return;
|
|
4660
|
+
const value = readStructuredValue(container);
|
|
4661
|
+
hiddenInput.value = JSON.stringify(value);
|
|
4662
|
+
};
|
|
4663
|
+
|
|
4664
|
+
container.addEventListener('input', updateHiddenInput);
|
|
4665
|
+
container.addEventListener('change', updateHiddenInput);
|
|
4666
|
+
updateHiddenInput();
|
|
4667
|
+
});
|
|
4668
|
+
|
|
4669
|
+
document.querySelectorAll('[data-structured-array]').forEach((container) => {
|
|
4670
|
+
if (container.dataset.structuredInitialized === 'true') {
|
|
4671
|
+
return;
|
|
4672
|
+
}
|
|
4673
|
+
container.dataset.structuredInitialized = 'true';
|
|
4674
|
+
const list = container.querySelector('[data-structured-array-list]');
|
|
4675
|
+
const hiddenInput = container.querySelector('input[type="hidden"]');
|
|
4676
|
+
const template = container.querySelector('template[data-structured-array-template]');
|
|
4677
|
+
|
|
4678
|
+
const updateOrderLabels = () => {
|
|
4679
|
+
const items = Array.from(container.querySelectorAll('.structured-array-item'));
|
|
4680
|
+
items.forEach((item, index) => {
|
|
4681
|
+
const label = item.querySelector('[data-array-order-label]');
|
|
4682
|
+
if (label) {
|
|
4683
|
+
label.textContent = '#'+ (index + 1);
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4686
|
+
const moveUpButton = item.querySelector('[data-action="move-up"]');
|
|
4687
|
+
if (moveUpButton instanceof HTMLButtonElement) {
|
|
4688
|
+
moveUpButton.disabled = index === 0;
|
|
4689
|
+
}
|
|
4690
|
+
|
|
4691
|
+
const moveDownButton = item.querySelector('[data-action="move-down"]');
|
|
4692
|
+
if (moveDownButton instanceof HTMLButtonElement) {
|
|
4693
|
+
moveDownButton.disabled = index === items.length - 1;
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
4696
|
+
};
|
|
4697
|
+
|
|
4698
|
+
const updateHiddenInput = () => {
|
|
4699
|
+
if (!hiddenInput || !list) return;
|
|
4700
|
+
const items = Array.from(list.querySelectorAll('.structured-array-item'));
|
|
4701
|
+
const values = items.map((item) => readStructuredValue(item));
|
|
4702
|
+
hiddenInput.value = JSON.stringify(values);
|
|
4703
|
+
|
|
4704
|
+
const emptyState = list.querySelector('[data-structured-empty]');
|
|
4705
|
+
if (emptyState) {
|
|
4706
|
+
emptyState.style.display = values.length === 0 ? 'block' : 'none';
|
|
4707
|
+
}
|
|
4708
|
+
updateOrderLabels();
|
|
4709
|
+
};
|
|
4710
|
+
|
|
4711
|
+
if (typeof window.initializeDragSortable === 'function' && list) {
|
|
4712
|
+
window.initializeDragSortable(list, {
|
|
4713
|
+
itemSelector: '.structured-array-item',
|
|
4714
|
+
handleSelector: '[data-action="drag-handle"]',
|
|
4715
|
+
onUpdate: updateHiddenInput
|
|
4716
|
+
});
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
container.addEventListener('click', (event) => {
|
|
4720
|
+
const target = event.target;
|
|
4721
|
+
if (!(target instanceof Element)) return;
|
|
4722
|
+
const actionButton = target.closest('[data-action]');
|
|
4723
|
+
if (!actionButton || actionButton.hasAttribute('disabled')) return;
|
|
4724
|
+
|
|
4725
|
+
const action = actionButton.getAttribute('data-action');
|
|
4726
|
+
|
|
4727
|
+
if (action === 'add-item') {
|
|
4728
|
+
if (!list || !template) return;
|
|
4729
|
+
const nextIndex = list.querySelectorAll('.structured-array-item').length;
|
|
4730
|
+
const html = template.innerHTML.replace(/__INDEX__/g, String(nextIndex));
|
|
4731
|
+
list.insertAdjacentHTML('beforeend', html);
|
|
4732
|
+
if (typeof initializeTinyMCE === 'function') {
|
|
4733
|
+
initializeTinyMCE();
|
|
4734
|
+
}
|
|
4735
|
+
if (typeof window.initializeQuillEditors === 'function') {
|
|
4736
|
+
window.initializeQuillEditors();
|
|
4737
|
+
}
|
|
4738
|
+
if (typeof initializeMDXEditor === 'function') {
|
|
4739
|
+
initializeMDXEditor();
|
|
4740
|
+
}
|
|
4741
|
+
updateHiddenInput();
|
|
4742
|
+
return;
|
|
4743
|
+
}
|
|
4744
|
+
|
|
4745
|
+
const item = actionButton.closest('.structured-array-item');
|
|
4746
|
+
if (!item || !list) return;
|
|
4747
|
+
|
|
4748
|
+
if (action === 'remove-item') {
|
|
4749
|
+
item.remove();
|
|
4750
|
+
updateHiddenInput();
|
|
4751
|
+
return;
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4754
|
+
if (action === 'move-up') {
|
|
4755
|
+
const previous = item.previousElementSibling;
|
|
4756
|
+
if (previous) {
|
|
4757
|
+
list.insertBefore(item, previous);
|
|
4758
|
+
updateHiddenInput();
|
|
4759
|
+
}
|
|
4760
|
+
return;
|
|
4761
|
+
}
|
|
4762
|
+
|
|
4763
|
+
if (action === 'move-down') {
|
|
4764
|
+
const next = item.nextElementSibling;
|
|
4765
|
+
if (next) {
|
|
4766
|
+
list.insertBefore(next, item);
|
|
4767
|
+
updateHiddenInput();
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
});
|
|
4771
|
+
|
|
4772
|
+
container.addEventListener('input', (event) => {
|
|
4773
|
+
const target = event.target;
|
|
4774
|
+
if (!(target instanceof Element)) return;
|
|
4775
|
+
if (target.closest('[data-structured-array-list]')) {
|
|
4776
|
+
updateHiddenInput();
|
|
4777
|
+
}
|
|
4778
|
+
});
|
|
4779
|
+
|
|
4780
|
+
container.addEventListener('change', (event) => {
|
|
4781
|
+
const target = event.target;
|
|
4782
|
+
if (!(target instanceof Element)) return;
|
|
4783
|
+
if (target.closest('[data-structured-array-list]')) {
|
|
4784
|
+
updateHiddenInput();
|
|
4785
|
+
}
|
|
4786
|
+
});
|
|
4787
|
+
|
|
4788
|
+
updateHiddenInput();
|
|
4789
|
+
});
|
|
4790
|
+
}
|
|
4791
|
+
|
|
4792
|
+
window.initializeStructuredFields = initializeStructuredFields;
|
|
4793
|
+
|
|
4794
|
+
if (document.readyState === 'loading') {
|
|
4795
|
+
document.addEventListener('DOMContentLoaded', initializeStructuredFields);
|
|
4796
|
+
} else {
|
|
4797
|
+
initializeStructuredFields();
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4800
|
+
document.addEventListener('htmx:afterSwap', function() {
|
|
4801
|
+
setTimeout(initializeStructuredFields, 50);
|
|
4802
|
+
});
|
|
4803
|
+
} else if (typeof window.initializeStructuredFields === 'function') {
|
|
4804
|
+
window.initializeStructuredFields();
|
|
4805
|
+
}
|
|
4806
|
+
</script>
|
|
4807
|
+
`;
|
|
4808
|
+
}
|
|
4809
|
+
function getBlocksFieldScript() {
|
|
4810
|
+
return `
|
|
4811
|
+
${getReadFieldValueScript()}
|
|
4812
|
+
<script>
|
|
4813
|
+
if (!window.__sonicBlocksFieldInit) {
|
|
4814
|
+
window.__sonicBlocksFieldInit = true;
|
|
4815
|
+
|
|
4816
|
+
function initializeBlocksFields() {
|
|
4817
|
+
document.querySelectorAll('.blocks-field').forEach((container) => {
|
|
4818
|
+
if (container.dataset.blocksInitialized === 'true') {
|
|
4819
|
+
return;
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4822
|
+
container.dataset.blocksInitialized = 'true';
|
|
4823
|
+
const list = container.querySelector('[data-blocks-list]');
|
|
4824
|
+
const hiddenInput = container.querySelector('input[type="hidden"]');
|
|
4825
|
+
const typeSelect = container.querySelector('[data-role="block-type-select"]');
|
|
4826
|
+
const discriminator = container.dataset.blocksDiscriminator || 'blockType';
|
|
4827
|
+
|
|
4828
|
+
const updateOrderLabels = () => {
|
|
4829
|
+
const items = Array.from(container.querySelectorAll('.blocks-item'));
|
|
4830
|
+
items.forEach((item, index) => {
|
|
4831
|
+
const label = item.querySelector('[data-block-order-label]');
|
|
4832
|
+
if (label) {
|
|
4833
|
+
label.textContent = '#'+ (index + 1);
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
const moveUpButton = item.querySelector('[data-action="move-up"]');
|
|
4837
|
+
if (moveUpButton instanceof HTMLButtonElement) {
|
|
4838
|
+
moveUpButton.disabled = index === 0;
|
|
4839
|
+
}
|
|
4840
|
+
|
|
4841
|
+
const moveDownButton = item.querySelector('[data-action="move-down"]');
|
|
4842
|
+
if (moveDownButton instanceof HTMLButtonElement) {
|
|
4843
|
+
moveDownButton.disabled = index === items.length - 1;
|
|
4844
|
+
}
|
|
4845
|
+
});
|
|
4846
|
+
};
|
|
4847
|
+
|
|
4848
|
+
const readFieldValue = window.sonicReadFieldValue;
|
|
4849
|
+
|
|
4850
|
+
const readBlockItem = (item) => {
|
|
4851
|
+
if (item.dataset.blockRaw) {
|
|
4852
|
+
try {
|
|
4853
|
+
return JSON.parse(item.dataset.blockRaw);
|
|
4854
|
+
} catch (error) {
|
|
4855
|
+
return {};
|
|
4856
|
+
}
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4859
|
+
const blockType = item.dataset.blockType;
|
|
4860
|
+
const data = {};
|
|
4861
|
+
|
|
4862
|
+
item.querySelectorAll('.blocks-subfield').forEach((fieldWrapper) => {
|
|
4863
|
+
const fieldName = fieldWrapper.dataset.blockField;
|
|
4864
|
+
if (!fieldName) {
|
|
4865
|
+
return;
|
|
4866
|
+
}
|
|
4867
|
+
data[fieldName] = readFieldValue(fieldWrapper);
|
|
4868
|
+
});
|
|
4869
|
+
|
|
4870
|
+
return { [discriminator]: blockType, ...data };
|
|
4871
|
+
};
|
|
4872
|
+
|
|
4873
|
+
const updateHiddenInput = () => {
|
|
4874
|
+
if (!hiddenInput || !list) return;
|
|
4875
|
+
const items = Array.from(list.querySelectorAll('.blocks-item, [data-block-raw]'));
|
|
4876
|
+
const blocksData = items.map((item) => readBlockItem(item));
|
|
4877
|
+
hiddenInput.value = JSON.stringify(blocksData);
|
|
4878
|
+
|
|
4879
|
+
const emptyState = list.querySelector('[data-blocks-empty]');
|
|
4880
|
+
if (emptyState) {
|
|
4881
|
+
emptyState.style.display = blocksData.length === 0 ? 'block' : 'none';
|
|
4882
|
+
}
|
|
4883
|
+
updateOrderLabels();
|
|
4884
|
+
};
|
|
4885
|
+
|
|
4886
|
+
const initializeEditors = () => {
|
|
4887
|
+
if (typeof initializeTinyMCE === 'function') {
|
|
4888
|
+
initializeTinyMCE();
|
|
4889
|
+
}
|
|
4890
|
+
if (typeof window.initializeQuillEditors === 'function') {
|
|
4891
|
+
window.initializeQuillEditors();
|
|
4892
|
+
}
|
|
4893
|
+
if (typeof initializeMDXEditor === 'function') {
|
|
4894
|
+
initializeMDXEditor();
|
|
4895
|
+
}
|
|
4896
|
+
};
|
|
4897
|
+
|
|
4898
|
+
if (typeof window.initializeDragSortable === 'function' && list) {
|
|
4899
|
+
window.initializeDragSortable(list, {
|
|
4900
|
+
itemSelector: '.blocks-item',
|
|
4901
|
+
handleSelector: '[data-action="drag-handle"]',
|
|
4902
|
+
onUpdate: updateHiddenInput
|
|
4903
|
+
});
|
|
4904
|
+
}
|
|
4905
|
+
|
|
4906
|
+
container.addEventListener('click', (event) => {
|
|
4907
|
+
const target = event.target;
|
|
4908
|
+
if (!(target instanceof Element)) return;
|
|
4909
|
+
const actionButton = target.closest('[data-action]');
|
|
4910
|
+
if (!actionButton) return;
|
|
4911
|
+
|
|
4912
|
+
if (actionButton.hasAttribute('disabled')) {
|
|
4913
|
+
return;
|
|
4914
|
+
}
|
|
4915
|
+
|
|
4916
|
+
const action = actionButton.getAttribute('data-action');
|
|
4917
|
+
if (action === 'add-block') {
|
|
4918
|
+
const blockType = typeSelect ? typeSelect.value : '';
|
|
4919
|
+
if (!blockType || !list) return;
|
|
4920
|
+
const template = container.querySelector('template[data-block-template="' + blockType + '"]');
|
|
4921
|
+
if (!template) return;
|
|
4922
|
+
|
|
4923
|
+
const nextIndex = list.querySelectorAll('.blocks-item').length;
|
|
4924
|
+
const html = template.innerHTML.replace(/__INDEX__/g, String(nextIndex));
|
|
4925
|
+
list.insertAdjacentHTML('beforeend', html);
|
|
4926
|
+
if (typeSelect) {
|
|
4927
|
+
typeSelect.value = '';
|
|
4928
|
+
}
|
|
4929
|
+
initializeEditors();
|
|
4930
|
+
if (typeof window.initializeStructuredFields === 'function') {
|
|
4931
|
+
window.initializeStructuredFields();
|
|
4932
|
+
}
|
|
4933
|
+
updateHiddenInput();
|
|
4934
|
+
return;
|
|
4935
|
+
}
|
|
4936
|
+
|
|
4937
|
+
const item = actionButton.closest('.blocks-item');
|
|
4938
|
+
if (!item || !list) return;
|
|
4939
|
+
|
|
4940
|
+
if (action === 'remove-block') {
|
|
4941
|
+
item.remove();
|
|
4942
|
+
updateHiddenInput();
|
|
4943
|
+
return;
|
|
4944
|
+
}
|
|
4945
|
+
|
|
4946
|
+
if (action === 'move-up') {
|
|
4947
|
+
const previous = item.previousElementSibling;
|
|
4948
|
+
if (previous) {
|
|
4949
|
+
list.insertBefore(item, previous);
|
|
4950
|
+
updateHiddenInput();
|
|
4951
|
+
}
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4954
|
+
|
|
4955
|
+
if (action === 'move-down') {
|
|
4956
|
+
const next = item.nextElementSibling;
|
|
4957
|
+
if (next) {
|
|
4958
|
+
list.insertBefore(next, item);
|
|
4959
|
+
updateHiddenInput();
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
});
|
|
4963
|
+
|
|
4964
|
+
container.addEventListener('input', (event) => {
|
|
4965
|
+
const target = event.target;
|
|
4966
|
+
if (!(target instanceof Element)) return;
|
|
4967
|
+
if (target.closest('[data-blocks-list]')) {
|
|
4968
|
+
updateHiddenInput();
|
|
4969
|
+
}
|
|
4970
|
+
});
|
|
4971
|
+
|
|
4972
|
+
container.addEventListener('change', (event) => {
|
|
4973
|
+
const target = event.target;
|
|
4974
|
+
if (!(target instanceof Element)) return;
|
|
4975
|
+
if (target.closest('[data-blocks-list]')) {
|
|
4976
|
+
updateHiddenInput();
|
|
4977
|
+
}
|
|
4978
|
+
});
|
|
4979
|
+
|
|
4980
|
+
updateHiddenInput();
|
|
4981
|
+
});
|
|
4982
|
+
}
|
|
4983
|
+
|
|
4984
|
+
window.initializeBlocksFields = initializeBlocksFields;
|
|
4985
|
+
|
|
4986
|
+
if (document.readyState === 'loading') {
|
|
4987
|
+
document.addEventListener('DOMContentLoaded', initializeBlocksFields);
|
|
4988
|
+
} else {
|
|
4989
|
+
initializeBlocksFields();
|
|
4990
|
+
}
|
|
4991
|
+
|
|
4992
|
+
document.addEventListener('htmx:afterSwap', function() {
|
|
4993
|
+
setTimeout(initializeBlocksFields, 50);
|
|
4994
|
+
});
|
|
4995
|
+
} else if (typeof window.initializeBlocksFields === 'function') {
|
|
4996
|
+
window.initializeBlocksFields();
|
|
4997
|
+
}
|
|
4998
|
+
</script>
|
|
4999
|
+
`;
|
|
5000
|
+
}
|
|
5001
|
+
function escapeHtml2(text) {
|
|
5002
|
+
if (typeof text !== "string") return String(text || "");
|
|
5003
|
+
return text.replace(/[&<>"']/g, (char) => ({
|
|
5004
|
+
"&": "&",
|
|
5005
|
+
"<": "<",
|
|
5006
|
+
">": ">",
|
|
5007
|
+
'"': """,
|
|
5008
|
+
"'": "'"
|
|
5009
|
+
})[char] || char);
|
|
5010
|
+
}
|
|
5011
|
+
|
|
5012
|
+
// src/plugins/available/tinymce-plugin/index.ts
|
|
5013
|
+
var builder = chunkYHW27CBV_cjs.PluginBuilder.create({
|
|
5014
|
+
name: "tinymce-plugin",
|
|
5015
|
+
version: "1.0.0",
|
|
5016
|
+
description: "Powerful WYSIWYG rich text editor for content creation"
|
|
5017
|
+
});
|
|
5018
|
+
builder.metadata({
|
|
5019
|
+
author: {
|
|
5020
|
+
name: "SonicJS Team",
|
|
5021
|
+
email: "team@sonicjs.com"
|
|
5022
|
+
},
|
|
5023
|
+
license: "MIT",
|
|
5024
|
+
compatibility: "^2.0.0"
|
|
5025
|
+
});
|
|
5026
|
+
builder.lifecycle({
|
|
5027
|
+
activate: async () => {
|
|
5028
|
+
console.info("\u2705 TinyMCE plugin activated");
|
|
5029
|
+
},
|
|
5030
|
+
deactivate: async () => {
|
|
5031
|
+
console.info("\u274C TinyMCE plugin deactivated");
|
|
5032
|
+
}
|
|
5033
|
+
});
|
|
5034
|
+
builder.build();
|
|
5035
|
+
function getTinyMCEScript(apiKey = "no-api-key") {
|
|
5036
|
+
return `<script src="https://cdn.tiny.cloud/1/${apiKey}/tinymce/6/tinymce.min.js" referrerpolicy="origin"></script>`;
|
|
5037
|
+
}
|
|
5038
|
+
function getTinyMCEInitScript(config) {
|
|
5039
|
+
const skin = config?.skin || "oxide-dark";
|
|
5040
|
+
const contentCss = skin.includes("dark") ? "dark" : "default";
|
|
5041
|
+
const defaultHeight = config?.defaultHeight || 300;
|
|
5042
|
+
return `
|
|
5043
|
+
// Initialize TinyMCE for all richtext fields
|
|
5044
|
+
function initializeTinyMCE() {
|
|
5045
|
+
if (typeof tinymce !== 'undefined') {
|
|
5046
|
+
// Find all textareas that need TinyMCE
|
|
5047
|
+
document.querySelectorAll('.richtext-container textarea').forEach((textarea) => {
|
|
5048
|
+
// Skip if already initialized
|
|
5049
|
+
if (tinymce.get(textarea.id)) {
|
|
5050
|
+
return;
|
|
5051
|
+
}
|
|
5052
|
+
|
|
5053
|
+
// Get configuration from data attributes
|
|
5054
|
+
const container = textarea.closest('.richtext-container');
|
|
5055
|
+
const height = container?.dataset.height || ${defaultHeight};
|
|
5056
|
+
const toolbar = container?.dataset.toolbar || 'full';
|
|
5057
|
+
|
|
5058
|
+
tinymce.init({
|
|
5059
|
+
selector: '#' + textarea.id,
|
|
5060
|
+
skin: '${skin}',
|
|
5061
|
+
content_css: '${contentCss}',
|
|
5062
|
+
height: parseInt(height),
|
|
5063
|
+
menubar: false,
|
|
5064
|
+
plugins: [
|
|
5065
|
+
'advlist', 'autolink', 'lists', 'link', 'image', 'charmap', 'preview',
|
|
5066
|
+
'anchor', 'searchreplace', 'visualblocks', 'code', 'fullscreen',
|
|
5067
|
+
'insertdatetime', 'media', 'table', 'help', 'wordcount'
|
|
5068
|
+
],
|
|
5069
|
+
toolbar: toolbar === 'simple'
|
|
5070
|
+
? 'bold italic underline | bullist numlist | link'
|
|
5071
|
+
: toolbar === 'minimal'
|
|
4031
5072
|
? 'bold italic | link'
|
|
4032
5073
|
: 'undo redo | blocks | bold italic forecolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | removeformat | help',
|
|
4033
5074
|
content_style: 'body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; font-size: 14px }'
|
|
@@ -4252,7 +5293,7 @@ function getQuillCDN(version = "2.0.2") {
|
|
|
4252
5293
|
`;
|
|
4253
5294
|
}
|
|
4254
5295
|
function createQuillEditorPlugin() {
|
|
4255
|
-
const builder3 =
|
|
5296
|
+
const builder3 = chunkYHW27CBV_cjs.PluginBuilder.create({
|
|
4256
5297
|
name: "quill-editor",
|
|
4257
5298
|
version: "1.0.0",
|
|
4258
5299
|
description: "Quill rich text editor integration for SonicJS"
|
|
@@ -4278,7 +5319,7 @@ function createQuillEditorPlugin() {
|
|
|
4278
5319
|
createQuillEditorPlugin();
|
|
4279
5320
|
|
|
4280
5321
|
// src/plugins/available/easy-mdx/index.ts
|
|
4281
|
-
var builder2 =
|
|
5322
|
+
var builder2 = chunkYHW27CBV_cjs.PluginBuilder.create({
|
|
4282
5323
|
name: "easy-mdx",
|
|
4283
5324
|
version: "1.0.0",
|
|
4284
5325
|
description: "Lightweight markdown editor with live preview"
|
|
@@ -4569,8 +5610,8 @@ function renderContentFormPage(data) {
|
|
|
4569
5610
|
<!-- Form Content -->
|
|
4570
5611
|
<div class="px-6 py-6">
|
|
4571
5612
|
<div id="form-messages">
|
|
4572
|
-
${data.error ?
|
|
4573
|
-
${data.success ?
|
|
5613
|
+
${data.error ? chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
5614
|
+
${data.success ? chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
4574
5615
|
</div>
|
|
4575
5616
|
|
|
4576
5617
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
@@ -4805,7 +5846,7 @@ function renderContentFormPage(data) {
|
|
|
4805
5846
|
</div>
|
|
4806
5847
|
|
|
4807
5848
|
<!-- Confirmation Dialogs -->
|
|
4808
|
-
${
|
|
5849
|
+
${chunkBZC4FYW7_cjs.renderConfirmationDialog({
|
|
4809
5850
|
id: "duplicate-content-confirm",
|
|
4810
5851
|
title: "Duplicate Content",
|
|
4811
5852
|
message: "Create a copy of this content?",
|
|
@@ -4816,7 +5857,7 @@ function renderContentFormPage(data) {
|
|
|
4816
5857
|
onConfirm: "performDuplicateContent()"
|
|
4817
5858
|
})}
|
|
4818
5859
|
|
|
4819
|
-
${
|
|
5860
|
+
${chunkBZC4FYW7_cjs.renderConfirmationDialog({
|
|
4820
5861
|
id: "delete-content-confirm",
|
|
4821
5862
|
title: "Delete Content",
|
|
4822
5863
|
message: "Are you sure you want to delete this content? This action cannot be undone.",
|
|
@@ -4827,7 +5868,7 @@ function renderContentFormPage(data) {
|
|
|
4827
5868
|
onConfirm: `performDeleteContent('${data.id}')`
|
|
4828
5869
|
})}
|
|
4829
5870
|
|
|
4830
|
-
${
|
|
5871
|
+
${chunkBZC4FYW7_cjs.getConfirmationDialogScript()}
|
|
4831
5872
|
|
|
4832
5873
|
${data.tinymceEnabled ? getTinyMCEScript(data.tinymceSettings?.apiKey) : "<!-- TinyMCE plugin not active -->"}
|
|
4833
5874
|
|
|
@@ -4898,86 +5939,386 @@ function renderContentFormPage(data) {
|
|
|
4898
5939
|
currentMediaFieldId = null;
|
|
4899
5940
|
}
|
|
4900
5941
|
|
|
4901
|
-
function cancelMediaSelection(fieldId, originalValue) {
|
|
4902
|
-
// Restore original value
|
|
4903
|
-
const hiddenInput = document.getElementById(fieldId);
|
|
4904
|
-
if (hiddenInput) {
|
|
4905
|
-
hiddenInput.value = originalValue;
|
|
4906
|
-
}
|
|
5942
|
+
function cancelMediaSelection(fieldId, originalValue) {
|
|
5943
|
+
// Restore original value
|
|
5944
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
5945
|
+
if (hiddenInput) {
|
|
5946
|
+
hiddenInput.value = originalValue;
|
|
5947
|
+
}
|
|
5948
|
+
|
|
5949
|
+
// If original value was empty, hide the preview and show select button
|
|
5950
|
+
if (!originalValue) {
|
|
5951
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
5952
|
+
if (preview) {
|
|
5953
|
+
preview.classList.add('hidden');
|
|
5954
|
+
}
|
|
5955
|
+
}
|
|
5956
|
+
|
|
5957
|
+
// Close modal
|
|
5958
|
+
closeMediaSelector();
|
|
5959
|
+
}
|
|
5960
|
+
|
|
5961
|
+
function clearMediaField(fieldId) {
|
|
5962
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
5963
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
5964
|
+
|
|
5965
|
+
if (hiddenInput) {
|
|
5966
|
+
hiddenInput.value = '';
|
|
5967
|
+
}
|
|
5968
|
+
|
|
5969
|
+
if (preview) {
|
|
5970
|
+
// Clear all children if it's a grid, or hide it
|
|
5971
|
+
if (preview.classList.contains('media-preview-grid')) {
|
|
5972
|
+
preview.innerHTML = '';
|
|
5973
|
+
}
|
|
5974
|
+
preview.classList.add('hidden');
|
|
5975
|
+
}
|
|
5976
|
+
}
|
|
5977
|
+
|
|
5978
|
+
// Global function to remove a single media from multiple selection
|
|
5979
|
+
window.removeMediaFromMultiple = function(fieldId, urlToRemove) {
|
|
5980
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
5981
|
+
if (!hiddenInput) return;
|
|
5982
|
+
|
|
5983
|
+
const values = hiddenInput.value.split(',').filter(url => url !== urlToRemove);
|
|
5984
|
+
hiddenInput.value = values.join(',');
|
|
5985
|
+
|
|
5986
|
+
// Remove preview item
|
|
5987
|
+
const previewItem = document.querySelector(\`[data-url="\${urlToRemove}"]\`);
|
|
5988
|
+
if (previewItem) {
|
|
5989
|
+
previewItem.remove();
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5992
|
+
// Hide preview grid if empty
|
|
5993
|
+
if (values.length === 0) {
|
|
5994
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
5995
|
+
if (preview) {
|
|
5996
|
+
preview.classList.add('hidden');
|
|
5997
|
+
}
|
|
5998
|
+
}
|
|
5999
|
+
};
|
|
6000
|
+
|
|
6001
|
+
// Global function called by media selector buttons
|
|
6002
|
+
window.selectMediaFile = function(mediaId, mediaUrl, filename) {
|
|
6003
|
+
if (!currentMediaFieldId) {
|
|
6004
|
+
console.error('No field ID set for media selection');
|
|
6005
|
+
return;
|
|
6006
|
+
}
|
|
6007
|
+
|
|
6008
|
+
const fieldId = currentMediaFieldId;
|
|
6009
|
+
|
|
6010
|
+
// Set the hidden input value to the media URL (not ID)
|
|
6011
|
+
const hiddenInput = document.getElementById(fieldId);
|
|
6012
|
+
if (hiddenInput) {
|
|
6013
|
+
hiddenInput.value = mediaUrl;
|
|
6014
|
+
}
|
|
6015
|
+
|
|
6016
|
+
// Update the preview
|
|
6017
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
6018
|
+
if (preview) {
|
|
6019
|
+
preview.innerHTML = \`<img src="\${mediaUrl}" alt="\${filename}" class="w-32 h-32 object-cover rounded-lg border border-white/20">\`;
|
|
6020
|
+
preview.classList.remove('hidden');
|
|
6021
|
+
}
|
|
6022
|
+
|
|
6023
|
+
// Show the remove button by finding the media actions container and updating it
|
|
6024
|
+
const mediaField = hiddenInput?.closest('.media-field-container');
|
|
6025
|
+
if (mediaField) {
|
|
6026
|
+
const actionsDiv = mediaField.querySelector('.media-actions');
|
|
6027
|
+
if (actionsDiv && !actionsDiv.querySelector('button:has-text("Remove")')) {
|
|
6028
|
+
const removeBtn = document.createElement('button');
|
|
6029
|
+
removeBtn.type = 'button';
|
|
6030
|
+
removeBtn.onclick = () => clearMediaField(fieldId);
|
|
6031
|
+
removeBtn.className = 'inline-flex items-center px-4 py-2 bg-red-600 text-white rounded-xl hover:bg-red-700 transition-all';
|
|
6032
|
+
removeBtn.textContent = 'Remove';
|
|
6033
|
+
actionsDiv.appendChild(removeBtn);
|
|
6034
|
+
}
|
|
6035
|
+
}
|
|
6036
|
+
|
|
6037
|
+
// DON'T close the modal - let user click OK button
|
|
6038
|
+
// Visual feedback: highlight the selected item
|
|
6039
|
+
document.querySelectorAll('#media-selector-grid [data-media-id]').forEach(el => {
|
|
6040
|
+
el.classList.remove('ring-2', 'ring-lime-500', 'dark:ring-lime-400');
|
|
6041
|
+
});
|
|
6042
|
+
const selectedItem = document.querySelector(\`#media-selector-grid [data-media-id="\${mediaId}"]\`);
|
|
6043
|
+
if (selectedItem) {
|
|
6044
|
+
selectedItem.classList.add('ring-2', 'ring-lime-500', 'dark:ring-lime-400');
|
|
6045
|
+
}
|
|
6046
|
+
};
|
|
6047
|
+
|
|
6048
|
+
function setMediaField(fieldId, mediaUrl) {
|
|
6049
|
+
document.getElementById(fieldId).value = mediaUrl;
|
|
6050
|
+
const preview = document.getElementById(fieldId + '-preview');
|
|
6051
|
+
preview.innerHTML = \`<img src="\${mediaUrl}" alt="Selected media" class="w-32 h-32 object-cover rounded-lg ring-1 ring-zinc-950/10 dark:ring-white/10">\`;
|
|
6052
|
+
preview.classList.remove('hidden');
|
|
6053
|
+
|
|
6054
|
+
// Close modal
|
|
6055
|
+
document.querySelector('.fixed.inset-0')?.remove();
|
|
6056
|
+
}
|
|
6057
|
+
|
|
6058
|
+
// Reference field functions
|
|
6059
|
+
let currentReferenceFieldId = null;
|
|
6060
|
+
let referenceSearchTimeout = null;
|
|
6061
|
+
|
|
6062
|
+
function getReferenceContainer(fieldId) {
|
|
6063
|
+
const input = document.getElementById(fieldId);
|
|
6064
|
+
return input ? input.closest('[data-reference-field]') : null;
|
|
6065
|
+
}
|
|
6066
|
+
|
|
6067
|
+
function getReferenceCollections(container) {
|
|
6068
|
+
if (!container) return [];
|
|
6069
|
+
const rawCollections = container.dataset.referenceCollections || '';
|
|
6070
|
+
const collections = rawCollections
|
|
6071
|
+
.split(',')
|
|
6072
|
+
.map((value) => value.trim())
|
|
6073
|
+
.filter(Boolean);
|
|
6074
|
+
if (collections.length > 0) {
|
|
6075
|
+
return collections;
|
|
6076
|
+
}
|
|
6077
|
+
const singleCollection = container.dataset.referenceCollection;
|
|
6078
|
+
return singleCollection ? [singleCollection] : [];
|
|
6079
|
+
}
|
|
6080
|
+
|
|
6081
|
+
async function fetchReferenceItems(collections, search = '', limit = 20) {
|
|
6082
|
+
const params = new URLSearchParams({ limit: String(limit) });
|
|
6083
|
+
collections.forEach((collection) => params.append('collection', collection));
|
|
6084
|
+
if (search) {
|
|
6085
|
+
params.set('search', search);
|
|
6086
|
+
}
|
|
6087
|
+
const response = await fetch('/admin/api/references?' + params.toString());
|
|
6088
|
+
if (!response.ok) {
|
|
6089
|
+
throw new Error('Failed to load references');
|
|
6090
|
+
}
|
|
6091
|
+
const data = await response.json();
|
|
6092
|
+
return data?.data || [];
|
|
6093
|
+
}
|
|
6094
|
+
|
|
6095
|
+
async function fetchReferenceById(collections, id) {
|
|
6096
|
+
if (!id) return null;
|
|
6097
|
+
const params = new URLSearchParams({ id });
|
|
6098
|
+
collections.forEach((collection) => params.append('collection', collection));
|
|
6099
|
+
const response = await fetch('/admin/api/references?' + params.toString());
|
|
6100
|
+
if (!response.ok) {
|
|
6101
|
+
return null;
|
|
6102
|
+
}
|
|
6103
|
+
const data = await response.json();
|
|
6104
|
+
return data?.data || null;
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
function renderReferenceDisplay(container, item, fallbackMessage = 'No reference selected.') {
|
|
6108
|
+
const display = container.querySelector('[data-reference-display]');
|
|
6109
|
+
const removeButton = container.querySelector('[data-reference-clear]');
|
|
6110
|
+
if (!display) return;
|
|
6111
|
+
|
|
6112
|
+
display.innerHTML = '';
|
|
6113
|
+
|
|
6114
|
+
if (!item) {
|
|
6115
|
+
display.textContent = fallbackMessage;
|
|
6116
|
+
if (removeButton) {
|
|
6117
|
+
removeButton.disabled = true;
|
|
6118
|
+
}
|
|
6119
|
+
return;
|
|
6120
|
+
}
|
|
6121
|
+
|
|
6122
|
+
const title = item.title || item.slug || item.id || 'Untitled';
|
|
6123
|
+
const titleEl = document.createElement('div');
|
|
6124
|
+
titleEl.className = 'font-medium text-zinc-900 dark:text-white';
|
|
6125
|
+
titleEl.textContent = title;
|
|
6126
|
+
|
|
6127
|
+
display.appendChild(titleEl);
|
|
6128
|
+
|
|
6129
|
+
const metaRow = document.createElement('div');
|
|
6130
|
+
metaRow.className = 'mt-1 flex flex-wrap items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400';
|
|
6131
|
+
|
|
6132
|
+
if (item.collection?.display_name || item.collection?.name) {
|
|
6133
|
+
const collectionLabel = document.createElement('span');
|
|
6134
|
+
collectionLabel.className = 'inline-flex items-center rounded-full bg-zinc-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-zinc-600 dark:bg-white/10 dark:text-zinc-200';
|
|
6135
|
+
collectionLabel.textContent = item.collection.display_name || item.collection.name;
|
|
6136
|
+
metaRow.appendChild(collectionLabel);
|
|
6137
|
+
}
|
|
6138
|
+
|
|
6139
|
+
if (item.slug) {
|
|
6140
|
+
const slugEl = document.createElement('span');
|
|
6141
|
+
slugEl.textContent = item.slug;
|
|
6142
|
+
metaRow.appendChild(slugEl);
|
|
6143
|
+
}
|
|
6144
|
+
|
|
6145
|
+
if (metaRow.childElementCount > 0) {
|
|
6146
|
+
display.appendChild(metaRow);
|
|
6147
|
+
}
|
|
6148
|
+
|
|
6149
|
+
if (removeButton) {
|
|
6150
|
+
removeButton.disabled = false;
|
|
6151
|
+
}
|
|
6152
|
+
}
|
|
6153
|
+
|
|
6154
|
+
function updateReferenceField(fieldId, item) {
|
|
6155
|
+
const input = document.getElementById(fieldId);
|
|
6156
|
+
const container = getReferenceContainer(fieldId);
|
|
6157
|
+
if (!input || !container) return;
|
|
6158
|
+
|
|
6159
|
+
input.value = item?.id || '';
|
|
6160
|
+
renderReferenceDisplay(container, item, 'No reference selected.');
|
|
6161
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
6162
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
6163
|
+
}
|
|
6164
|
+
|
|
6165
|
+
function clearReferenceField(fieldId) {
|
|
6166
|
+
updateReferenceField(fieldId, null);
|
|
6167
|
+
}
|
|
6168
|
+
|
|
6169
|
+
function closeReferenceSelector() {
|
|
6170
|
+
const modal = document.getElementById('reference-selector-modal');
|
|
6171
|
+
if (modal) {
|
|
6172
|
+
modal.remove();
|
|
6173
|
+
}
|
|
6174
|
+
currentReferenceFieldId = null;
|
|
6175
|
+
}
|
|
6176
|
+
|
|
6177
|
+
function openReferenceSelector(fieldId) {
|
|
6178
|
+
const container = getReferenceContainer(fieldId);
|
|
6179
|
+
const collections = getReferenceCollections(container);
|
|
6180
|
+
if (!container || collections.length === 0) {
|
|
6181
|
+
console.error('Reference collection is missing for field', fieldId);
|
|
6182
|
+
return;
|
|
6183
|
+
}
|
|
6184
|
+
|
|
6185
|
+
currentReferenceFieldId = fieldId;
|
|
6186
|
+
|
|
6187
|
+
const modal = document.createElement('div');
|
|
6188
|
+
modal.className = 'fixed inset-0 bg-black/50 backdrop-blur-sm flex items-center justify-center z-50';
|
|
6189
|
+
modal.id = 'reference-selector-modal';
|
|
6190
|
+
modal.innerHTML = \`
|
|
6191
|
+
<div class="rounded-xl bg-white dark:bg-zinc-900 shadow-xl ring-1 ring-zinc-950/5 dark:ring-white/10 p-6 w-full max-w-3xl max-h-[90vh] overflow-y-auto">
|
|
6192
|
+
<div class="flex items-center justify-between gap-3">
|
|
6193
|
+
<h3 class="text-lg font-semibold text-zinc-950 dark:text-white">Select Reference</h3>
|
|
6194
|
+
<button
|
|
6195
|
+
type="button"
|
|
6196
|
+
onclick="closeReferenceSelector()"
|
|
6197
|
+
class="rounded-md text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
6198
|
+
aria-label="Close"
|
|
6199
|
+
>
|
|
6200
|
+
\u2715
|
|
6201
|
+
</button>
|
|
6202
|
+
</div>
|
|
6203
|
+
<div class="mt-4">
|
|
6204
|
+
<input
|
|
6205
|
+
type="search"
|
|
6206
|
+
id="reference-search-input"
|
|
6207
|
+
placeholder="Search by title or slug..."
|
|
6208
|
+
class="w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 shadow-sm focus:border-cyan-500 focus:ring-cyan-500 dark:border-white/10 dark:bg-zinc-900 dark:text-white"
|
|
6209
|
+
>
|
|
6210
|
+
</div>
|
|
6211
|
+
<div id="reference-results" class="mt-4 space-y-2"></div>
|
|
6212
|
+
<div class="mt-4 flex justify-end">
|
|
6213
|
+
<button
|
|
6214
|
+
type="button"
|
|
6215
|
+
onclick="closeReferenceSelector()"
|
|
6216
|
+
class="rounded-lg bg-zinc-950 dark:bg-white px-4 py-2 text-sm font-semibold text-white dark:text-zinc-950 hover:bg-zinc-800 dark:hover:bg-zinc-100 transition-colors"
|
|
6217
|
+
>
|
|
6218
|
+
Close
|
|
6219
|
+
</button>
|
|
6220
|
+
</div>
|
|
6221
|
+
</div>
|
|
6222
|
+
\`;
|
|
6223
|
+
|
|
6224
|
+
document.body.appendChild(modal);
|
|
4907
6225
|
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
6226
|
+
const resultsContainer = modal.querySelector('#reference-results');
|
|
6227
|
+
const searchInput = modal.querySelector('#reference-search-input');
|
|
6228
|
+
|
|
6229
|
+
const renderResults = (items) => {
|
|
6230
|
+
resultsContainer.innerHTML = '';
|
|
6231
|
+
if (!items || items.length === 0) {
|
|
6232
|
+
resultsContainer.innerHTML = '<div class="rounded-lg border border-dashed border-zinc-200 p-4 text-sm text-zinc-500 dark:border-white/10 dark:text-zinc-400">No items found.</div>';
|
|
6233
|
+
return;
|
|
4913
6234
|
}
|
|
4914
|
-
}
|
|
4915
6235
|
|
|
4916
|
-
|
|
4917
|
-
closeMediaSelector();
|
|
4918
|
-
}
|
|
6236
|
+
const selectedId = document.getElementById(fieldId)?.value;
|
|
4919
6237
|
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
|
|
4923
|
-
|
|
6238
|
+
items.forEach((item) => {
|
|
6239
|
+
const button = document.createElement('button');
|
|
6240
|
+
button.type = 'button';
|
|
6241
|
+
button.className = 'w-full text-left rounded-lg border border-zinc-200 px-4 py-3 text-sm text-zinc-700 hover:bg-zinc-50 dark:border-white/10 dark:text-zinc-200 dark:hover:bg-white/5';
|
|
6242
|
+
if (item.id === selectedId) {
|
|
6243
|
+
button.classList.add('ring-2', 'ring-cyan-500', 'dark:ring-cyan-400');
|
|
6244
|
+
}
|
|
4924
6245
|
|
|
4925
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
return;
|
|
4930
|
-
}
|
|
6246
|
+
const title = item.title || item.slug || item.id || 'Untitled';
|
|
6247
|
+
const titleEl = document.createElement('div');
|
|
6248
|
+
titleEl.className = 'font-medium text-zinc-900 dark:text-white';
|
|
6249
|
+
titleEl.textContent = title;
|
|
4931
6250
|
|
|
4932
|
-
|
|
6251
|
+
button.appendChild(titleEl);
|
|
4933
6252
|
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
if (hiddenInput) {
|
|
4937
|
-
hiddenInput.value = mediaUrl;
|
|
4938
|
-
}
|
|
6253
|
+
const metaRow = document.createElement('div');
|
|
6254
|
+
metaRow.className = 'mt-1 flex flex-wrap items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400';
|
|
4939
6255
|
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
4944
|
-
|
|
4945
|
-
|
|
6256
|
+
if (item.collection?.display_name || item.collection?.name) {
|
|
6257
|
+
const collectionLabel = document.createElement('span');
|
|
6258
|
+
collectionLabel.className = 'inline-flex items-center rounded-full bg-zinc-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-zinc-600 dark:bg-white/10 dark:text-zinc-200';
|
|
6259
|
+
collectionLabel.textContent = item.collection.display_name || item.collection.name;
|
|
6260
|
+
metaRow.appendChild(collectionLabel);
|
|
6261
|
+
}
|
|
4946
6262
|
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
4953
|
-
|
|
4954
|
-
|
|
4955
|
-
|
|
4956
|
-
|
|
4957
|
-
|
|
6263
|
+
if (item.slug) {
|
|
6264
|
+
const slugEl = document.createElement('span');
|
|
6265
|
+
slugEl.textContent = item.slug;
|
|
6266
|
+
metaRow.appendChild(slugEl);
|
|
6267
|
+
}
|
|
6268
|
+
|
|
6269
|
+
if (metaRow.childElementCount > 0) {
|
|
6270
|
+
button.appendChild(metaRow);
|
|
6271
|
+
}
|
|
6272
|
+
|
|
6273
|
+
button.addEventListener('click', () => {
|
|
6274
|
+
updateReferenceField(fieldId, item);
|
|
6275
|
+
closeReferenceSelector();
|
|
6276
|
+
});
|
|
6277
|
+
|
|
6278
|
+
resultsContainer.appendChild(button);
|
|
6279
|
+
});
|
|
6280
|
+
};
|
|
6281
|
+
|
|
6282
|
+
const loadResults = async (searchValue = '') => {
|
|
6283
|
+
try {
|
|
6284
|
+
const items = await fetchReferenceItems(collections, searchValue);
|
|
6285
|
+
renderResults(items);
|
|
6286
|
+
} catch (error) {
|
|
6287
|
+
resultsContainer.innerHTML = '<div class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200">Failed to load references.</div>';
|
|
4958
6288
|
}
|
|
4959
|
-
}
|
|
6289
|
+
};
|
|
4960
6290
|
|
|
4961
|
-
|
|
4962
|
-
|
|
4963
|
-
|
|
4964
|
-
|
|
6291
|
+
loadResults();
|
|
6292
|
+
|
|
6293
|
+
searchInput.addEventListener('input', () => {
|
|
6294
|
+
if (referenceSearchTimeout) {
|
|
6295
|
+
clearTimeout(referenceSearchTimeout);
|
|
6296
|
+
}
|
|
6297
|
+
referenceSearchTimeout = setTimeout(() => {
|
|
6298
|
+
loadResults(searchInput.value.trim());
|
|
6299
|
+
}, 250);
|
|
4965
6300
|
});
|
|
4966
|
-
|
|
4967
|
-
if (selectedItem) {
|
|
4968
|
-
selectedItem.classList.add('ring-2', 'ring-lime-500', 'dark:ring-lime-400');
|
|
4969
|
-
}
|
|
4970
|
-
};
|
|
6301
|
+
}
|
|
4971
6302
|
|
|
4972
|
-
|
|
4973
|
-
document.
|
|
4974
|
-
|
|
4975
|
-
|
|
4976
|
-
|
|
6303
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
6304
|
+
document.querySelectorAll('[data-reference-field]').forEach(async (container) => {
|
|
6305
|
+
const input = container.querySelector('input[type="hidden"]');
|
|
6306
|
+
const collections = getReferenceCollections(container);
|
|
6307
|
+
if (!input || collections.length === 0) return;
|
|
4977
6308
|
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
6309
|
+
if (!input.value) {
|
|
6310
|
+
renderReferenceDisplay(container, null, 'No reference selected.');
|
|
6311
|
+
return;
|
|
6312
|
+
}
|
|
6313
|
+
|
|
6314
|
+
const item = await fetchReferenceById(collections, input.value);
|
|
6315
|
+
if (item) {
|
|
6316
|
+
renderReferenceDisplay(container, item);
|
|
6317
|
+
} else {
|
|
6318
|
+
renderReferenceDisplay(container, null, 'Reference not found.');
|
|
6319
|
+
}
|
|
6320
|
+
});
|
|
6321
|
+
});
|
|
4981
6322
|
|
|
4982
6323
|
// Custom select options
|
|
4983
6324
|
function addCustomOption(input, selectId) {
|
|
@@ -5132,11 +6473,11 @@ function renderContentFormPage(data) {
|
|
|
5132
6473
|
content: pageContent,
|
|
5133
6474
|
version: data.version
|
|
5134
6475
|
};
|
|
5135
|
-
return
|
|
6476
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
5136
6477
|
}
|
|
5137
6478
|
|
|
5138
6479
|
// src/templates/pages/admin-content-list.template.ts
|
|
5139
|
-
|
|
6480
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
5140
6481
|
function renderContentListPage(data) {
|
|
5141
6482
|
const urlParams = new URLSearchParams();
|
|
5142
6483
|
if (data.modelName && data.modelName !== "all") urlParams.set("model", data.modelName);
|
|
@@ -5144,7 +6485,7 @@ function renderContentListPage(data) {
|
|
|
5144
6485
|
if (data.search) urlParams.set("search", data.search);
|
|
5145
6486
|
if (data.page && data.page !== 1) urlParams.set("page", data.page.toString());
|
|
5146
6487
|
const currentParams = urlParams.toString();
|
|
5147
|
-
|
|
6488
|
+
data.modelName !== "all" || data.status !== "all" || !!data.search;
|
|
5148
6489
|
const filterBarData = {
|
|
5149
6490
|
filters: [
|
|
5150
6491
|
{
|
|
@@ -5174,6 +6515,11 @@ function renderContentListPage(data) {
|
|
|
5174
6515
|
}
|
|
5175
6516
|
],
|
|
5176
6517
|
actions: [
|
|
6518
|
+
{
|
|
6519
|
+
label: "Advanced Search",
|
|
6520
|
+
className: "btn-primary",
|
|
6521
|
+
onclick: "openAdvancedSearch()"
|
|
6522
|
+
},
|
|
5177
6523
|
{
|
|
5178
6524
|
label: "Refresh",
|
|
5179
6525
|
className: "btn-secondary",
|
|
@@ -5325,12 +6671,57 @@ function renderContentListPage(data) {
|
|
|
5325
6671
|
<div class="relative bg-white/80 dark:bg-zinc-900/80 backdrop-blur-xl shadow-sm ring-1 ring-zinc-950/5 dark:ring-white/10 rounded-xl">
|
|
5326
6672
|
<div class="px-6 py-5">
|
|
5327
6673
|
<div class="flex items-center justify-between">
|
|
5328
|
-
<div class="flex items-center space-x-4">
|
|
5329
|
-
<!--
|
|
6674
|
+
<div class="flex items-center space-x-4 flex-1">
|
|
6675
|
+
<!-- Model Filter -->
|
|
6676
|
+
<div>
|
|
6677
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Model</label>
|
|
6678
|
+
<div class="grid grid-cols-1">
|
|
6679
|
+
<select
|
|
6680
|
+
name="model"
|
|
6681
|
+
onchange="updateContentFilters('model', this.value)"
|
|
6682
|
+
class="col-start-1 row-start-1 w-full appearance-none rounded-lg bg-white/5 dark:bg-white/5 py-2 pl-3 pr-8 text-sm text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-cyan-500/30 dark:outline-cyan-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-cyan-500 dark:focus-visible:outline-cyan-400 min-w-40"
|
|
6683
|
+
>
|
|
6684
|
+
<option value="all" ${data.modelName === "all" ? "selected" : ""}>All Models</option>
|
|
6685
|
+
${data.models.map((model) => `
|
|
6686
|
+
<option value="${model.name}" ${data.modelName === model.name ? "selected" : ""}>
|
|
6687
|
+
${model.displayName}
|
|
6688
|
+
</option>
|
|
6689
|
+
`).join("")}
|
|
6690
|
+
</select>
|
|
6691
|
+
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-cyan-600 dark:text-cyan-400 sm:size-4">
|
|
6692
|
+
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
6693
|
+
</svg>
|
|
6694
|
+
</div>
|
|
6695
|
+
</div>
|
|
6696
|
+
|
|
6697
|
+
<!-- Status Filter -->
|
|
5330
6698
|
<div>
|
|
6699
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Status</label>
|
|
6700
|
+
<div class="grid grid-cols-1">
|
|
6701
|
+
<select
|
|
6702
|
+
name="status"
|
|
6703
|
+
onchange="updateContentFilters('status', this.value)"
|
|
6704
|
+
class="col-start-1 row-start-1 w-full appearance-none rounded-lg bg-white/5 dark:bg-white/5 py-2 pl-3 pr-8 text-sm text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-cyan-500/30 dark:outline-cyan-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-cyan-500 dark:focus-visible:outline-cyan-400 min-w-40"
|
|
6705
|
+
>
|
|
6706
|
+
<option value="all" ${data.status === "all" ? "selected" : ""}>All Status</option>
|
|
6707
|
+
<option value="draft" ${data.status === "draft" ? "selected" : ""}>Draft</option>
|
|
6708
|
+
<option value="review" ${data.status === "review" ? "selected" : ""}>Under Review</option>
|
|
6709
|
+
<option value="scheduled" ${data.status === "scheduled" ? "selected" : ""}>Scheduled</option>
|
|
6710
|
+
<option value="published" ${data.status === "published" ? "selected" : ""}>Published</option>
|
|
6711
|
+
<option value="archived" ${data.status === "archived" ? "selected" : ""}>Archived</option>
|
|
6712
|
+
<option value="deleted" ${data.status === "deleted" ? "selected" : ""}>Deleted</option>
|
|
6713
|
+
</select>
|
|
6714
|
+
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-cyan-600 dark:text-cyan-400 sm:size-4">
|
|
6715
|
+
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
6716
|
+
</svg>
|
|
6717
|
+
</div>
|
|
6718
|
+
</div>
|
|
6719
|
+
|
|
6720
|
+
<!-- Search Input -->
|
|
6721
|
+
<div class="flex-1 max-w-md">
|
|
5331
6722
|
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Search</label>
|
|
5332
6723
|
<form onsubmit="performContentSearch(event)" class="flex items-center space-x-2">
|
|
5333
|
-
<div class="relative group">
|
|
6724
|
+
<div class="relative group flex-1">
|
|
5334
6725
|
<input
|
|
5335
6726
|
type="text"
|
|
5336
6727
|
name="search"
|
|
@@ -5338,7 +6729,7 @@ function renderContentListPage(data) {
|
|
|
5338
6729
|
value="${data.search || ""}"
|
|
5339
6730
|
oninput="toggleContentClearButton()"
|
|
5340
6731
|
placeholder="Search content..."
|
|
5341
|
-
class="rounded-full bg-white/90 dark:bg-zinc-800/90 backdrop-blur-sm px-4 py-2.5 pl-11 pr-10 text-sm
|
|
6732
|
+
class="w-full rounded-full bg-white/90 dark:bg-zinc-800/90 backdrop-blur-sm px-4 py-2.5 pl-11 pr-10 text-sm text-zinc-950 dark:text-white border-2 border-cyan-200/50 dark:border-cyan-700/50 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:border-cyan-500 dark:focus:border-cyan-400 focus:bg-white dark:focus:bg-zinc-800 focus:shadow-lg focus:shadow-cyan-500/20 dark:focus:shadow-cyan-400/20 transition-all duration-300"
|
|
5342
6733
|
>
|
|
5343
6734
|
<div class="absolute left-3.5 top-2.5 flex items-center justify-center w-5 h-5 rounded-full bg-gradient-to-br from-cyan-400 to-blue-500 dark:from-cyan-300 dark:to-blue-400 opacity-90 group-focus-within:opacity-100 transition-opacity">
|
|
5344
6735
|
<svg class="h-3 w-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
|
@@ -5410,57 +6801,6 @@ function renderContentListPage(data) {
|
|
|
5410
6801
|
}
|
|
5411
6802
|
</script>
|
|
5412
6803
|
</div>
|
|
5413
|
-
|
|
5414
|
-
${filterBarData.filters.map((filter) => {
|
|
5415
|
-
const selectedOption = filter.options.find((opt) => opt.selected);
|
|
5416
|
-
const selectedColor = selectedOption?.color || "cyan";
|
|
5417
|
-
const colorMap = {
|
|
5418
|
-
"cyan": "bg-cyan-400 dark:bg-cyan-400",
|
|
5419
|
-
"lime": "bg-lime-400 dark:bg-lime-400",
|
|
5420
|
-
"pink": "bg-pink-400 dark:bg-pink-400",
|
|
5421
|
-
"purple": "bg-purple-400 dark:bg-purple-400",
|
|
5422
|
-
"amber": "bg-amber-400 dark:bg-amber-400",
|
|
5423
|
-
"zinc": "bg-zinc-400 dark:bg-zinc-400"
|
|
5424
|
-
};
|
|
5425
|
-
return `
|
|
5426
|
-
<div>
|
|
5427
|
-
<label class="block text-sm/6 font-medium text-zinc-950 dark:text-white">${filter.label}</label>
|
|
5428
|
-
<div class="mt-2 grid grid-cols-1">
|
|
5429
|
-
<div class="col-start-1 row-start-1 flex items-center gap-3 pl-3 pr-8 pointer-events-none">
|
|
5430
|
-
${filter.name === "status" ? `<span class="inline-block size-2 shrink-0 rounded-full border border-transparent ${colorMap[selectedColor]}"></span>` : ""}
|
|
5431
|
-
</div>
|
|
5432
|
-
<select
|
|
5433
|
-
name="${filter.name}"
|
|
5434
|
-
onchange="updateContentFilters('${filter.name}', this.value)"
|
|
5435
|
-
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 ${filter.name === "status" ? "pl-8" : "pl-3"} pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-cyan-500/30 dark:outline-cyan-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-cyan-500 dark:focus-visible:outline-cyan-400 sm:text-sm/6 min-w-48"
|
|
5436
|
-
>
|
|
5437
|
-
${filter.options.map((opt) => `
|
|
5438
|
-
<option value="${opt.value}" ${opt.selected ? "selected" : ""}>${opt.label}</option>
|
|
5439
|
-
`).join("")}
|
|
5440
|
-
</select>
|
|
5441
|
-
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-cyan-600 dark:text-cyan-400 sm:size-4">
|
|
5442
|
-
<path d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" fill-rule="evenodd" />
|
|
5443
|
-
</svg>
|
|
5444
|
-
</div>
|
|
5445
|
-
</div>
|
|
5446
|
-
`;
|
|
5447
|
-
}).join("")}
|
|
5448
|
-
|
|
5449
|
-
<!-- Clear Filters Button -->
|
|
5450
|
-
${hasActiveFilters ? `
|
|
5451
|
-
<div>
|
|
5452
|
-
<label class="block text-sm/6 font-medium text-zinc-950 dark:text-white mb-2"> </label>
|
|
5453
|
-
<button
|
|
5454
|
-
onclick="clearAllFilters()"
|
|
5455
|
-
class="inline-flex items-center gap-x-1.5 px-3 py-2 bg-pink-50 dark:bg-pink-500/10 text-pink-700 dark:text-pink-300 text-sm font-medium rounded-md ring-1 ring-inset ring-pink-600/20 dark:ring-pink-500/20 hover:bg-pink-100 dark:hover:bg-pink-500/20 transition-colors"
|
|
5456
|
-
>
|
|
5457
|
-
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2">
|
|
5458
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12"/>
|
|
5459
|
-
</svg>
|
|
5460
|
-
Clear Filters
|
|
5461
|
-
</button>
|
|
5462
|
-
</div>
|
|
5463
|
-
` : ""}
|
|
5464
6804
|
</div>
|
|
5465
6805
|
<div class="flex items-center gap-x-3">
|
|
5466
6806
|
<span class="text-sm/6 font-medium text-zinc-700 dark:text-zinc-300 px-3 py-1.5 rounded-full bg-white/60 dark:bg-zinc-800/60 backdrop-blur-sm">${data.totalItems} ${data.totalItems === 1 ? "item" : "items"}</span>
|
|
@@ -5541,8 +6881,8 @@ function renderContentListPage(data) {
|
|
|
5541
6881
|
|
|
5542
6882
|
<!-- Content List -->
|
|
5543
6883
|
<div id="content-list">
|
|
5544
|
-
${
|
|
5545
|
-
${
|
|
6884
|
+
${chunkBZC4FYW7_cjs.renderTable(tableData)}
|
|
6885
|
+
${chunkBZC4FYW7_cjs.renderPagination(paginationData)}
|
|
5546
6886
|
</div>
|
|
5547
6887
|
|
|
5548
6888
|
</div>
|
|
@@ -5721,49 +7061,335 @@ function renderContentListPage(data) {
|
|
|
5721
7061
|
} else {
|
|
5722
7062
|
alert('Error: ' + (data.error || 'Unknown error'));
|
|
5723
7063
|
}
|
|
5724
|
-
})
|
|
5725
|
-
.catch(err => {
|
|
5726
|
-
console.error('Bulk action error:', err);
|
|
5727
|
-
alert('Failed to perform bulk action');
|
|
5728
|
-
})
|
|
5729
|
-
.finally(() => {
|
|
5730
|
-
// Clear context
|
|
5731
|
-
currentBulkAction = null;
|
|
5732
|
-
currentSelectedIds = [];
|
|
7064
|
+
})
|
|
7065
|
+
.catch(err => {
|
|
7066
|
+
console.error('Bulk action error:', err);
|
|
7067
|
+
alert('Failed to perform bulk action');
|
|
7068
|
+
})
|
|
7069
|
+
.finally(() => {
|
|
7070
|
+
// Clear context
|
|
7071
|
+
currentBulkAction = null;
|
|
7072
|
+
currentSelectedIds = [];
|
|
7073
|
+
});
|
|
7074
|
+
}
|
|
7075
|
+
|
|
7076
|
+
// Helper to get action text for display
|
|
7077
|
+
function getActionText(action) {
|
|
7078
|
+
const actionCount = currentSelectedIds.length;
|
|
7079
|
+
switch(action) {
|
|
7080
|
+
case 'publish':
|
|
7081
|
+
return \`publish \${actionCount} item\${actionCount > 1 ? 's' : ''}\`;
|
|
7082
|
+
case 'draft':
|
|
7083
|
+
return \`move \${actionCount} item\${actionCount > 1 ? 's' : ''} to draft\`;
|
|
7084
|
+
case 'delete':
|
|
7085
|
+
return \`delete \${actionCount} item\${actionCount > 1 ? 's' : ''}\`;
|
|
7086
|
+
default:
|
|
7087
|
+
return \`perform action on \${actionCount} item\${actionCount > 1 ? 's' : ''}\`;
|
|
7088
|
+
}
|
|
7089
|
+
}
|
|
7090
|
+
|
|
7091
|
+
</script>
|
|
7092
|
+
|
|
7093
|
+
<!-- Confirmation Dialog for Bulk Actions -->
|
|
7094
|
+
${chunkBZC4FYW7_cjs.renderConfirmationDialog({
|
|
7095
|
+
id: "bulk-action-confirm",
|
|
7096
|
+
title: "Confirm Bulk Action",
|
|
7097
|
+
message: "Are you sure you want to perform this action? This operation will affect multiple items.",
|
|
7098
|
+
confirmText: "Confirm",
|
|
7099
|
+
cancelText: "Cancel",
|
|
7100
|
+
confirmClass: "bg-blue-500 hover:bg-blue-400",
|
|
7101
|
+
iconColor: "blue",
|
|
7102
|
+
onConfirm: "executeBulkAction()"
|
|
7103
|
+
})}
|
|
7104
|
+
|
|
7105
|
+
<!-- Confirmation Dialog Script -->
|
|
7106
|
+
${chunkBZC4FYW7_cjs.getConfirmationDialogScript()}
|
|
7107
|
+
|
|
7108
|
+
<!-- Advanced Search Modal -->
|
|
7109
|
+
<div id="advancedSearchModal" class="hidden fixed inset-0 z-50 overflow-y-auto" aria-labelledby="modal-title" role="dialog" aria-modal="true">
|
|
7110
|
+
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
|
|
7111
|
+
<!-- Background overlay -->
|
|
7112
|
+
<div class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" onclick="closeAdvancedSearch()"></div>
|
|
7113
|
+
|
|
7114
|
+
<!-- Modal panel -->
|
|
7115
|
+
<div class="inline-block align-bottom bg-white dark:bg-zinc-900 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-4xl sm:w-full">
|
|
7116
|
+
<div class="bg-white dark:bg-zinc-900 px-4 pt-5 pb-4 sm:p-6">
|
|
7117
|
+
<!-- Header -->
|
|
7118
|
+
<div class="flex items-center justify-between mb-4">
|
|
7119
|
+
<h3 class="text-lg font-semibold text-zinc-950 dark:text-white" id="modal-title">
|
|
7120
|
+
\u{1F50D} Advanced Search
|
|
7121
|
+
</h3>
|
|
7122
|
+
<button onclick="closeAdvancedSearch()" class="text-zinc-400 hover:text-zinc-500 dark:hover:text-zinc-300">
|
|
7123
|
+
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
7124
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
|
7125
|
+
</svg>
|
|
7126
|
+
</button>
|
|
7127
|
+
</div>
|
|
7128
|
+
|
|
7129
|
+
<!-- Search Form -->
|
|
7130
|
+
<form id="advancedSearchForm" class="space-y-4">
|
|
7131
|
+
<!-- Search Input -->
|
|
7132
|
+
<div>
|
|
7133
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Search Query</label>
|
|
7134
|
+
<div class="relative">
|
|
7135
|
+
<input
|
|
7136
|
+
type="text"
|
|
7137
|
+
id="searchQuery"
|
|
7138
|
+
name="query"
|
|
7139
|
+
placeholder="Enter your search query..."
|
|
7140
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-4 py-3 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 focus:ring-2 focus:ring-indigo-500"
|
|
7141
|
+
autocomplete="off"
|
|
7142
|
+
/>
|
|
7143
|
+
<div id="searchSuggestions" class="hidden absolute z-10 w-full mt-1 bg-white dark:bg-zinc-800 rounded-lg shadow-lg border border-zinc-200 dark:border-zinc-700 max-h-60 overflow-y-auto"></div>
|
|
7144
|
+
</div>
|
|
7145
|
+
</div>
|
|
7146
|
+
|
|
7147
|
+
<!-- Mode Toggle -->
|
|
7148
|
+
<div>
|
|
7149
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Search Mode</label>
|
|
7150
|
+
<div class="flex gap-4">
|
|
7151
|
+
<label class="flex items-center">
|
|
7152
|
+
<input type="radio" name="mode" value="ai" checked class="mr-2">
|
|
7153
|
+
<span class="text-sm text-zinc-950 dark:text-white">\u{1F916} AI Search (Semantic)</span>
|
|
7154
|
+
</label>
|
|
7155
|
+
<label class="flex items-center">
|
|
7156
|
+
<input type="radio" name="mode" value="keyword" class="mr-2">
|
|
7157
|
+
<span class="text-sm text-zinc-950 dark:text-white">\u{1F524} Keyword Search</span>
|
|
7158
|
+
</label>
|
|
7159
|
+
</div>
|
|
7160
|
+
</div>
|
|
7161
|
+
|
|
7162
|
+
<!-- Filters -->
|
|
7163
|
+
<div class="border-t border-zinc-200 dark:border-zinc-800 pt-4">
|
|
7164
|
+
<h4 class="text-sm font-semibold text-zinc-950 dark:text-white mb-3">Filters</h4>
|
|
7165
|
+
|
|
7166
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
7167
|
+
<!-- Collection Filter -->
|
|
7168
|
+
<div>
|
|
7169
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Collections</label>
|
|
7170
|
+
<select
|
|
7171
|
+
id="filterCollections"
|
|
7172
|
+
name="collections"
|
|
7173
|
+
multiple
|
|
7174
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10"
|
|
7175
|
+
size="4"
|
|
7176
|
+
>
|
|
7177
|
+
<option value="">All Collections</option>
|
|
7178
|
+
${data.models.map(
|
|
7179
|
+
(model) => `
|
|
7180
|
+
<option value="${model.name}">${model.displayName}</option>
|
|
7181
|
+
`
|
|
7182
|
+
).join("")}
|
|
7183
|
+
</select>
|
|
7184
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">Hold Ctrl/Cmd to select multiple</p>
|
|
7185
|
+
</div>
|
|
7186
|
+
|
|
7187
|
+
<!-- Status Filter -->
|
|
7188
|
+
<div>
|
|
7189
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Status</label>
|
|
7190
|
+
<select
|
|
7191
|
+
id="filterStatus"
|
|
7192
|
+
name="status"
|
|
7193
|
+
multiple
|
|
7194
|
+
class="w-full rounded-lg bg-white dark:bg-white/5 px-3 py-2 text-sm text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10"
|
|
7195
|
+
size="4"
|
|
7196
|
+
>
|
|
7197
|
+
<option value="published">Published</option>
|
|
7198
|
+
<option value="draft">Draft</option>
|
|
7199
|
+
<option value="review">Under Review</option>
|
|
7200
|
+
<option value="scheduled">Scheduled</option>
|
|
7201
|
+
<option value="archived">Archived</option>
|
|
7202
|
+
</select>
|
|
7203
|
+
</div>
|
|
7204
|
+
</div>
|
|
7205
|
+
</div>
|
|
7206
|
+
|
|
7207
|
+
<!-- Actions -->
|
|
7208
|
+
<div class="flex items-center justify-end gap-3 pt-4 border-t border-zinc-200 dark:border-zinc-800">
|
|
7209
|
+
<button
|
|
7210
|
+
type="button"
|
|
7211
|
+
onclick="closeAdvancedSearch()"
|
|
7212
|
+
class="inline-flex items-center justify-center rounded-lg bg-white dark:bg-zinc-800 px-4 py-2 text-sm font-semibold text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 hover:bg-zinc-50 dark:hover:bg-zinc-700"
|
|
7213
|
+
>
|
|
7214
|
+
Cancel
|
|
7215
|
+
</button>
|
|
7216
|
+
<button
|
|
7217
|
+
type="submit"
|
|
7218
|
+
class="inline-flex items-center justify-center rounded-lg bg-indigo-600 text-white px-6 py-2.5 text-sm font-semibold hover:bg-indigo-500 shadow-sm"
|
|
7219
|
+
>
|
|
7220
|
+
Search
|
|
7221
|
+
</button>
|
|
7222
|
+
</div>
|
|
7223
|
+
</form>
|
|
7224
|
+
</div>
|
|
7225
|
+
|
|
7226
|
+
<!-- Results Area -->
|
|
7227
|
+
<div id="searchResults" class="hidden px-4 pb-4 sm:px-6">
|
|
7228
|
+
<div class="border-t border-zinc-200 dark:border-zinc-800 pt-4">
|
|
7229
|
+
<div id="searchResultsContent" class="space-y-3"></div>
|
|
7230
|
+
<div id="searchResultsPagination" class="mt-4 flex items-center justify-between"></div>
|
|
7231
|
+
</div>
|
|
7232
|
+
</div>
|
|
7233
|
+
</div>
|
|
7234
|
+
</div>
|
|
7235
|
+
</div>
|
|
7236
|
+
|
|
7237
|
+
<script>
|
|
7238
|
+
// Open modal
|
|
7239
|
+
function openAdvancedSearch() {
|
|
7240
|
+
document.getElementById('advancedSearchModal').classList.remove('hidden');
|
|
7241
|
+
document.getElementById('searchQuery').focus();
|
|
7242
|
+
}
|
|
7243
|
+
|
|
7244
|
+
// Close modal
|
|
7245
|
+
function closeAdvancedSearch() {
|
|
7246
|
+
document.getElementById('advancedSearchModal').classList.add('hidden');
|
|
7247
|
+
document.getElementById('searchResults').classList.add('hidden');
|
|
7248
|
+
}
|
|
7249
|
+
|
|
7250
|
+
// Autocomplete
|
|
7251
|
+
let autocompleteTimeout;
|
|
7252
|
+
const searchQueryInput = document.getElementById('searchQuery');
|
|
7253
|
+
if (searchQueryInput) {
|
|
7254
|
+
searchQueryInput.addEventListener('input', (e) => {
|
|
7255
|
+
const query = e.target.value.trim();
|
|
7256
|
+
const suggestionsDiv = document.getElementById('searchSuggestions');
|
|
7257
|
+
|
|
7258
|
+
clearTimeout(autocompleteTimeout);
|
|
7259
|
+
|
|
7260
|
+
if (query.length < 2) {
|
|
7261
|
+
suggestionsDiv.classList.add('hidden');
|
|
7262
|
+
return;
|
|
7263
|
+
}
|
|
7264
|
+
|
|
7265
|
+
autocompleteTimeout = setTimeout(async () => {
|
|
7266
|
+
try {
|
|
7267
|
+
const res = await fetch(\`/api/search/suggest?q=\${encodeURIComponent(query)}\`);
|
|
7268
|
+
const { data } = await res.json();
|
|
7269
|
+
|
|
7270
|
+
if (data && data.length > 0) {
|
|
7271
|
+
suggestionsDiv.innerHTML = data.map(s => \`
|
|
7272
|
+
<div class="px-4 py-2 hover:bg-zinc-100 dark:hover:bg-zinc-700 cursor-pointer" onclick="selectSuggestion('\${s.replace(/'/g, "\\'")}')">\${s}</div>
|
|
7273
|
+
\`).join('');
|
|
7274
|
+
suggestionsDiv.classList.remove('hidden');
|
|
7275
|
+
} else {
|
|
7276
|
+
suggestionsDiv.classList.add('hidden');
|
|
7277
|
+
}
|
|
7278
|
+
} catch (error) {
|
|
7279
|
+
console.error('Autocomplete error:', error);
|
|
7280
|
+
}
|
|
7281
|
+
}, 300);
|
|
7282
|
+
});
|
|
7283
|
+
}
|
|
7284
|
+
|
|
7285
|
+
function selectSuggestion(suggestion) {
|
|
7286
|
+
document.getElementById('searchQuery').value = suggestion;
|
|
7287
|
+
document.getElementById('searchSuggestions').classList.add('hidden');
|
|
7288
|
+
}
|
|
7289
|
+
|
|
7290
|
+
// Hide suggestions when clicking outside
|
|
7291
|
+
document.addEventListener('click', (e) => {
|
|
7292
|
+
const suggestionsDiv = document.getElementById('searchSuggestions');
|
|
7293
|
+
if (!e.target.closest('#searchQuery') && !e.target.closest('#searchSuggestions')) {
|
|
7294
|
+
suggestionsDiv.classList.add('hidden');
|
|
7295
|
+
}
|
|
7296
|
+
});
|
|
7297
|
+
|
|
7298
|
+
// Form submission
|
|
7299
|
+
const advancedSearchForm = document.getElementById('advancedSearchForm');
|
|
7300
|
+
if (advancedSearchForm) {
|
|
7301
|
+
advancedSearchForm.addEventListener('submit', async (e) => {
|
|
7302
|
+
e.preventDefault();
|
|
7303
|
+
|
|
7304
|
+
const formData = new FormData(e.target);
|
|
7305
|
+
const query = formData.get('query');
|
|
7306
|
+
const mode = formData.get('mode') || 'ai';
|
|
7307
|
+
|
|
7308
|
+
// Build filters
|
|
7309
|
+
const filters = {};
|
|
7310
|
+
|
|
7311
|
+
const collections = Array.from(formData.getAll('collections')).filter(c => c !== '');
|
|
7312
|
+
if (collections.length > 0) {
|
|
7313
|
+
// Need to convert collection names to IDs - for now, pass names
|
|
7314
|
+
filters.collections = collections;
|
|
7315
|
+
}
|
|
7316
|
+
|
|
7317
|
+
const status = Array.from(formData.getAll('status'));
|
|
7318
|
+
if (status.length > 0) {
|
|
7319
|
+
filters.status = status;
|
|
7320
|
+
}
|
|
7321
|
+
|
|
7322
|
+
const dateStart = formData.get('date_start');
|
|
7323
|
+
const dateEnd = formData.get('date_end');
|
|
7324
|
+
if (dateStart || dateEnd) {
|
|
7325
|
+
filters.dateRange = {
|
|
7326
|
+
start: dateStart ? new Date(dateStart) : null,
|
|
7327
|
+
end: dateEnd ? new Date(dateEnd) : null,
|
|
7328
|
+
field: 'created_at'
|
|
7329
|
+
};
|
|
7330
|
+
}
|
|
7331
|
+
|
|
7332
|
+
// Execute search
|
|
7333
|
+
try {
|
|
7334
|
+
const res = await fetch('/api/search', {
|
|
7335
|
+
method: 'POST',
|
|
7336
|
+
headers: {'Content-Type': 'application/json'},
|
|
7337
|
+
body: JSON.stringify({
|
|
7338
|
+
query,
|
|
7339
|
+
mode,
|
|
7340
|
+
filters,
|
|
7341
|
+
limit: 20
|
|
7342
|
+
})
|
|
7343
|
+
});
|
|
7344
|
+
|
|
7345
|
+
const { data } = await res.json();
|
|
7346
|
+
|
|
7347
|
+
if (data && data.results) {
|
|
7348
|
+
displaySearchResults(data);
|
|
7349
|
+
}
|
|
7350
|
+
} catch (error) {
|
|
7351
|
+
console.error('Search error:', error);
|
|
7352
|
+
alert('Search failed. Please try again.');
|
|
7353
|
+
}
|
|
5733
7354
|
});
|
|
5734
7355
|
}
|
|
5735
7356
|
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
const
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
7357
|
+
function displaySearchResults(searchData) {
|
|
7358
|
+
const resultsDiv = document.getElementById('searchResultsContent');
|
|
7359
|
+
const resultsSection = document.getElementById('searchResults');
|
|
7360
|
+
|
|
7361
|
+
if (searchData.results.length === 0) {
|
|
7362
|
+
resultsDiv.innerHTML = '<p class="text-sm text-zinc-500 dark:text-zinc-400">No results found.</p>';
|
|
7363
|
+
} else {
|
|
7364
|
+
resultsDiv.innerHTML = searchData.results.map(result => \`
|
|
7365
|
+
<div class="p-4 rounded-lg border border-zinc-200 dark:border-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-800">
|
|
7366
|
+
<div class="flex items-start justify-between">
|
|
7367
|
+
<div class="flex-1">
|
|
7368
|
+
<h4 class="text-sm font-semibold text-zinc-950 dark:text-white mb-1">
|
|
7369
|
+
<a href="/admin/content/\${result.id}/edit" class="hover:text-indigo-600 dark:hover:text-indigo-400">\${result.title || 'Untitled'}</a>
|
|
7370
|
+
</h4>
|
|
7371
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400 mb-2">
|
|
7372
|
+
\${result.collection_name} \u2022 \${new Date(result.created_at).toLocaleDateString()}
|
|
7373
|
+
\${result.relevance_score ? \` \u2022 Relevance: \${(result.relevance_score * 100).toFixed(0)}%\` : ''}
|
|
7374
|
+
</p>
|
|
7375
|
+
\${result.snippet ? \`<p class="text-sm text-zinc-600 dark:text-zinc-400">\${result.snippet}</p>\` : ''}
|
|
7376
|
+
</div>
|
|
7377
|
+
<div class="ml-4">
|
|
7378
|
+
<span class="px-2 py-1 text-xs rounded-full \${result.status === 'published' ? 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-300' : 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300'}">\${result.status}</span>
|
|
7379
|
+
</div>
|
|
7380
|
+
</div>
|
|
7381
|
+
</div>
|
|
7382
|
+
\`).join('');
|
|
5748
7383
|
}
|
|
7384
|
+
|
|
7385
|
+
resultsSection.classList.remove('hidden');
|
|
7386
|
+
resultsSection.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
5749
7387
|
}
|
|
5750
7388
|
|
|
7389
|
+
// Make functions globally available
|
|
7390
|
+
window.openAdvancedSearch = openAdvancedSearch;
|
|
7391
|
+
window.closeAdvancedSearch = closeAdvancedSearch;
|
|
5751
7392
|
</script>
|
|
5752
|
-
|
|
5753
|
-
<!-- Confirmation Dialog for Bulk Actions -->
|
|
5754
|
-
${chunkAZLU3ROK_cjs.renderConfirmationDialog({
|
|
5755
|
-
id: "bulk-action-confirm",
|
|
5756
|
-
title: "Confirm Bulk Action",
|
|
5757
|
-
message: "Are you sure you want to perform this action? This operation will affect multiple items.",
|
|
5758
|
-
confirmText: "Confirm",
|
|
5759
|
-
cancelText: "Cancel",
|
|
5760
|
-
confirmClass: "bg-blue-500 hover:bg-blue-400",
|
|
5761
|
-
iconColor: "blue",
|
|
5762
|
-
onConfirm: "executeBulkAction()"
|
|
5763
|
-
})}
|
|
5764
|
-
|
|
5765
|
-
<!-- Confirmation Dialog Script -->
|
|
5766
|
-
${chunkAZLU3ROK_cjs.getConfirmationDialogScript()}
|
|
5767
7393
|
`;
|
|
5768
7394
|
const layoutData = {
|
|
5769
7395
|
title: "Content Management",
|
|
@@ -5773,7 +7399,7 @@ function renderContentListPage(data) {
|
|
|
5773
7399
|
version: data.version,
|
|
5774
7400
|
content: pageContent
|
|
5775
7401
|
};
|
|
5776
|
-
return
|
|
7402
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
5777
7403
|
}
|
|
5778
7404
|
|
|
5779
7405
|
// src/templates/components/version-history.template.ts
|
|
@@ -5967,7 +7593,123 @@ async function isPluginActive2(db, pluginId) {
|
|
|
5967
7593
|
|
|
5968
7594
|
// src/routes/admin-content.ts
|
|
5969
7595
|
var adminContentRoutes = new hono.Hono();
|
|
5970
|
-
|
|
7596
|
+
function parseFieldValue(field, formData, options = {}) {
|
|
7597
|
+
const { skipValidation = false } = options;
|
|
7598
|
+
const value = formData.get(field.field_name);
|
|
7599
|
+
const errors = [];
|
|
7600
|
+
const blocksConfig = chunkYMTTGHEK_cjs.getBlocksFieldConfig(field.field_options);
|
|
7601
|
+
if (blocksConfig) {
|
|
7602
|
+
const parsed = chunkYMTTGHEK_cjs.parseBlocksValue(value, blocksConfig);
|
|
7603
|
+
if (!skipValidation && field.is_required && parsed.value.length === 0) {
|
|
7604
|
+
parsed.errors.push(`${field.field_label} is required`);
|
|
7605
|
+
}
|
|
7606
|
+
return { value: parsed.value, errors: parsed.errors };
|
|
7607
|
+
}
|
|
7608
|
+
if (!skipValidation && field.is_required && (!value || value.toString().trim() === "")) {
|
|
7609
|
+
return { value: null, errors: [`${field.field_label} is required`] };
|
|
7610
|
+
}
|
|
7611
|
+
switch (field.field_type) {
|
|
7612
|
+
case "number":
|
|
7613
|
+
if (value && isNaN(Number(value))) {
|
|
7614
|
+
if (!skipValidation) {
|
|
7615
|
+
errors.push(`${field.field_label} must be a valid number`);
|
|
7616
|
+
}
|
|
7617
|
+
return { value: null, errors };
|
|
7618
|
+
}
|
|
7619
|
+
return { value: value ? Number(value) : null, errors: [] };
|
|
7620
|
+
case "boolean":
|
|
7621
|
+
const submitted = formData.get(`${field.field_name}_submitted`);
|
|
7622
|
+
return { value: submitted ? value === "true" : false, errors: [] };
|
|
7623
|
+
case "select":
|
|
7624
|
+
if (field.field_options?.multiple) {
|
|
7625
|
+
return { value: formData.getAll(`${field.field_name}[]`), errors: [] };
|
|
7626
|
+
}
|
|
7627
|
+
return { value, errors: [] };
|
|
7628
|
+
case "array": {
|
|
7629
|
+
if (!value || value.toString().trim() === "") {
|
|
7630
|
+
if (!skipValidation && field.is_required) {
|
|
7631
|
+
errors.push(`${field.field_label} is required`);
|
|
7632
|
+
}
|
|
7633
|
+
return { value: [], errors };
|
|
7634
|
+
}
|
|
7635
|
+
try {
|
|
7636
|
+
const parsed = JSON.parse(value.toString());
|
|
7637
|
+
if (!Array.isArray(parsed)) {
|
|
7638
|
+
if (!skipValidation) {
|
|
7639
|
+
errors.push(`${field.field_label} must be a JSON array`);
|
|
7640
|
+
}
|
|
7641
|
+
return { value: [], errors };
|
|
7642
|
+
}
|
|
7643
|
+
if (!skipValidation && field.is_required && parsed.length === 0) {
|
|
7644
|
+
errors.push(`${field.field_label} is required`);
|
|
7645
|
+
}
|
|
7646
|
+
return { value: parsed, errors };
|
|
7647
|
+
} catch {
|
|
7648
|
+
if (!skipValidation) {
|
|
7649
|
+
errors.push(`${field.field_label} must be valid JSON`);
|
|
7650
|
+
}
|
|
7651
|
+
return { value: [], errors };
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7654
|
+
case "object": {
|
|
7655
|
+
if (!value || value.toString().trim() === "") {
|
|
7656
|
+
if (!skipValidation && field.is_required) {
|
|
7657
|
+
errors.push(`${field.field_label} is required`);
|
|
7658
|
+
}
|
|
7659
|
+
return { value: {}, errors };
|
|
7660
|
+
}
|
|
7661
|
+
try {
|
|
7662
|
+
const parsed = JSON.parse(value.toString());
|
|
7663
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
7664
|
+
if (!skipValidation) {
|
|
7665
|
+
errors.push(`${field.field_label} must be a JSON object`);
|
|
7666
|
+
}
|
|
7667
|
+
return { value: {}, errors };
|
|
7668
|
+
}
|
|
7669
|
+
if (!skipValidation && field.is_required && Object.keys(parsed).length === 0) {
|
|
7670
|
+
errors.push(`${field.field_label} is required`);
|
|
7671
|
+
}
|
|
7672
|
+
return { value: parsed, errors };
|
|
7673
|
+
} catch {
|
|
7674
|
+
if (!skipValidation) {
|
|
7675
|
+
errors.push(`${field.field_label} must be valid JSON`);
|
|
7676
|
+
}
|
|
7677
|
+
return { value: {}, errors };
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
case "json": {
|
|
7681
|
+
if (!value || value.toString().trim() === "") {
|
|
7682
|
+
if (!skipValidation && field.is_required) {
|
|
7683
|
+
errors.push(`${field.field_label} is required`);
|
|
7684
|
+
}
|
|
7685
|
+
return { value: null, errors };
|
|
7686
|
+
}
|
|
7687
|
+
try {
|
|
7688
|
+
return { value: JSON.parse(value.toString()), errors: [] };
|
|
7689
|
+
} catch {
|
|
7690
|
+
if (!skipValidation) {
|
|
7691
|
+
errors.push(`${field.field_label} must be valid JSON`);
|
|
7692
|
+
}
|
|
7693
|
+
return { value: null, errors };
|
|
7694
|
+
}
|
|
7695
|
+
}
|
|
7696
|
+
default:
|
|
7697
|
+
return { value, errors: [] };
|
|
7698
|
+
}
|
|
7699
|
+
}
|
|
7700
|
+
function extractFieldData(fields, formData, options = {}) {
|
|
7701
|
+
const data = {};
|
|
7702
|
+
const errors = {};
|
|
7703
|
+
for (const field of fields) {
|
|
7704
|
+
const result = parseFieldValue(field, formData, options);
|
|
7705
|
+
data[field.field_name] = result.value;
|
|
7706
|
+
if (result.errors.length > 0) {
|
|
7707
|
+
errors[field.field_name] = result.errors;
|
|
7708
|
+
}
|
|
7709
|
+
}
|
|
7710
|
+
return { data, errors };
|
|
7711
|
+
}
|
|
7712
|
+
adminContentRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
5971
7713
|
async function getCollectionFields(db, collectionId) {
|
|
5972
7714
|
const cache = chunk7FOAMNTI_cjs.getCacheService(chunk7FOAMNTI_cjs.CACHE_CONFIGS.collection);
|
|
5973
7715
|
return cache.getOrSet(
|
|
@@ -6249,21 +7991,21 @@ adminContentRoutes.get("/new", async (c) => {
|
|
|
6249
7991
|
const tinymceEnabled = await isPluginActive2(db, "tinymce-plugin");
|
|
6250
7992
|
let tinymceSettings;
|
|
6251
7993
|
if (tinymceEnabled) {
|
|
6252
|
-
const pluginService = new
|
|
7994
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6253
7995
|
const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
|
|
6254
7996
|
tinymceSettings = tinymcePlugin2?.settings;
|
|
6255
7997
|
}
|
|
6256
7998
|
const quillEnabled = await isPluginActive2(db, "quill-editor");
|
|
6257
7999
|
let quillSettings;
|
|
6258
8000
|
if (quillEnabled) {
|
|
6259
|
-
const pluginService = new
|
|
8001
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6260
8002
|
const quillPlugin = await pluginService.getPlugin("quill-editor");
|
|
6261
8003
|
quillSettings = quillPlugin?.settings;
|
|
6262
8004
|
}
|
|
6263
8005
|
const mdxeditorEnabled = await isPluginActive2(db, "easy-mdx");
|
|
6264
8006
|
let mdxeditorSettings;
|
|
6265
8007
|
if (mdxeditorEnabled) {
|
|
6266
|
-
const pluginService = new
|
|
8008
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6267
8009
|
const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
|
|
6268
8010
|
mdxeditorSettings = mdxeditorPlugin?.settings;
|
|
6269
8011
|
}
|
|
@@ -6354,21 +8096,21 @@ adminContentRoutes.get("/:id/edit", async (c) => {
|
|
|
6354
8096
|
const tinymceEnabled = await isPluginActive2(db, "tinymce-plugin");
|
|
6355
8097
|
let tinymceSettings;
|
|
6356
8098
|
if (tinymceEnabled) {
|
|
6357
|
-
const pluginService = new
|
|
8099
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6358
8100
|
const tinymcePlugin2 = await pluginService.getPlugin("tinymce-plugin");
|
|
6359
8101
|
tinymceSettings = tinymcePlugin2?.settings;
|
|
6360
8102
|
}
|
|
6361
8103
|
const quillEnabled = await isPluginActive2(db, "quill-editor");
|
|
6362
8104
|
let quillSettings;
|
|
6363
8105
|
if (quillEnabled) {
|
|
6364
|
-
const pluginService = new
|
|
8106
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6365
8107
|
const quillPlugin = await pluginService.getPlugin("quill-editor");
|
|
6366
8108
|
quillSettings = quillPlugin?.settings;
|
|
6367
8109
|
}
|
|
6368
8110
|
const mdxeditorEnabled = await isPluginActive2(db, "easy-mdx");
|
|
6369
8111
|
let mdxeditorSettings;
|
|
6370
8112
|
if (mdxeditorEnabled) {
|
|
6371
|
-
const pluginService = new
|
|
8113
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
6372
8114
|
const mdxeditorPlugin = await pluginService.getPlugin("easy-mdx");
|
|
6373
8115
|
mdxeditorSettings = mdxeditorPlugin?.settings;
|
|
6374
8116
|
}
|
|
@@ -6440,109 +8182,7 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
6440
8182
|
`);
|
|
6441
8183
|
}
|
|
6442
8184
|
const fields = await getCollectionFields(db, collectionId);
|
|
6443
|
-
const data =
|
|
6444
|
-
const errors = {};
|
|
6445
|
-
for (const field of fields) {
|
|
6446
|
-
const value = formData.get(field.field_name);
|
|
6447
|
-
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6448
|
-
if (blocksConfig) {
|
|
6449
|
-
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6450
|
-
if (field.is_required && parsed.value.length === 0) {
|
|
6451
|
-
parsed.errors.push(`${field.field_label} is required`);
|
|
6452
|
-
}
|
|
6453
|
-
if (parsed.errors.length > 0) {
|
|
6454
|
-
errors[field.field_name] = parsed.errors;
|
|
6455
|
-
}
|
|
6456
|
-
data[field.field_name] = parsed.value;
|
|
6457
|
-
continue;
|
|
6458
|
-
}
|
|
6459
|
-
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6460
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6461
|
-
continue;
|
|
6462
|
-
}
|
|
6463
|
-
switch (field.field_type) {
|
|
6464
|
-
case "number":
|
|
6465
|
-
if (value && isNaN(Number(value))) {
|
|
6466
|
-
errors[field.field_name] = [`${field.field_label} must be a valid number`];
|
|
6467
|
-
} else {
|
|
6468
|
-
data[field.field_name] = value ? Number(value) : null;
|
|
6469
|
-
}
|
|
6470
|
-
break;
|
|
6471
|
-
case "boolean":
|
|
6472
|
-
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ? value === "true" : false;
|
|
6473
|
-
break;
|
|
6474
|
-
case "select":
|
|
6475
|
-
if (field.field_options?.multiple) {
|
|
6476
|
-
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
6477
|
-
} else {
|
|
6478
|
-
data[field.field_name] = value;
|
|
6479
|
-
}
|
|
6480
|
-
break;
|
|
6481
|
-
case "array": {
|
|
6482
|
-
if (!value || value.toString().trim() === "") {
|
|
6483
|
-
data[field.field_name] = [];
|
|
6484
|
-
if (field.is_required) {
|
|
6485
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6486
|
-
}
|
|
6487
|
-
break;
|
|
6488
|
-
}
|
|
6489
|
-
try {
|
|
6490
|
-
const parsed = JSON.parse(value.toString());
|
|
6491
|
-
if (!Array.isArray(parsed)) {
|
|
6492
|
-
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6493
|
-
} else {
|
|
6494
|
-
if (field.is_required && parsed.length === 0) {
|
|
6495
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6496
|
-
}
|
|
6497
|
-
data[field.field_name] = parsed;
|
|
6498
|
-
}
|
|
6499
|
-
} catch {
|
|
6500
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6501
|
-
}
|
|
6502
|
-
break;
|
|
6503
|
-
}
|
|
6504
|
-
case "object": {
|
|
6505
|
-
if (!value || value.toString().trim() === "") {
|
|
6506
|
-
data[field.field_name] = {};
|
|
6507
|
-
if (field.is_required) {
|
|
6508
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6509
|
-
}
|
|
6510
|
-
break;
|
|
6511
|
-
}
|
|
6512
|
-
try {
|
|
6513
|
-
const parsed = JSON.parse(value.toString());
|
|
6514
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6515
|
-
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6516
|
-
} else {
|
|
6517
|
-
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6518
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6519
|
-
}
|
|
6520
|
-
data[field.field_name] = parsed;
|
|
6521
|
-
}
|
|
6522
|
-
} catch {
|
|
6523
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6524
|
-
}
|
|
6525
|
-
break;
|
|
6526
|
-
}
|
|
6527
|
-
case "json": {
|
|
6528
|
-
if (!value || value.toString().trim() === "") {
|
|
6529
|
-
data[field.field_name] = null;
|
|
6530
|
-
if (field.is_required) {
|
|
6531
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6532
|
-
}
|
|
6533
|
-
break;
|
|
6534
|
-
}
|
|
6535
|
-
try {
|
|
6536
|
-
data[field.field_name] = JSON.parse(value.toString());
|
|
6537
|
-
} catch {
|
|
6538
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6539
|
-
}
|
|
6540
|
-
break;
|
|
6541
|
-
}
|
|
6542
|
-
default:
|
|
6543
|
-
data[field.field_name] = value;
|
|
6544
|
-
}
|
|
6545
|
-
}
|
|
8185
|
+
const { data, errors } = extractFieldData(fields, formData);
|
|
6546
8186
|
if (Object.keys(errors).length > 0) {
|
|
6547
8187
|
const formDataWithErrors = {
|
|
6548
8188
|
collection,
|
|
@@ -6659,109 +8299,7 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
6659
8299
|
`);
|
|
6660
8300
|
}
|
|
6661
8301
|
const fields = await getCollectionFields(db, existingContent.collection_id);
|
|
6662
|
-
const data =
|
|
6663
|
-
const errors = {};
|
|
6664
|
-
for (const field of fields) {
|
|
6665
|
-
const value = formData.get(field.field_name);
|
|
6666
|
-
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6667
|
-
if (blocksConfig) {
|
|
6668
|
-
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6669
|
-
if (field.is_required && parsed.value.length === 0) {
|
|
6670
|
-
parsed.errors.push(`${field.field_label} is required`);
|
|
6671
|
-
}
|
|
6672
|
-
if (parsed.errors.length > 0) {
|
|
6673
|
-
errors[field.field_name] = parsed.errors;
|
|
6674
|
-
}
|
|
6675
|
-
data[field.field_name] = parsed.value;
|
|
6676
|
-
continue;
|
|
6677
|
-
}
|
|
6678
|
-
if (field.is_required && (!value || value.toString().trim() === "")) {
|
|
6679
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6680
|
-
continue;
|
|
6681
|
-
}
|
|
6682
|
-
switch (field.field_type) {
|
|
6683
|
-
case "number":
|
|
6684
|
-
if (value && isNaN(Number(value))) {
|
|
6685
|
-
errors[field.field_name] = [`${field.field_label} must be a valid number`];
|
|
6686
|
-
} else {
|
|
6687
|
-
data[field.field_name] = value ? Number(value) : null;
|
|
6688
|
-
}
|
|
6689
|
-
break;
|
|
6690
|
-
case "boolean":
|
|
6691
|
-
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ? value === "true" : false;
|
|
6692
|
-
break;
|
|
6693
|
-
case "select":
|
|
6694
|
-
if (field.field_options?.multiple) {
|
|
6695
|
-
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
6696
|
-
} else {
|
|
6697
|
-
data[field.field_name] = value;
|
|
6698
|
-
}
|
|
6699
|
-
break;
|
|
6700
|
-
case "array": {
|
|
6701
|
-
if (!value || value.toString().trim() === "") {
|
|
6702
|
-
data[field.field_name] = [];
|
|
6703
|
-
if (field.is_required) {
|
|
6704
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6705
|
-
}
|
|
6706
|
-
break;
|
|
6707
|
-
}
|
|
6708
|
-
try {
|
|
6709
|
-
const parsed = JSON.parse(value.toString());
|
|
6710
|
-
if (!Array.isArray(parsed)) {
|
|
6711
|
-
errors[field.field_name] = [`${field.field_label} must be a JSON array`];
|
|
6712
|
-
} else {
|
|
6713
|
-
if (field.is_required && parsed.length === 0) {
|
|
6714
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6715
|
-
}
|
|
6716
|
-
data[field.field_name] = parsed;
|
|
6717
|
-
}
|
|
6718
|
-
} catch {
|
|
6719
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6720
|
-
}
|
|
6721
|
-
break;
|
|
6722
|
-
}
|
|
6723
|
-
case "object": {
|
|
6724
|
-
if (!value || value.toString().trim() === "") {
|
|
6725
|
-
data[field.field_name] = {};
|
|
6726
|
-
if (field.is_required) {
|
|
6727
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6728
|
-
}
|
|
6729
|
-
break;
|
|
6730
|
-
}
|
|
6731
|
-
try {
|
|
6732
|
-
const parsed = JSON.parse(value.toString());
|
|
6733
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6734
|
-
errors[field.field_name] = [`${field.field_label} must be a JSON object`];
|
|
6735
|
-
} else {
|
|
6736
|
-
if (field.is_required && Object.keys(parsed).length === 0) {
|
|
6737
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6738
|
-
}
|
|
6739
|
-
data[field.field_name] = parsed;
|
|
6740
|
-
}
|
|
6741
|
-
} catch {
|
|
6742
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6743
|
-
}
|
|
6744
|
-
break;
|
|
6745
|
-
}
|
|
6746
|
-
case "json": {
|
|
6747
|
-
if (!value || value.toString().trim() === "") {
|
|
6748
|
-
data[field.field_name] = null;
|
|
6749
|
-
if (field.is_required) {
|
|
6750
|
-
errors[field.field_name] = [`${field.field_label} is required`];
|
|
6751
|
-
}
|
|
6752
|
-
break;
|
|
6753
|
-
}
|
|
6754
|
-
try {
|
|
6755
|
-
data[field.field_name] = JSON.parse(value.toString());
|
|
6756
|
-
} catch {
|
|
6757
|
-
errors[field.field_name] = [`${field.field_label} must be valid JSON`];
|
|
6758
|
-
}
|
|
6759
|
-
break;
|
|
6760
|
-
}
|
|
6761
|
-
default:
|
|
6762
|
-
data[field.field_name] = value;
|
|
6763
|
-
}
|
|
6764
|
-
}
|
|
8302
|
+
const { data, errors } = extractFieldData(fields, formData);
|
|
6765
8303
|
if (Object.keys(errors).length > 0) {
|
|
6766
8304
|
const formDataWithErrors = {
|
|
6767
8305
|
id,
|
|
@@ -6874,33 +8412,7 @@ adminContentRoutes.post("/preview", async (c) => {
|
|
|
6874
8412
|
return c.html("<p>Collection not found</p>");
|
|
6875
8413
|
}
|
|
6876
8414
|
const fields = await getCollectionFields(db, collectionId);
|
|
6877
|
-
const data = {};
|
|
6878
|
-
for (const field of fields) {
|
|
6879
|
-
const value = formData.get(field.field_name);
|
|
6880
|
-
const blocksConfig = chunkZWV3EBZ7_cjs.getBlocksFieldConfig(field.field_options);
|
|
6881
|
-
if (blocksConfig) {
|
|
6882
|
-
const parsed = chunkZWV3EBZ7_cjs.parseBlocksValue(value, blocksConfig);
|
|
6883
|
-
data[field.field_name] = parsed.value;
|
|
6884
|
-
continue;
|
|
6885
|
-
}
|
|
6886
|
-
switch (field.field_type) {
|
|
6887
|
-
case "number":
|
|
6888
|
-
data[field.field_name] = value ? Number(value) : null;
|
|
6889
|
-
break;
|
|
6890
|
-
case "boolean":
|
|
6891
|
-
data[field.field_name] = value === "true";
|
|
6892
|
-
break;
|
|
6893
|
-
case "select":
|
|
6894
|
-
if (field.field_options?.multiple) {
|
|
6895
|
-
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
6896
|
-
} else {
|
|
6897
|
-
data[field.field_name] = value;
|
|
6898
|
-
}
|
|
6899
|
-
break;
|
|
6900
|
-
default:
|
|
6901
|
-
data[field.field_name] = value;
|
|
6902
|
-
}
|
|
6903
|
-
}
|
|
8415
|
+
const { data } = extractFieldData(fields, formData, { skipValidation: true });
|
|
6904
8416
|
const previewHTML = `
|
|
6905
8417
|
<!DOCTYPE html>
|
|
6906
8418
|
<html lang="en">
|
|
@@ -7328,7 +8840,7 @@ ${JSON.stringify(data, null, 2)}
|
|
|
7328
8840
|
var admin_content_default = adminContentRoutes;
|
|
7329
8841
|
|
|
7330
8842
|
// src/templates/pages/admin-profile.template.ts
|
|
7331
|
-
|
|
8843
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
7332
8844
|
function renderAvatarImage(avatarUrl, firstName, lastName) {
|
|
7333
8845
|
return `<div id="avatar-image-container" class="w-24 h-24 rounded-full mx-auto mb-4 overflow-hidden bg-gradient-to-br from-cyan-400 to-purple-400 flex items-center justify-center ring-4 ring-zinc-950/5 dark:ring-white/10">
|
|
7334
8846
|
${avatarUrl ? `<img src="${avatarUrl}" alt="Profile picture" class="w-full h-full object-cover">` : `<span class="text-2xl font-bold text-white">${firstName.charAt(0)}${lastName.charAt(0)}</span>`}
|
|
@@ -7348,8 +8860,8 @@ function renderProfilePage(data) {
|
|
|
7348
8860
|
</div>
|
|
7349
8861
|
|
|
7350
8862
|
<!-- Alert Messages -->
|
|
7351
|
-
${data.error ?
|
|
7352
|
-
${data.success ?
|
|
8863
|
+
${data.error ? chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
8864
|
+
${data.success ? chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
7353
8865
|
|
|
7354
8866
|
<!-- Profile Form -->
|
|
7355
8867
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
|
@@ -7736,7 +9248,7 @@ function renderProfilePage(data) {
|
|
|
7736
9248
|
version: data.version,
|
|
7737
9249
|
content: pageContent
|
|
7738
9250
|
};
|
|
7739
|
-
return
|
|
9251
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
7740
9252
|
}
|
|
7741
9253
|
|
|
7742
9254
|
// src/templates/components/alert.template.ts
|
|
@@ -8019,7 +9531,7 @@ function renderActivityLogsPage(data) {
|
|
|
8019
9531
|
user: data.user,
|
|
8020
9532
|
content: pageContent
|
|
8021
9533
|
};
|
|
8022
|
-
return
|
|
9534
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout(layoutData);
|
|
8023
9535
|
}
|
|
8024
9536
|
function getActionBadgeClass(action) {
|
|
8025
9537
|
if (action.includes("login") || action.includes("logout")) {
|
|
@@ -8039,7 +9551,7 @@ function formatAction(action) {
|
|
|
8039
9551
|
}
|
|
8040
9552
|
|
|
8041
9553
|
// src/templates/pages/admin-user-edit.template.ts
|
|
8042
|
-
|
|
9554
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
8043
9555
|
|
|
8044
9556
|
// src/templates/components/confirmation-dialog.template.ts
|
|
8045
9557
|
function renderConfirmationDialog2(options) {
|
|
@@ -8160,8 +9672,8 @@ function renderUserEditPage(data) {
|
|
|
8160
9672
|
|
|
8161
9673
|
<!-- Alert Messages -->
|
|
8162
9674
|
<div id="form-messages">
|
|
8163
|
-
${data.error ?
|
|
8164
|
-
${data.success ?
|
|
9675
|
+
${data.error ? chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
9676
|
+
${data.success ? chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
8165
9677
|
</div>
|
|
8166
9678
|
|
|
8167
9679
|
<!-- User Edit Form -->
|
|
@@ -8180,7 +9692,7 @@ function renderUserEditPage(data) {
|
|
|
8180
9692
|
<input
|
|
8181
9693
|
type="text"
|
|
8182
9694
|
name="first_name"
|
|
8183
|
-
value="${
|
|
9695
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.firstName || "")}"
|
|
8184
9696
|
required
|
|
8185
9697
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8186
9698
|
/>
|
|
@@ -8191,7 +9703,7 @@ function renderUserEditPage(data) {
|
|
|
8191
9703
|
<input
|
|
8192
9704
|
type="text"
|
|
8193
9705
|
name="last_name"
|
|
8194
|
-
value="${
|
|
9706
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.lastName || "")}"
|
|
8195
9707
|
required
|
|
8196
9708
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8197
9709
|
/>
|
|
@@ -8202,7 +9714,7 @@ function renderUserEditPage(data) {
|
|
|
8202
9714
|
<input
|
|
8203
9715
|
type="text"
|
|
8204
9716
|
name="username"
|
|
8205
|
-
value="${
|
|
9717
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.username || "")}"
|
|
8206
9718
|
required
|
|
8207
9719
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8208
9720
|
/>
|
|
@@ -8213,7 +9725,7 @@ function renderUserEditPage(data) {
|
|
|
8213
9725
|
<input
|
|
8214
9726
|
type="email"
|
|
8215
9727
|
name="email"
|
|
8216
|
-
value="${
|
|
9728
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.email || "")}"
|
|
8217
9729
|
required
|
|
8218
9730
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8219
9731
|
/>
|
|
@@ -8224,7 +9736,7 @@ function renderUserEditPage(data) {
|
|
|
8224
9736
|
<input
|
|
8225
9737
|
type="tel"
|
|
8226
9738
|
name="phone"
|
|
8227
|
-
value="${
|
|
9739
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.phone || "")}"
|
|
8228
9740
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8229
9741
|
/>
|
|
8230
9742
|
</div>
|
|
@@ -8238,7 +9750,7 @@ function renderUserEditPage(data) {
|
|
|
8238
9750
|
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white/5 dark:bg-white/5 py-1.5 pl-3 pr-8 text-base text-zinc-950 dark:text-white outline outline-1 -outline-offset-1 outline-zinc-500/30 dark:outline-zinc-400/30 *:bg-white dark:*:bg-zinc-800 focus-visible:outline focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-zinc-500 dark:focus-visible:outline-zinc-400 sm:text-sm/6"
|
|
8239
9751
|
>
|
|
8240
9752
|
${data.roles.map((role) => `
|
|
8241
|
-
<option value="${
|
|
9753
|
+
<option value="${chunkYMTTGHEK_cjs.escapeHtml(role.value)}" ${data.userToEdit.role === role.value ? "selected" : ""}>${chunkYMTTGHEK_cjs.escapeHtml(role.label)}</option>
|
|
8242
9754
|
`).join("")}
|
|
8243
9755
|
</select>
|
|
8244
9756
|
<svg viewBox="0 0 16 16" fill="currentColor" data-slot="icon" aria-hidden="true" class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-zinc-600 dark:text-zinc-400 sm:size-4">
|
|
@@ -8247,14 +9759,87 @@ function renderUserEditPage(data) {
|
|
|
8247
9759
|
</div>
|
|
8248
9760
|
</div>
|
|
8249
9761
|
</div>
|
|
9762
|
+
</div>
|
|
9763
|
+
|
|
9764
|
+
<!-- Profile Information -->
|
|
9765
|
+
<div class="mb-8">
|
|
9766
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-4">Profile Information</h3>
|
|
9767
|
+
<p class="text-sm text-zinc-500 dark:text-zinc-400 mb-4">Extended profile data for this user</p>
|
|
9768
|
+
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
9769
|
+
<div>
|
|
9770
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Display Name</label>
|
|
9771
|
+
<input
|
|
9772
|
+
type="text"
|
|
9773
|
+
name="profile_display_name"
|
|
9774
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.displayName || "")}"
|
|
9775
|
+
placeholder="Public display name"
|
|
9776
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9777
|
+
/>
|
|
9778
|
+
</div>
|
|
9779
|
+
|
|
9780
|
+
<div>
|
|
9781
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Company</label>
|
|
9782
|
+
<input
|
|
9783
|
+
type="text"
|
|
9784
|
+
name="profile_company"
|
|
9785
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.company || "")}"
|
|
9786
|
+
placeholder="Company or organization"
|
|
9787
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9788
|
+
/>
|
|
9789
|
+
</div>
|
|
9790
|
+
|
|
9791
|
+
<div>
|
|
9792
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Job Title</label>
|
|
9793
|
+
<input
|
|
9794
|
+
type="text"
|
|
9795
|
+
name="profile_job_title"
|
|
9796
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.jobTitle || "")}"
|
|
9797
|
+
placeholder="Job title or role"
|
|
9798
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9799
|
+
/>
|
|
9800
|
+
</div>
|
|
9801
|
+
|
|
9802
|
+
<div>
|
|
9803
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Website</label>
|
|
9804
|
+
<input
|
|
9805
|
+
type="url"
|
|
9806
|
+
name="profile_website"
|
|
9807
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.website || "")}"
|
|
9808
|
+
placeholder="https://example.com"
|
|
9809
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9810
|
+
/>
|
|
9811
|
+
</div>
|
|
9812
|
+
|
|
9813
|
+
<div>
|
|
9814
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Location</label>
|
|
9815
|
+
<input
|
|
9816
|
+
type="text"
|
|
9817
|
+
name="profile_location"
|
|
9818
|
+
value="${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.location || "")}"
|
|
9819
|
+
placeholder="City, Country"
|
|
9820
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9821
|
+
/>
|
|
9822
|
+
</div>
|
|
9823
|
+
|
|
9824
|
+
<div>
|
|
9825
|
+
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Date of Birth</label>
|
|
9826
|
+
<input
|
|
9827
|
+
type="date"
|
|
9828
|
+
name="profile_date_of_birth"
|
|
9829
|
+
value="${data.userToEdit.profile?.dateOfBirth ? new Date(data.userToEdit.profile.dateOfBirth).toISOString().split("T")[0] : ""}"
|
|
9830
|
+
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
9831
|
+
/>
|
|
9832
|
+
</div>
|
|
9833
|
+
</div>
|
|
8250
9834
|
|
|
8251
9835
|
<div class="mt-6">
|
|
8252
9836
|
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Bio</label>
|
|
8253
9837
|
<textarea
|
|
8254
|
-
name="
|
|
9838
|
+
name="profile_bio"
|
|
8255
9839
|
rows="3"
|
|
9840
|
+
placeholder="Short bio or description"
|
|
8256
9841
|
class="w-full rounded-lg bg-white dark:bg-zinc-800 px-3 py-2 text-sm text-zinc-950 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-950 dark:focus:ring-white transition-shadow"
|
|
8257
|
-
>${
|
|
9842
|
+
>${chunkYMTTGHEK_cjs.escapeHtml(data.userToEdit.profile?.bio || "")}</textarea>
|
|
8258
9843
|
</div>
|
|
8259
9844
|
</div>
|
|
8260
9845
|
|
|
@@ -8454,11 +10039,11 @@ function renderUserEditPage(data) {
|
|
|
8454
10039
|
user: data.user,
|
|
8455
10040
|
content: pageContent
|
|
8456
10041
|
};
|
|
8457
|
-
return
|
|
10042
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
8458
10043
|
}
|
|
8459
10044
|
|
|
8460
10045
|
// src/templates/pages/admin-user-new.template.ts
|
|
8461
|
-
|
|
10046
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
8462
10047
|
function renderUserNewPage(data) {
|
|
8463
10048
|
const pageContent = `
|
|
8464
10049
|
<div>
|
|
@@ -8497,8 +10082,8 @@ function renderUserNewPage(data) {
|
|
|
8497
10082
|
|
|
8498
10083
|
<!-- Alert Messages -->
|
|
8499
10084
|
<div id="form-messages">
|
|
8500
|
-
${data.error ?
|
|
8501
|
-
${data.success ?
|
|
10085
|
+
${data.error ? chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
10086
|
+
${data.success ? chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
8502
10087
|
</div>
|
|
8503
10088
|
|
|
8504
10089
|
<!-- User New Form -->
|
|
@@ -8742,11 +10327,11 @@ function renderUserNewPage(data) {
|
|
|
8742
10327
|
user: data.user,
|
|
8743
10328
|
content: pageContent
|
|
8744
10329
|
};
|
|
8745
|
-
return
|
|
10330
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
8746
10331
|
}
|
|
8747
10332
|
|
|
8748
10333
|
// src/templates/pages/admin-users-list.template.ts
|
|
8749
|
-
|
|
10334
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
8750
10335
|
function renderUsersListPage(data) {
|
|
8751
10336
|
const columns = [
|
|
8752
10337
|
{
|
|
@@ -8897,8 +10482,8 @@ function renderUsersListPage(data) {
|
|
|
8897
10482
|
</div>
|
|
8898
10483
|
|
|
8899
10484
|
<!-- Alert Messages -->
|
|
8900
|
-
${data.error ?
|
|
8901
|
-
${data.success ?
|
|
10485
|
+
${data.error ? chunkBZC4FYW7_cjs.renderAlert({ type: "error", message: data.error, dismissible: true }) : ""}
|
|
10486
|
+
${data.success ? chunkBZC4FYW7_cjs.renderAlert({ type: "success", message: data.success, dismissible: true }) : ""}
|
|
8902
10487
|
|
|
8903
10488
|
<!-- Stats -->
|
|
8904
10489
|
<div class="mb-6">
|
|
@@ -9075,10 +10660,10 @@ function renderUsersListPage(data) {
|
|
|
9075
10660
|
</div>
|
|
9076
10661
|
|
|
9077
10662
|
<!-- Users Table -->
|
|
9078
|
-
${
|
|
10663
|
+
${chunkBZC4FYW7_cjs.renderTable(tableData)}
|
|
9079
10664
|
|
|
9080
10665
|
<!-- Pagination -->
|
|
9081
|
-
${data.pagination ?
|
|
10666
|
+
${data.pagination ? chunkBZC4FYW7_cjs.renderPagination(data.pagination) : ""}
|
|
9082
10667
|
</div>
|
|
9083
10668
|
|
|
9084
10669
|
<script>
|
|
@@ -9149,12 +10734,12 @@ function renderUsersListPage(data) {
|
|
|
9149
10734
|
version: data.version,
|
|
9150
10735
|
content: pageContent
|
|
9151
10736
|
};
|
|
9152
|
-
return
|
|
10737
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
9153
10738
|
}
|
|
9154
10739
|
|
|
9155
10740
|
// src/routes/admin-users.ts
|
|
9156
10741
|
var userRoutes = new hono.Hono();
|
|
9157
|
-
userRoutes.use("*",
|
|
10742
|
+
userRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
9158
10743
|
userRoutes.get("/", (c) => {
|
|
9159
10744
|
return c.redirect("/admin/dashboard");
|
|
9160
10745
|
});
|
|
@@ -9253,12 +10838,12 @@ userRoutes.put("/profile", async (c) => {
|
|
|
9253
10838
|
const db = c.env.DB;
|
|
9254
10839
|
try {
|
|
9255
10840
|
const formData = await c.req.formData();
|
|
9256
|
-
const firstName =
|
|
9257
|
-
const lastName =
|
|
9258
|
-
const username =
|
|
10841
|
+
const firstName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
10842
|
+
const lastName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
10843
|
+
const username = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9259
10844
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9260
|
-
const phone =
|
|
9261
|
-
const bio =
|
|
10845
|
+
const phone = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
10846
|
+
const bio = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
9262
10847
|
const timezone = formData.get("timezone")?.toString() || "UTC";
|
|
9263
10848
|
const language = formData.get("language")?.toString() || "en";
|
|
9264
10849
|
const emailNotifications = formData.get("email_notifications") === "1";
|
|
@@ -9309,7 +10894,7 @@ userRoutes.put("/profile", async (c) => {
|
|
|
9309
10894
|
Date.now(),
|
|
9310
10895
|
user.userId
|
|
9311
10896
|
).run();
|
|
9312
|
-
await
|
|
10897
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9313
10898
|
db,
|
|
9314
10899
|
user.userId,
|
|
9315
10900
|
"profile.update",
|
|
@@ -9372,7 +10957,7 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
9372
10957
|
SELECT first_name, last_name FROM users WHERE id = ?
|
|
9373
10958
|
`);
|
|
9374
10959
|
const userData = await userStmt.bind(user.userId).first();
|
|
9375
|
-
await
|
|
10960
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9376
10961
|
db,
|
|
9377
10962
|
user.userId,
|
|
9378
10963
|
"profile.avatar_update",
|
|
@@ -9443,7 +11028,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9443
11028
|
dismissible: true
|
|
9444
11029
|
}));
|
|
9445
11030
|
}
|
|
9446
|
-
const validPassword = await
|
|
11031
|
+
const validPassword = await chunkT3YIKW2A_cjs.AuthManager.verifyPassword(currentPassword, userData.password_hash);
|
|
9447
11032
|
if (!validPassword) {
|
|
9448
11033
|
return c.html(renderAlert2({
|
|
9449
11034
|
type: "error",
|
|
@@ -9451,7 +11036,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9451
11036
|
dismissible: true
|
|
9452
11037
|
}));
|
|
9453
11038
|
}
|
|
9454
|
-
const newPasswordHash = await
|
|
11039
|
+
const newPasswordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(newPassword);
|
|
9455
11040
|
const historyStmt = db.prepare(`
|
|
9456
11041
|
INSERT INTO password_history (id, user_id, password_hash, created_at)
|
|
9457
11042
|
VALUES (?, ?, ?, ?)
|
|
@@ -9467,7 +11052,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
9467
11052
|
WHERE id = ?
|
|
9468
11053
|
`);
|
|
9469
11054
|
await updateStmt.bind(newPasswordHash, Date.now(), user.userId).run();
|
|
9470
|
-
await
|
|
11055
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9471
11056
|
db,
|
|
9472
11057
|
user.userId,
|
|
9473
11058
|
"profile.password_change",
|
|
@@ -9534,7 +11119,7 @@ userRoutes.get("/users", async (c) => {
|
|
|
9534
11119
|
`);
|
|
9535
11120
|
const countResult = await countStmt.bind(...params).first();
|
|
9536
11121
|
const totalUsers = countResult?.total || 0;
|
|
9537
|
-
await
|
|
11122
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9538
11123
|
db,
|
|
9539
11124
|
user.userId,
|
|
9540
11125
|
"users.list_view",
|
|
@@ -9636,12 +11221,12 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9636
11221
|
const user = c.get("user");
|
|
9637
11222
|
try {
|
|
9638
11223
|
const formData = await c.req.formData();
|
|
9639
|
-
const firstName =
|
|
9640
|
-
const lastName =
|
|
9641
|
-
const username =
|
|
11224
|
+
const firstName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
11225
|
+
const lastName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
11226
|
+
const username = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9642
11227
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9643
|
-
const phone =
|
|
9644
|
-
const bio =
|
|
11228
|
+
const phone = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
11229
|
+
const bio = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
9645
11230
|
const role = formData.get("role")?.toString() || "viewer";
|
|
9646
11231
|
const password = formData.get("password")?.toString() || "";
|
|
9647
11232
|
const confirmPassword = formData.get("confirm_password")?.toString() || "";
|
|
@@ -9688,7 +11273,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9688
11273
|
dismissible: true
|
|
9689
11274
|
}));
|
|
9690
11275
|
}
|
|
9691
|
-
const passwordHash = await
|
|
11276
|
+
const passwordHash = await chunkT3YIKW2A_cjs.AuthManager.hashPassword(password);
|
|
9692
11277
|
const userId = crypto.randomUUID();
|
|
9693
11278
|
const createStmt = db.prepare(`
|
|
9694
11279
|
INSERT INTO users (
|
|
@@ -9711,7 +11296,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
9711
11296
|
Date.now(),
|
|
9712
11297
|
Date.now()
|
|
9713
11298
|
).run();
|
|
9714
|
-
await
|
|
11299
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9715
11300
|
db,
|
|
9716
11301
|
user.userId,
|
|
9717
11302
|
"user!.create",
|
|
@@ -9749,7 +11334,7 @@ userRoutes.get("/users/:id", async (c) => {
|
|
|
9749
11334
|
if (!userRecord) {
|
|
9750
11335
|
return c.json({ error: "User not found" }, 404);
|
|
9751
11336
|
}
|
|
9752
|
-
await
|
|
11337
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9753
11338
|
db,
|
|
9754
11339
|
user.userId,
|
|
9755
11340
|
"user!.view",
|
|
@@ -9788,7 +11373,7 @@ userRoutes.get("/users/:id/edit", async (c) => {
|
|
|
9788
11373
|
const userId = c.req.param("id");
|
|
9789
11374
|
try {
|
|
9790
11375
|
const userStmt = db.prepare(`
|
|
9791
|
-
SELECT id, email, username, first_name, last_name, phone,
|
|
11376
|
+
SELECT id, email, username, first_name, last_name, phone, avatar_url,
|
|
9792
11377
|
role, is_active, email_verified, two_factor_enabled, created_at, last_login_at
|
|
9793
11378
|
FROM users
|
|
9794
11379
|
WHERE id = ?
|
|
@@ -9801,6 +11386,21 @@ userRoutes.get("/users/:id/edit", async (c) => {
|
|
|
9801
11386
|
dismissible: true
|
|
9802
11387
|
}), 404);
|
|
9803
11388
|
}
|
|
11389
|
+
const profileStmt = db.prepare(`
|
|
11390
|
+
SELECT display_name, bio, company, job_title, website, location, date_of_birth
|
|
11391
|
+
FROM user_profiles
|
|
11392
|
+
WHERE user_id = ?
|
|
11393
|
+
`);
|
|
11394
|
+
const profileData = await profileStmt.bind(userId).first();
|
|
11395
|
+
const profile = profileData ? {
|
|
11396
|
+
displayName: profileData.display_name,
|
|
11397
|
+
bio: profileData.bio,
|
|
11398
|
+
company: profileData.company,
|
|
11399
|
+
jobTitle: profileData.job_title,
|
|
11400
|
+
website: profileData.website,
|
|
11401
|
+
location: profileData.location,
|
|
11402
|
+
dateOfBirth: profileData.date_of_birth
|
|
11403
|
+
} : void 0;
|
|
9804
11404
|
const editData = {
|
|
9805
11405
|
id: userToEdit.id,
|
|
9806
11406
|
email: userToEdit.email,
|
|
@@ -9808,14 +11408,14 @@ userRoutes.get("/users/:id/edit", async (c) => {
|
|
|
9808
11408
|
firstName: userToEdit.first_name || "",
|
|
9809
11409
|
lastName: userToEdit.last_name || "",
|
|
9810
11410
|
phone: userToEdit.phone,
|
|
9811
|
-
bio: userToEdit.bio,
|
|
9812
11411
|
avatarUrl: userToEdit.avatar_url,
|
|
9813
11412
|
role: userToEdit.role,
|
|
9814
11413
|
isActive: Boolean(userToEdit.is_active),
|
|
9815
11414
|
emailVerified: Boolean(userToEdit.email_verified),
|
|
9816
11415
|
twoFactorEnabled: Boolean(userToEdit.two_factor_enabled),
|
|
9817
11416
|
createdAt: userToEdit.created_at,
|
|
9818
|
-
lastLoginAt: userToEdit.last_login_at
|
|
11417
|
+
lastLoginAt: userToEdit.last_login_at,
|
|
11418
|
+
profile
|
|
9819
11419
|
};
|
|
9820
11420
|
const pageData = {
|
|
9821
11421
|
userToEdit: editData,
|
|
@@ -9831,7 +11431,7 @@ userRoutes.get("/users/:id/edit", async (c) => {
|
|
|
9831
11431
|
console.error("User edit page error:", error);
|
|
9832
11432
|
return c.html(renderAlert2({
|
|
9833
11433
|
type: "error",
|
|
9834
|
-
message: "Failed to load user
|
|
11434
|
+
message: "Failed to load user. Please try again.",
|
|
9835
11435
|
dismissible: true
|
|
9836
11436
|
}), 500);
|
|
9837
11437
|
}
|
|
@@ -9842,15 +11442,22 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9842
11442
|
const userId = c.req.param("id");
|
|
9843
11443
|
try {
|
|
9844
11444
|
const formData = await c.req.formData();
|
|
9845
|
-
const firstName =
|
|
9846
|
-
const lastName =
|
|
9847
|
-
const username =
|
|
11445
|
+
const firstName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
11446
|
+
const lastName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
11447
|
+
const username = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("username")?.toString());
|
|
9848
11448
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
9849
|
-
const phone =
|
|
9850
|
-
const bio = chunkZWV3EBZ7_cjs.sanitizeInput(formData.get("bio")?.toString()) || null;
|
|
11449
|
+
const phone = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("phone")?.toString()) || null;
|
|
9851
11450
|
const role = formData.get("role")?.toString() || "viewer";
|
|
9852
11451
|
const isActive = formData.get("is_active") === "1";
|
|
9853
11452
|
const emailVerified = formData.get("email_verified") === "1";
|
|
11453
|
+
const profileDisplayName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("profile_display_name")?.toString()) || null;
|
|
11454
|
+
const profileBio = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("profile_bio")?.toString()) || null;
|
|
11455
|
+
const profileCompany = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("profile_company")?.toString()) || null;
|
|
11456
|
+
const profileJobTitle = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("profile_job_title")?.toString()) || null;
|
|
11457
|
+
const profileWebsite = formData.get("profile_website")?.toString()?.trim() || null;
|
|
11458
|
+
const profileLocation = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("profile_location")?.toString()) || null;
|
|
11459
|
+
const profileDateOfBirthStr = formData.get("profile_date_of_birth")?.toString()?.trim() || null;
|
|
11460
|
+
const profileDateOfBirth = profileDateOfBirthStr ? new Date(profileDateOfBirthStr).getTime() : null;
|
|
9854
11461
|
if (!firstName || !lastName || !username || !email) {
|
|
9855
11462
|
return c.html(renderAlert2({
|
|
9856
11463
|
type: "error",
|
|
@@ -9866,6 +11473,17 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9866
11473
|
dismissible: true
|
|
9867
11474
|
}));
|
|
9868
11475
|
}
|
|
11476
|
+
if (profileWebsite) {
|
|
11477
|
+
try {
|
|
11478
|
+
new URL(profileWebsite);
|
|
11479
|
+
} catch {
|
|
11480
|
+
return c.html(renderAlert2({
|
|
11481
|
+
type: "error",
|
|
11482
|
+
message: "Please enter a valid website URL.",
|
|
11483
|
+
dismissible: true
|
|
11484
|
+
}));
|
|
11485
|
+
}
|
|
11486
|
+
}
|
|
9869
11487
|
const checkStmt = db.prepare(`
|
|
9870
11488
|
SELECT id FROM users
|
|
9871
11489
|
WHERE (username = ? OR email = ?) AND id != ?
|
|
@@ -9874,14 +11492,14 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9874
11492
|
if (existingUser) {
|
|
9875
11493
|
return c.html(renderAlert2({
|
|
9876
11494
|
type: "error",
|
|
9877
|
-
message: "Username or email is already taken by another user
|
|
11495
|
+
message: "Username or email is already taken by another user.",
|
|
9878
11496
|
dismissible: true
|
|
9879
11497
|
}));
|
|
9880
11498
|
}
|
|
9881
11499
|
const updateStmt = db.prepare(`
|
|
9882
11500
|
UPDATE users SET
|
|
9883
11501
|
first_name = ?, last_name = ?, username = ?, email = ?,
|
|
9884
|
-
phone = ?,
|
|
11502
|
+
phone = ?, role = ?, is_active = ?, email_verified = ?,
|
|
9885
11503
|
updated_at = ?
|
|
9886
11504
|
WHERE id = ?
|
|
9887
11505
|
`);
|
|
@@ -9891,20 +11509,63 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9891
11509
|
username,
|
|
9892
11510
|
email,
|
|
9893
11511
|
phone,
|
|
9894
|
-
bio,
|
|
9895
11512
|
role,
|
|
9896
11513
|
isActive ? 1 : 0,
|
|
9897
11514
|
emailVerified ? 1 : 0,
|
|
9898
11515
|
Date.now(),
|
|
9899
11516
|
userId
|
|
9900
11517
|
).run();
|
|
9901
|
-
|
|
11518
|
+
const hasProfileData = profileDisplayName || profileBio || profileCompany || profileJobTitle || profileWebsite || profileLocation || profileDateOfBirth;
|
|
11519
|
+
if (hasProfileData) {
|
|
11520
|
+
const now = Date.now();
|
|
11521
|
+
const profileCheckStmt = db.prepare(`SELECT id FROM user_profiles WHERE user_id = ?`);
|
|
11522
|
+
const existingProfile = await profileCheckStmt.bind(userId).first();
|
|
11523
|
+
if (existingProfile) {
|
|
11524
|
+
const updateProfileStmt = db.prepare(`
|
|
11525
|
+
UPDATE user_profiles SET
|
|
11526
|
+
display_name = ?, bio = ?, company = ?, job_title = ?,
|
|
11527
|
+
website = ?, location = ?, date_of_birth = ?, updated_at = ?
|
|
11528
|
+
WHERE user_id = ?
|
|
11529
|
+
`);
|
|
11530
|
+
await updateProfileStmt.bind(
|
|
11531
|
+
profileDisplayName,
|
|
11532
|
+
profileBio,
|
|
11533
|
+
profileCompany,
|
|
11534
|
+
profileJobTitle,
|
|
11535
|
+
profileWebsite,
|
|
11536
|
+
profileLocation,
|
|
11537
|
+
profileDateOfBirth,
|
|
11538
|
+
now,
|
|
11539
|
+
userId
|
|
11540
|
+
).run();
|
|
11541
|
+
} else {
|
|
11542
|
+
const profileId = `profile_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
11543
|
+
const insertProfileStmt = db.prepare(`
|
|
11544
|
+
INSERT INTO user_profiles (id, user_id, display_name, bio, company, job_title, website, location, date_of_birth, created_at, updated_at)
|
|
11545
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
11546
|
+
`);
|
|
11547
|
+
await insertProfileStmt.bind(
|
|
11548
|
+
profileId,
|
|
11549
|
+
userId,
|
|
11550
|
+
profileDisplayName,
|
|
11551
|
+
profileBio,
|
|
11552
|
+
profileCompany,
|
|
11553
|
+
profileJobTitle,
|
|
11554
|
+
profileWebsite,
|
|
11555
|
+
profileLocation,
|
|
11556
|
+
profileDateOfBirth,
|
|
11557
|
+
now,
|
|
11558
|
+
now
|
|
11559
|
+
).run();
|
|
11560
|
+
}
|
|
11561
|
+
}
|
|
11562
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9902
11563
|
db,
|
|
9903
11564
|
user.userId,
|
|
9904
|
-
"user
|
|
11565
|
+
"user.update",
|
|
9905
11566
|
"users",
|
|
9906
11567
|
userId,
|
|
9907
|
-
{ fields: ["first_name", "last_name", "username", "email", "phone", "
|
|
11568
|
+
{ fields: ["first_name", "last_name", "username", "email", "phone", "role", "is_active", "email_verified", "profile"] },
|
|
9908
11569
|
c.req.header("x-forwarded-for") || c.req.header("cf-connecting-ip"),
|
|
9909
11570
|
c.req.header("user-agent")
|
|
9910
11571
|
);
|
|
@@ -9917,7 +11578,7 @@ userRoutes.put("/users/:id", async (c) => {
|
|
|
9917
11578
|
console.error("User update error:", error);
|
|
9918
11579
|
return c.html(renderAlert2({
|
|
9919
11580
|
type: "error",
|
|
9920
|
-
message: "Failed to update user
|
|
11581
|
+
message: "Failed to update user. Please try again.",
|
|
9921
11582
|
dismissible: true
|
|
9922
11583
|
}));
|
|
9923
11584
|
}
|
|
@@ -9943,7 +11604,7 @@ userRoutes.post("/users/:id/toggle", async (c) => {
|
|
|
9943
11604
|
UPDATE users SET is_active = ?, updated_at = ? WHERE id = ?
|
|
9944
11605
|
`);
|
|
9945
11606
|
await toggleStmt.bind(active ? 1 : 0, Date.now(), userId).run();
|
|
9946
|
-
await
|
|
11607
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9947
11608
|
db,
|
|
9948
11609
|
user.userId,
|
|
9949
11610
|
active ? "user.activate" : "user.deactivate",
|
|
@@ -9984,7 +11645,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
9984
11645
|
DELETE FROM users WHERE id = ?
|
|
9985
11646
|
`);
|
|
9986
11647
|
await deleteStmt.bind(userId).run();
|
|
9987
|
-
await
|
|
11648
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
9988
11649
|
db,
|
|
9989
11650
|
user.userId,
|
|
9990
11651
|
"user!.hard_delete",
|
|
@@ -10003,7 +11664,7 @@ userRoutes.delete("/users/:id", async (c) => {
|
|
|
10003
11664
|
UPDATE users SET is_active = 0, updated_at = ? WHERE id = ?
|
|
10004
11665
|
`);
|
|
10005
11666
|
await deleteStmt.bind(Date.now(), userId).run();
|
|
10006
|
-
await
|
|
11667
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10007
11668
|
db,
|
|
10008
11669
|
user.userId,
|
|
10009
11670
|
"user!.soft_delete",
|
|
@@ -10030,8 +11691,8 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
10030
11691
|
const formData = await c.req.formData();
|
|
10031
11692
|
const email = formData.get("email")?.toString()?.trim().toLowerCase() || "";
|
|
10032
11693
|
const role = formData.get("role")?.toString()?.trim() || "viewer";
|
|
10033
|
-
const firstName =
|
|
10034
|
-
const lastName =
|
|
11694
|
+
const firstName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("first_name")?.toString());
|
|
11695
|
+
const lastName = chunkYMTTGHEK_cjs.sanitizeInput(formData.get("last_name")?.toString());
|
|
10035
11696
|
if (!email || !firstName || !lastName) {
|
|
10036
11697
|
return c.json({ error: "Email, first name, and last name are required" }, 400);
|
|
10037
11698
|
}
|
|
@@ -10069,7 +11730,7 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
10069
11730
|
Date.now(),
|
|
10070
11731
|
Date.now()
|
|
10071
11732
|
).run();
|
|
10072
|
-
await
|
|
11733
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10073
11734
|
db,
|
|
10074
11735
|
user.userId,
|
|
10075
11736
|
"user!.invite_sent",
|
|
@@ -10126,7 +11787,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
|
10126
11787
|
Date.now(),
|
|
10127
11788
|
userId
|
|
10128
11789
|
).run();
|
|
10129
|
-
await
|
|
11790
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10130
11791
|
db,
|
|
10131
11792
|
user.userId,
|
|
10132
11793
|
"user!.invitation_resent",
|
|
@@ -10162,7 +11823,7 @@ userRoutes.delete("/cancel-invitation/:id", async (c) => {
|
|
|
10162
11823
|
}
|
|
10163
11824
|
const deleteStmt = db.prepare(`DELETE FROM users WHERE id = ?`);
|
|
10164
11825
|
await deleteStmt.bind(userId).run();
|
|
10165
|
-
await
|
|
11826
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10166
11827
|
db,
|
|
10167
11828
|
user.userId,
|
|
10168
11829
|
"user!.invitation_cancelled",
|
|
@@ -10245,7 +11906,7 @@ userRoutes.get("/activity-logs", async (c) => {
|
|
|
10245
11906
|
...log,
|
|
10246
11907
|
details: log.details ? JSON.parse(log.details) : null
|
|
10247
11908
|
}));
|
|
10248
|
-
await
|
|
11909
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10249
11910
|
db,
|
|
10250
11911
|
user.userId,
|
|
10251
11912
|
"activity.logs_viewed",
|
|
@@ -10352,7 +12013,7 @@ userRoutes.get("/activity-logs/export", async (c) => {
|
|
|
10352
12013
|
csvRows.push(row.join(","));
|
|
10353
12014
|
}
|
|
10354
12015
|
const csvContent = csvRows.join("\n");
|
|
10355
|
-
await
|
|
12016
|
+
await chunkT3YIKW2A_cjs.logActivity(
|
|
10356
12017
|
db,
|
|
10357
12018
|
user.userId,
|
|
10358
12019
|
"activity.logs_exported",
|
|
@@ -10570,7 +12231,7 @@ function getFileIcon(mimeType) {
|
|
|
10570
12231
|
}
|
|
10571
12232
|
|
|
10572
12233
|
// src/templates/pages/admin-media-library.template.ts
|
|
10573
|
-
|
|
12234
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
10574
12235
|
function renderMediaLibraryPage(data) {
|
|
10575
12236
|
const pageContent = `
|
|
10576
12237
|
<div>
|
|
@@ -11505,7 +13166,7 @@ function renderMediaLibraryPage(data) {
|
|
|
11505
13166
|
version: data.version,
|
|
11506
13167
|
content: pageContent
|
|
11507
13168
|
};
|
|
11508
|
-
return
|
|
13169
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
11509
13170
|
}
|
|
11510
13171
|
|
|
11511
13172
|
// src/templates/components/media-file-details.template.ts
|
|
@@ -11691,7 +13352,7 @@ var fileValidationSchema2 = zod.z.object({
|
|
|
11691
13352
|
// 50MB max
|
|
11692
13353
|
});
|
|
11693
13354
|
var adminMediaRoutes = new hono.Hono();
|
|
11694
|
-
adminMediaRoutes.use("*",
|
|
13355
|
+
adminMediaRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
11695
13356
|
adminMediaRoutes.get("/", async (c) => {
|
|
11696
13357
|
try {
|
|
11697
13358
|
const user = c.get("user");
|
|
@@ -12277,7 +13938,7 @@ adminMediaRoutes.put("/:id", async (c) => {
|
|
|
12277
13938
|
`);
|
|
12278
13939
|
}
|
|
12279
13940
|
});
|
|
12280
|
-
adminMediaRoutes.delete("/cleanup",
|
|
13941
|
+
adminMediaRoutes.delete("/cleanup", chunkT3YIKW2A_cjs.requireRole("admin"), async (c) => {
|
|
12281
13942
|
try {
|
|
12282
13943
|
const db = c.env.DB;
|
|
12283
13944
|
const allMediaStmt = db.prepare("SELECT id, r2_key, filename FROM media WHERE deleted_at IS NULL");
|
|
@@ -12527,7 +14188,7 @@ function formatFileSize(bytes) {
|
|
|
12527
14188
|
}
|
|
12528
14189
|
|
|
12529
14190
|
// src/templates/pages/admin-plugins-list.template.ts
|
|
12530
|
-
|
|
14191
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
12531
14192
|
function renderPluginsListPage(data) {
|
|
12532
14193
|
const categories = [
|
|
12533
14194
|
{ value: "content", label: "Content Management" },
|
|
@@ -13007,7 +14668,7 @@ function renderPluginsListPage(data) {
|
|
|
13007
14668
|
version: data.version,
|
|
13008
14669
|
content: pageContent
|
|
13009
14670
|
};
|
|
13010
|
-
return
|
|
14671
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
13011
14672
|
}
|
|
13012
14673
|
function renderPluginCard(plugin) {
|
|
13013
14674
|
const statusColors = {
|
|
@@ -13662,7 +15323,7 @@ function renderPluginSettingsPage(data) {
|
|
|
13662
15323
|
user,
|
|
13663
15324
|
content: pageContent
|
|
13664
15325
|
};
|
|
13665
|
-
return
|
|
15326
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout(layoutData);
|
|
13666
15327
|
}
|
|
13667
15328
|
function renderStatusBadge(status) {
|
|
13668
15329
|
const statusColors = {
|
|
@@ -14003,7 +15664,7 @@ function formatTimestamp(timestamp) {
|
|
|
14003
15664
|
|
|
14004
15665
|
// src/routes/admin-plugins.ts
|
|
14005
15666
|
var adminPluginRoutes = new hono.Hono();
|
|
14006
|
-
adminPluginRoutes.use("*",
|
|
15667
|
+
adminPluginRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
14007
15668
|
var AVAILABLE_PLUGINS = [
|
|
14008
15669
|
{
|
|
14009
15670
|
id: "third-party-faq",
|
|
@@ -14108,6 +15769,19 @@ var AVAILABLE_PLUGINS = [
|
|
|
14108
15769
|
permissions: [],
|
|
14109
15770
|
dependencies: [],
|
|
14110
15771
|
is_core: true
|
|
15772
|
+
},
|
|
15773
|
+
{
|
|
15774
|
+
id: "ai-search",
|
|
15775
|
+
name: "ai-search-plugin",
|
|
15776
|
+
display_name: "AI Search",
|
|
15777
|
+
description: "Advanced search with Cloudflare AI Search. Full-text search, semantic search, and advanced filtering across all content collections.",
|
|
15778
|
+
version: "1.0.0",
|
|
15779
|
+
author: "SonicJS Team",
|
|
15780
|
+
category: "search",
|
|
15781
|
+
icon: "\u{1F50D}",
|
|
15782
|
+
permissions: [],
|
|
15783
|
+
dependencies: [],
|
|
15784
|
+
is_core: true
|
|
14111
15785
|
}
|
|
14112
15786
|
];
|
|
14113
15787
|
adminPluginRoutes.get("/", async (c) => {
|
|
@@ -14117,7 +15791,7 @@ adminPluginRoutes.get("/", async (c) => {
|
|
|
14117
15791
|
if (user?.role !== "admin") {
|
|
14118
15792
|
return c.text("Access denied", 403);
|
|
14119
15793
|
}
|
|
14120
|
-
const pluginService = new
|
|
15794
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14121
15795
|
let installedPlugins = [];
|
|
14122
15796
|
let stats = { total: 0, active: 0, inactive: 0, errors: 0, uninstalled: 0 };
|
|
14123
15797
|
try {
|
|
@@ -14186,10 +15860,13 @@ adminPluginRoutes.get("/:id", async (c) => {
|
|
|
14186
15860
|
const user = c.get("user");
|
|
14187
15861
|
const db = c.env.DB;
|
|
14188
15862
|
const pluginId = c.req.param("id");
|
|
15863
|
+
if (pluginId === "ai-search") {
|
|
15864
|
+
return c.text("", 404);
|
|
15865
|
+
}
|
|
14189
15866
|
if (user?.role !== "admin") {
|
|
14190
15867
|
return c.redirect("/admin/plugins");
|
|
14191
15868
|
}
|
|
14192
|
-
const pluginService = new
|
|
15869
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14193
15870
|
const plugin = await pluginService.getPlugin(pluginId);
|
|
14194
15871
|
if (!plugin) {
|
|
14195
15872
|
return c.text("Plugin not found", 404);
|
|
@@ -14243,7 +15920,7 @@ adminPluginRoutes.post("/:id/activate", async (c) => {
|
|
|
14243
15920
|
if (user?.role !== "admin") {
|
|
14244
15921
|
return c.json({ error: "Access denied" }, 403);
|
|
14245
15922
|
}
|
|
14246
|
-
const pluginService = new
|
|
15923
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14247
15924
|
await pluginService.activatePlugin(pluginId);
|
|
14248
15925
|
return c.json({ success: true });
|
|
14249
15926
|
} catch (error) {
|
|
@@ -14260,7 +15937,7 @@ adminPluginRoutes.post("/:id/deactivate", async (c) => {
|
|
|
14260
15937
|
if (user?.role !== "admin") {
|
|
14261
15938
|
return c.json({ error: "Access denied" }, 403);
|
|
14262
15939
|
}
|
|
14263
|
-
const pluginService = new
|
|
15940
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14264
15941
|
await pluginService.deactivatePlugin(pluginId);
|
|
14265
15942
|
return c.json({ success: true });
|
|
14266
15943
|
} catch (error) {
|
|
@@ -14277,7 +15954,7 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
14277
15954
|
return c.json({ error: "Access denied" }, 403);
|
|
14278
15955
|
}
|
|
14279
15956
|
const body = await c.req.json();
|
|
14280
|
-
const pluginService = new
|
|
15957
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14281
15958
|
if (body.name === "faq-plugin") {
|
|
14282
15959
|
const faqPlugin = await pluginService.installPlugin({
|
|
14283
15960
|
id: "third-party-faq",
|
|
@@ -14478,6 +16155,33 @@ adminPluginRoutes.post("/install", async (c) => {
|
|
|
14478
16155
|
});
|
|
14479
16156
|
return c.json({ success: true, plugin: easyMdxPlugin2 });
|
|
14480
16157
|
}
|
|
16158
|
+
if (body.name === "ai-search-plugin" || body.name === "ai-search") {
|
|
16159
|
+
const defaultSettings = {
|
|
16160
|
+
enabled: true,
|
|
16161
|
+
ai_mode_enabled: true,
|
|
16162
|
+
selected_collections: [],
|
|
16163
|
+
dismissed_collections: [],
|
|
16164
|
+
autocomplete_enabled: true,
|
|
16165
|
+
cache_duration: 1,
|
|
16166
|
+
results_limit: 20,
|
|
16167
|
+
index_media: false
|
|
16168
|
+
};
|
|
16169
|
+
const aiSearchPlugin = await pluginService.installPlugin({
|
|
16170
|
+
id: "ai-search",
|
|
16171
|
+
name: "ai-search-plugin",
|
|
16172
|
+
display_name: "AI Search",
|
|
16173
|
+
description: "Advanced search with Cloudflare AI Search. Full-text search, semantic search, and advanced filtering across all content collections.",
|
|
16174
|
+
version: "1.0.0",
|
|
16175
|
+
author: "SonicJS Team",
|
|
16176
|
+
category: "search",
|
|
16177
|
+
icon: "\u{1F50D}",
|
|
16178
|
+
permissions: [],
|
|
16179
|
+
dependencies: [],
|
|
16180
|
+
is_core: true,
|
|
16181
|
+
settings: defaultSettings
|
|
16182
|
+
});
|
|
16183
|
+
return c.json({ success: true, plugin: aiSearchPlugin });
|
|
16184
|
+
}
|
|
14481
16185
|
if (body.name === "turnstile-plugin") {
|
|
14482
16186
|
const turnstilePlugin = await pluginService.installPlugin({
|
|
14483
16187
|
id: "turnstile",
|
|
@@ -14520,7 +16224,7 @@ adminPluginRoutes.post("/:id/uninstall", async (c) => {
|
|
|
14520
16224
|
if (user?.role !== "admin") {
|
|
14521
16225
|
return c.json({ error: "Access denied" }, 403);
|
|
14522
16226
|
}
|
|
14523
|
-
const pluginService = new
|
|
16227
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14524
16228
|
await pluginService.uninstallPlugin(pluginId);
|
|
14525
16229
|
return c.json({ success: true });
|
|
14526
16230
|
} catch (error) {
|
|
@@ -14538,7 +16242,7 @@ adminPluginRoutes.post("/:id/settings", async (c) => {
|
|
|
14538
16242
|
return c.json({ error: "Access denied" }, 403);
|
|
14539
16243
|
}
|
|
14540
16244
|
const settings = await c.req.json();
|
|
14541
|
-
const pluginService = new
|
|
16245
|
+
const pluginService = new chunkMPT5PA6U_cjs.PluginService(db);
|
|
14542
16246
|
await pluginService.updatePluginSettings(pluginId, settings);
|
|
14543
16247
|
return c.json({ success: true });
|
|
14544
16248
|
} catch (error) {
|
|
@@ -14559,7 +16263,7 @@ function formatLastUpdated(timestamp) {
|
|
|
14559
16263
|
}
|
|
14560
16264
|
|
|
14561
16265
|
// src/templates/pages/admin-logs-list.template.ts
|
|
14562
|
-
|
|
16266
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
14563
16267
|
function renderLogsListPage(data) {
|
|
14564
16268
|
const { logs, pagination, filters, user } = data;
|
|
14565
16269
|
const content = `
|
|
@@ -14870,7 +16574,7 @@ function renderLogsListPage(data) {
|
|
|
14870
16574
|
user,
|
|
14871
16575
|
content
|
|
14872
16576
|
};
|
|
14873
|
-
return
|
|
16577
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
14874
16578
|
}
|
|
14875
16579
|
function renderLogDetailsPage(data) {
|
|
14876
16580
|
const { log, user } = data;
|
|
@@ -15082,7 +16786,7 @@ function renderLogDetailsPage(data) {
|
|
|
15082
16786
|
</div>
|
|
15083
16787
|
</div>
|
|
15084
16788
|
`;
|
|
15085
|
-
return
|
|
16789
|
+
return chunkBZC4FYW7_cjs.adminLayoutV2({
|
|
15086
16790
|
title: `Log Details - ${log.id}`,
|
|
15087
16791
|
user,
|
|
15088
16792
|
content
|
|
@@ -15325,7 +17029,7 @@ function renderLogConfigPage(data) {
|
|
|
15325
17029
|
|
|
15326
17030
|
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
|
|
15327
17031
|
`;
|
|
15328
|
-
return
|
|
17032
|
+
return chunkBZC4FYW7_cjs.adminLayoutV2({
|
|
15329
17033
|
title: "Log Configuration",
|
|
15330
17034
|
user,
|
|
15331
17035
|
content
|
|
@@ -15334,7 +17038,7 @@ function renderLogConfigPage(data) {
|
|
|
15334
17038
|
|
|
15335
17039
|
// src/routes/admin-logs.ts
|
|
15336
17040
|
var adminLogsRoutes = new hono.Hono();
|
|
15337
|
-
adminLogsRoutes.use("*",
|
|
17041
|
+
adminLogsRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
15338
17042
|
adminLogsRoutes.get("/", async (c) => {
|
|
15339
17043
|
try {
|
|
15340
17044
|
const user = c.get("user");
|
|
@@ -15706,7 +17410,7 @@ adminDesignRoutes.get("/", (c) => {
|
|
|
15706
17410
|
role: user.role
|
|
15707
17411
|
} : void 0
|
|
15708
17412
|
};
|
|
15709
|
-
return c.html(
|
|
17413
|
+
return c.html(chunkBZC4FYW7_cjs.renderDesignPage(pageData));
|
|
15710
17414
|
});
|
|
15711
17415
|
var adminCheckboxRoutes = new hono.Hono();
|
|
15712
17416
|
adminCheckboxRoutes.get("/", (c) => {
|
|
@@ -15718,7 +17422,7 @@ adminCheckboxRoutes.get("/", (c) => {
|
|
|
15718
17422
|
role: user.role
|
|
15719
17423
|
} : void 0
|
|
15720
17424
|
};
|
|
15721
|
-
return c.html(
|
|
17425
|
+
return c.html(chunkBZC4FYW7_cjs.renderCheckboxPage(pageData));
|
|
15722
17426
|
});
|
|
15723
17427
|
|
|
15724
17428
|
// src/templates/pages/admin-testimonials-form.template.ts
|
|
@@ -15746,7 +17450,7 @@ function renderTestimonialsForm(data) {
|
|
|
15746
17450
|
</div>
|
|
15747
17451
|
</div>
|
|
15748
17452
|
|
|
15749
|
-
${message ?
|
|
17453
|
+
${message ? chunkBZC4FYW7_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
|
|
15750
17454
|
|
|
15751
17455
|
<!-- Form -->
|
|
15752
17456
|
<div class="backdrop-blur-xl bg-white/10 rounded-xl border border-white/20 shadow-2xl">
|
|
@@ -15975,7 +17679,7 @@ function renderTestimonialsForm(data) {
|
|
|
15975
17679
|
user: data.user,
|
|
15976
17680
|
content: pageContent
|
|
15977
17681
|
};
|
|
15978
|
-
return
|
|
17682
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout(layoutData);
|
|
15979
17683
|
}
|
|
15980
17684
|
function escapeHtml4(unsafe) {
|
|
15981
17685
|
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -16001,7 +17705,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
|
|
|
16001
17705
|
const offset = (currentPage - 1) * limit;
|
|
16002
17706
|
const db = c.env?.DB;
|
|
16003
17707
|
if (!db) {
|
|
16004
|
-
return c.html(
|
|
17708
|
+
return c.html(chunkBZC4FYW7_cjs.renderTestimonialsList({
|
|
16005
17709
|
testimonials: [],
|
|
16006
17710
|
totalCount: 0,
|
|
16007
17711
|
currentPage: 1,
|
|
@@ -16041,7 +17745,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
|
|
|
16041
17745
|
`;
|
|
16042
17746
|
const { results: testimonials } = await db.prepare(dataQuery).bind(...params, limit, offset).all();
|
|
16043
17747
|
const totalPages = Math.ceil(totalCount / limit);
|
|
16044
|
-
return c.html(
|
|
17748
|
+
return c.html(chunkBZC4FYW7_cjs.renderTestimonialsList({
|
|
16045
17749
|
testimonials: testimonials || [],
|
|
16046
17750
|
totalCount,
|
|
16047
17751
|
currentPage,
|
|
@@ -16055,7 +17759,7 @@ adminTestimonialsRoutes.get("/", async (c) => {
|
|
|
16055
17759
|
} catch (error) {
|
|
16056
17760
|
console.error("Error fetching testimonials:", error);
|
|
16057
17761
|
const user = c.get("user");
|
|
16058
|
-
return c.html(
|
|
17762
|
+
return c.html(chunkBZC4FYW7_cjs.renderTestimonialsList({
|
|
16059
17763
|
testimonials: [],
|
|
16060
17764
|
totalCount: 0,
|
|
16061
17765
|
currentPage: 1,
|
|
@@ -16374,7 +18078,7 @@ function renderCodeExamplesForm(data) {
|
|
|
16374
18078
|
</div>
|
|
16375
18079
|
</div>
|
|
16376
18080
|
|
|
16377
|
-
${message ?
|
|
18081
|
+
${message ? chunkBZC4FYW7_cjs.renderAlert({ type: messageType || "info", message, dismissible: true }) : ""}
|
|
16378
18082
|
|
|
16379
18083
|
<!-- Form -->
|
|
16380
18084
|
<div class="backdrop-blur-xl bg-white/10 rounded-xl border border-white/20 shadow-2xl">
|
|
@@ -16644,7 +18348,7 @@ function renderCodeExamplesForm(data) {
|
|
|
16644
18348
|
user: data.user,
|
|
16645
18349
|
content: pageContent
|
|
16646
18350
|
};
|
|
16647
|
-
return
|
|
18351
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout(layoutData);
|
|
16648
18352
|
}
|
|
16649
18353
|
function escapeHtml5(unsafe) {
|
|
16650
18354
|
return unsafe.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
@@ -16671,7 +18375,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
|
|
|
16671
18375
|
const offset = (currentPage - 1) * limit;
|
|
16672
18376
|
const db = c.env?.DB;
|
|
16673
18377
|
if (!db) {
|
|
16674
|
-
return c.html(
|
|
18378
|
+
return c.html(chunkBZC4FYW7_cjs.renderCodeExamplesList({
|
|
16675
18379
|
codeExamples: [],
|
|
16676
18380
|
totalCount: 0,
|
|
16677
18381
|
currentPage: 1,
|
|
@@ -16711,7 +18415,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
|
|
|
16711
18415
|
`;
|
|
16712
18416
|
const { results: codeExamples } = await db.prepare(dataQuery).bind(...params, limit, offset).all();
|
|
16713
18417
|
const totalPages = Math.ceil(totalCount / limit);
|
|
16714
|
-
return c.html(
|
|
18418
|
+
return c.html(chunkBZC4FYW7_cjs.renderCodeExamplesList({
|
|
16715
18419
|
codeExamples: codeExamples || [],
|
|
16716
18420
|
totalCount,
|
|
16717
18421
|
currentPage,
|
|
@@ -16725,7 +18429,7 @@ adminCodeExamplesRoutes.get("/", async (c) => {
|
|
|
16725
18429
|
} catch (error) {
|
|
16726
18430
|
console.error("Error fetching code examples:", error);
|
|
16727
18431
|
const user = c.get("user");
|
|
16728
|
-
return c.html(
|
|
18432
|
+
return c.html(chunkBZC4FYW7_cjs.renderCodeExamplesList({
|
|
16729
18433
|
codeExamples: [],
|
|
16730
18434
|
totalCount: 0,
|
|
16731
18435
|
currentPage: 1,
|
|
@@ -17114,7 +18818,7 @@ function renderDashboardPage(data) {
|
|
|
17114
18818
|
version: data.version,
|
|
17115
18819
|
content: pageContent
|
|
17116
18820
|
};
|
|
17117
|
-
return
|
|
18821
|
+
return chunkBZC4FYW7_cjs.renderAdminLayout(layoutData);
|
|
17118
18822
|
}
|
|
17119
18823
|
function renderStatsCards(stats) {
|
|
17120
18824
|
const cards = [
|
|
@@ -17662,9 +19366,9 @@ function renderStorageUsage(databaseSizeBytes, mediaSizeBytes) {
|
|
|
17662
19366
|
}
|
|
17663
19367
|
|
|
17664
19368
|
// src/routes/admin-dashboard.ts
|
|
17665
|
-
var VERSION =
|
|
19369
|
+
var VERSION = chunkYMTTGHEK_cjs.getCoreVersion();
|
|
17666
19370
|
var router = new hono.Hono();
|
|
17667
|
-
router.use("*",
|
|
19371
|
+
router.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
17668
19372
|
router.get("/", async (c) => {
|
|
17669
19373
|
const user = c.get("user");
|
|
17670
19374
|
try {
|
|
@@ -17889,7 +19593,7 @@ router.get("/system-status", async (c) => {
|
|
|
17889
19593
|
});
|
|
17890
19594
|
|
|
17891
19595
|
// src/templates/pages/admin-collections-list.template.ts
|
|
17892
|
-
|
|
19596
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
17893
19597
|
|
|
17894
19598
|
// src/templates/components/table.template.ts
|
|
17895
19599
|
function renderTable2(data) {
|
|
@@ -18363,11 +20067,11 @@ function renderCollectionsListPage(data) {
|
|
|
18363
20067
|
version: data.version,
|
|
18364
20068
|
content: pageContent
|
|
18365
20069
|
};
|
|
18366
|
-
return
|
|
20070
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
18367
20071
|
}
|
|
18368
20072
|
|
|
18369
20073
|
// src/templates/pages/admin-collections-form.template.ts
|
|
18370
|
-
|
|
20074
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
18371
20075
|
function getFieldTypeBadge(fieldType) {
|
|
18372
20076
|
const typeLabels = {
|
|
18373
20077
|
"text": "Text",
|
|
@@ -18628,7 +20332,7 @@ function renderCollectionFormPage(data) {
|
|
|
18628
20332
|
}
|
|
18629
20333
|
</style>
|
|
18630
20334
|
|
|
18631
|
-
${
|
|
20335
|
+
${chunkBZC4FYW7_cjs.renderForm(formData)}
|
|
18632
20336
|
|
|
18633
20337
|
${isEdit && data.managed ? `
|
|
18634
20338
|
<!-- Read-Only Fields Display for Managed Collections -->
|
|
@@ -19417,12 +21121,12 @@ function renderCollectionFormPage(data) {
|
|
|
19417
21121
|
version: data.version,
|
|
19418
21122
|
content: pageContent
|
|
19419
21123
|
};
|
|
19420
|
-
return
|
|
21124
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
19421
21125
|
}
|
|
19422
21126
|
|
|
19423
21127
|
// src/routes/admin-collections.ts
|
|
19424
21128
|
var adminCollectionsRoutes = new hono.Hono();
|
|
19425
|
-
adminCollectionsRoutes.use("*",
|
|
21129
|
+
adminCollectionsRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
19426
21130
|
adminCollectionsRoutes.get("/", async (c) => {
|
|
19427
21131
|
try {
|
|
19428
21132
|
const user = c.get("user");
|
|
@@ -20138,7 +21842,7 @@ adminCollectionsRoutes.post("/:collectionId/fields/reorder", async (c) => {
|
|
|
20138
21842
|
});
|
|
20139
21843
|
|
|
20140
21844
|
// src/templates/pages/admin-settings.template.ts
|
|
20141
|
-
|
|
21845
|
+
chunkBZC4FYW7_cjs.init_admin_layout_catalyst_template();
|
|
20142
21846
|
function renderSettingsPage(data) {
|
|
20143
21847
|
const activeTab = data.activeTab || "general";
|
|
20144
21848
|
const pageContent = `
|
|
@@ -20520,7 +22224,7 @@ function renderSettingsPage(data) {
|
|
|
20520
22224
|
version: data.version,
|
|
20521
22225
|
content: pageContent
|
|
20522
22226
|
};
|
|
20523
|
-
return
|
|
22227
|
+
return chunkBZC4FYW7_cjs.renderAdminLayoutCatalyst(layoutData);
|
|
20524
22228
|
}
|
|
20525
22229
|
function renderTabButton(tabId, label, iconPath, activeTab) {
|
|
20526
22230
|
const isActive = activeTab === tabId;
|
|
@@ -21602,7 +23306,7 @@ function renderDatabaseToolsSettings(settings) {
|
|
|
21602
23306
|
|
|
21603
23307
|
// src/routes/admin-settings.ts
|
|
21604
23308
|
var adminSettingsRoutes = new hono.Hono();
|
|
21605
|
-
adminSettingsRoutes.use("*",
|
|
23309
|
+
adminSettingsRoutes.use("*", chunkT3YIKW2A_cjs.requireAuth());
|
|
21606
23310
|
function getMockSettings(user) {
|
|
21607
23311
|
return {
|
|
21608
23312
|
general: {
|
|
@@ -21770,7 +23474,7 @@ adminSettingsRoutes.get("/database-tools", (c) => {
|
|
|
21770
23474
|
adminSettingsRoutes.get("/api/migrations/status", async (c) => {
|
|
21771
23475
|
try {
|
|
21772
23476
|
const db = c.env.DB;
|
|
21773
|
-
const migrationService = new
|
|
23477
|
+
const migrationService = new chunkIIRVZSP2_cjs.MigrationService(db);
|
|
21774
23478
|
const status = await migrationService.getMigrationStatus();
|
|
21775
23479
|
return c.json({
|
|
21776
23480
|
success: true,
|
|
@@ -21794,7 +23498,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
21794
23498
|
}, 403);
|
|
21795
23499
|
}
|
|
21796
23500
|
const db = c.env.DB;
|
|
21797
|
-
const migrationService = new
|
|
23501
|
+
const migrationService = new chunkIIRVZSP2_cjs.MigrationService(db);
|
|
21798
23502
|
const result = await migrationService.runPendingMigrations();
|
|
21799
23503
|
return c.json({
|
|
21800
23504
|
success: result.success,
|
|
@@ -21812,7 +23516,7 @@ adminSettingsRoutes.post("/api/migrations/run", async (c) => {
|
|
|
21812
23516
|
adminSettingsRoutes.get("/api/migrations/validate", async (c) => {
|
|
21813
23517
|
try {
|
|
21814
23518
|
const db = c.env.DB;
|
|
21815
|
-
const migrationService = new
|
|
23519
|
+
const migrationService = new chunkIIRVZSP2_cjs.MigrationService(db);
|
|
21816
23520
|
const validation = await migrationService.validateSchema();
|
|
21817
23521
|
return c.json({
|
|
21818
23522
|
success: true,
|
|
@@ -22053,9 +23757,8 @@ exports.api_default = api_default;
|
|
|
22053
23757
|
exports.api_media_default = api_media_default;
|
|
22054
23758
|
exports.api_system_default = api_system_default;
|
|
22055
23759
|
exports.auth_default = auth_default;
|
|
22056
|
-
exports.checkAdminUserExists = checkAdminUserExists;
|
|
22057
23760
|
exports.router = router;
|
|
22058
23761
|
exports.test_cleanup_default = test_cleanup_default;
|
|
22059
23762
|
exports.userRoutes = userRoutes;
|
|
22060
|
-
//# sourceMappingURL=chunk-
|
|
22061
|
-
//# sourceMappingURL=chunk-
|
|
23763
|
+
//# sourceMappingURL=chunk-N7TDLOUE.cjs.map
|
|
23764
|
+
//# sourceMappingURL=chunk-N7TDLOUE.cjs.map
|