@sonicjs-cms/core 2.0.8 → 2.0.10
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/README.md +1 -1
- package/dist/{chunk-CHMO2DOC.cjs → chunk-3PHG75W4.cjs} +3 -3
- package/dist/chunk-3PHG75W4.cjs.map +1 -0
- package/dist/chunk-6FR25MPC.js.map +1 -1
- package/dist/{chunk-KHNSPJ6X.cjs → chunk-CAP6QQR2.cjs} +5 -5
- package/dist/{chunk-KHNSPJ6X.cjs.map → chunk-CAP6QQR2.cjs.map} +1 -1
- package/dist/{chunk-4MBTSUI6.js → chunk-COBUPOMD.js} +22 -6
- package/dist/chunk-COBUPOMD.js.map +1 -0
- package/dist/chunk-DOR2IU73.cjs.map +1 -1
- package/dist/{chunk-HJZOA2O5.cjs → chunk-F5ESJXI2.cjs} +25 -3
- package/dist/chunk-F5ESJXI2.cjs.map +1 -0
- package/dist/chunk-FICTAGD4.js.map +1 -1
- package/dist/{chunk-LS5CMDNL.js → chunk-FTMKKKNH.js} +3 -3
- package/dist/{chunk-LS5CMDNL.js.map → chunk-FTMKKKNH.js.map} +1 -1
- package/dist/{chunk-EAELJXRV.js → chunk-HKEK7UNV.js} +25 -3
- package/dist/chunk-HKEK7UNV.js.map +1 -0
- package/dist/{chunk-3R7EQNGO.cjs → chunk-HXA5QSI3.cjs} +15 -11
- package/dist/chunk-HXA5QSI3.cjs.map +1 -0
- package/dist/{chunk-YHLLVUJC.js → chunk-LW33AOBF.js} +6 -8
- package/dist/chunk-LW33AOBF.js.map +1 -0
- package/dist/{chunk-7XEESVSX.cjs → chunk-MU3MR2QR.cjs} +6 -8
- package/dist/chunk-MU3MR2QR.cjs.map +1 -0
- package/dist/{chunk-Z2CZC6TC.js → chunk-MXJJN4IA.js} +3 -3
- package/dist/chunk-MXJJN4IA.js.map +1 -0
- package/dist/{chunk-YGVWY6KO.cjs → chunk-NBDPIRQS.cjs} +22 -5
- package/dist/chunk-NBDPIRQS.cjs.map +1 -0
- package/dist/{chunk-GN7Q6V5C.cjs → chunk-Q7SL7U43.cjs} +397 -273
- package/dist/chunk-Q7SL7U43.cjs.map +1 -0
- package/dist/chunk-RCQ2HIQD.cjs.map +1 -1
- package/dist/{chunk-O7LMFJMZ.js → chunk-YHG45LMU.js} +13 -9
- package/dist/chunk-YHG45LMU.js.map +1 -0
- package/dist/{chunk-CUEIM4FE.js → chunk-Z4H6DBVF.js} +241 -117
- package/dist/chunk-Z4H6DBVF.js.map +1 -0
- package/dist/index.cjs +108 -104
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +12 -12
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +23 -23
- package/dist/middleware.js +2 -2
- package/dist/plugins.cjs +7 -7
- package/dist/plugins.js +1 -1
- package/dist/routes.cjs +25 -25
- package/dist/routes.js +5 -5
- package/dist/services.cjs +18 -14
- package/dist/services.js +1 -1
- package/dist/templates.cjs +18 -18
- package/dist/templates.js +2 -2
- package/dist/utils.cjs +11 -11
- package/dist/utils.js +1 -1
- package/migrations/019_remove_blog_posts_collection.sql +15 -0
- package/package.json +1 -1
- package/dist/chunk-3R7EQNGO.cjs.map +0 -1
- package/dist/chunk-4MBTSUI6.js.map +0 -1
- package/dist/chunk-7XEESVSX.cjs.map +0 -1
- package/dist/chunk-CHMO2DOC.cjs.map +0 -1
- package/dist/chunk-CUEIM4FE.js.map +0 -1
- package/dist/chunk-EAELJXRV.js.map +0 -1
- package/dist/chunk-GN7Q6V5C.cjs.map +0 -1
- package/dist/chunk-HJZOA2O5.cjs.map +0 -1
- package/dist/chunk-O7LMFJMZ.js.map +0 -1
- package/dist/chunk-YGVWY6KO.cjs.map +0 -1
- package/dist/chunk-YHLLVUJC.js.map +0 -1
- package/dist/chunk-Z2CZC6TC.js.map +0 -1
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { getCacheService, CACHE_CONFIGS, getLogger, SettingsService } from './chunk-6FR25MPC.js';
|
|
2
|
-
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-
|
|
3
|
-
import { PluginService, MigrationService } from './chunk-
|
|
4
|
-
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderFAQList, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-
|
|
5
|
-
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-
|
|
2
|
+
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-YHG45LMU.js';
|
|
3
|
+
import { PluginService, MigrationService } from './chunk-COBUPOMD.js';
|
|
4
|
+
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderFAQList, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-LW33AOBF.js';
|
|
5
|
+
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-MXJJN4IA.js';
|
|
6
6
|
import { metricsTracker } from './chunk-FICTAGD4.js';
|
|
7
7
|
import { Hono } from 'hono';
|
|
8
8
|
import { cors } from 'hono/cors';
|
|
@@ -1084,10 +1084,10 @@ apiMediaRoutes.patch("/:id", async (c) => {
|
|
|
1084
1084
|
const allowedFields = ["alt", "caption", "tags", "folder"];
|
|
1085
1085
|
const updates = [];
|
|
1086
1086
|
const values = [];
|
|
1087
|
-
for (const [key,
|
|
1087
|
+
for (const [key, value2] of Object.entries(body)) {
|
|
1088
1088
|
if (allowedFields.includes(key)) {
|
|
1089
1089
|
updates.push(`${key} = ?`);
|
|
1090
|
-
values.push(key === "tags" ? JSON.stringify(
|
|
1090
|
+
values.push(key === "tags" ? JSON.stringify(value2) : value2);
|
|
1091
1091
|
}
|
|
1092
1092
|
}
|
|
1093
1093
|
if (updates.length === 0) {
|
|
@@ -1568,7 +1568,7 @@ adminApiRoutes.post("/collections", async (c) => {
|
|
|
1568
1568
|
}
|
|
1569
1569
|
const validatedData = validation.data;
|
|
1570
1570
|
const db = c.env.DB;
|
|
1571
|
-
const
|
|
1571
|
+
const ____user = c.get("user");
|
|
1572
1572
|
const displayName = validatedData.displayName || validatedData.display_name || "";
|
|
1573
1573
|
const existingStmt = db.prepare("SELECT id FROM collections WHERE name = ?");
|
|
1574
1574
|
const existing = await existingStmt.bind(validatedData.name).first();
|
|
@@ -2320,7 +2320,6 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2320
2320
|
`);
|
|
2321
2321
|
}
|
|
2322
2322
|
const validatedData = validation.data;
|
|
2323
|
-
const email = validatedData.email;
|
|
2324
2323
|
const password = validatedData.password;
|
|
2325
2324
|
const username = validatedData.username || authValidationService.generateDefaultValue("username", validatedData);
|
|
2326
2325
|
const firstName = validatedData.firstName || authValidationService.generateDefaultValue("firstName", validatedData);
|
|
@@ -2991,7 +2990,7 @@ init_admin_layout_catalyst_template();
|
|
|
2991
2990
|
|
|
2992
2991
|
// src/templates/components/dynamic-field.template.ts
|
|
2993
2992
|
function renderDynamicField(field, options = {}) {
|
|
2994
|
-
const { value = "", errors = [], disabled = false, className = "" } = options;
|
|
2993
|
+
const { value: value2 = "", errors = [], disabled = false, className = "" } = options;
|
|
2995
2994
|
const opts = field.field_options || {};
|
|
2996
2995
|
const required = field.is_required ? "required" : "";
|
|
2997
2996
|
const baseClasses = `w-full rounded-lg px-3 py-2 text-sm text-zinc-950 dark:text-white bg-white dark:bg-zinc-800 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 ${className}`;
|
|
@@ -3048,7 +3047,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3048
3047
|
type="text"
|
|
3049
3048
|
id="${fieldId}"
|
|
3050
3049
|
name="${fieldName}"
|
|
3051
|
-
value="${escapeHtml2(
|
|
3050
|
+
value="${escapeHtml2(value2)}"
|
|
3052
3051
|
placeholder="${opts.placeholder || ""}"
|
|
3053
3052
|
maxlength="${opts.maxLength || ""}"
|
|
3054
3053
|
${opts.pattern ? `data-pattern="${opts.pattern}"` : ""}
|
|
@@ -3093,7 +3092,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3093
3092
|
class="${baseClasses} ${errorClasses} min-h-[${opts.height || 300}px]"
|
|
3094
3093
|
${required}
|
|
3095
3094
|
${disabled ? "disabled" : ""}
|
|
3096
|
-
>${escapeHtml2(
|
|
3095
|
+
>${escapeHtml2(value2)}</textarea>
|
|
3097
3096
|
<script>
|
|
3098
3097
|
// Initialize TinyMCE for this field
|
|
3099
3098
|
if (typeof tinymce !== 'undefined') {
|
|
@@ -3127,7 +3126,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3127
3126
|
type="number"
|
|
3128
3127
|
id="${fieldId}"
|
|
3129
3128
|
name="${fieldName}"
|
|
3130
|
-
value="${
|
|
3129
|
+
value="${value2}"
|
|
3131
3130
|
min="${opts.min || ""}"
|
|
3132
3131
|
max="${opts.max || ""}"
|
|
3133
3132
|
step="${opts.step || ""}"
|
|
@@ -3139,7 +3138,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3139
3138
|
`;
|
|
3140
3139
|
break;
|
|
3141
3140
|
case "boolean":
|
|
3142
|
-
const checked =
|
|
3141
|
+
const checked = value2 === true || value2 === "true" || value2 === "1" ? "checked" : "";
|
|
3143
3142
|
fieldHTML = `
|
|
3144
3143
|
<div class="flex items-center space-x-3">
|
|
3145
3144
|
<input
|
|
@@ -3164,7 +3163,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3164
3163
|
type="date"
|
|
3165
3164
|
id="${fieldId}"
|
|
3166
3165
|
name="${fieldName}"
|
|
3167
|
-
value="${
|
|
3166
|
+
value="${value2}"
|
|
3168
3167
|
min="${opts.min || ""}"
|
|
3169
3168
|
max="${opts.max || ""}"
|
|
3170
3169
|
class="${baseClasses} ${errorClasses}"
|
|
@@ -3176,7 +3175,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3176
3175
|
case "select":
|
|
3177
3176
|
const options2 = opts.options || [];
|
|
3178
3177
|
const multiple = opts.multiple ? "multiple" : "";
|
|
3179
|
-
const selectedValues = Array.isArray(
|
|
3178
|
+
const selectedValues = Array.isArray(value2) ? value2 : [value2];
|
|
3180
3179
|
fieldHTML = `
|
|
3181
3180
|
<select
|
|
3182
3181
|
id="${fieldId}"
|
|
@@ -3209,9 +3208,9 @@ function renderDynamicField(field, options = {}) {
|
|
|
3209
3208
|
case "media":
|
|
3210
3209
|
fieldHTML = `
|
|
3211
3210
|
<div class="media-field-container">
|
|
3212
|
-
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${
|
|
3213
|
-
<div class="media-preview ${
|
|
3214
|
-
${
|
|
3211
|
+
<input type="hidden" id="${fieldId}" name="${fieldName}" value="${value2}">
|
|
3212
|
+
<div class="media-preview ${value2 ? "" : "hidden"}" id="${fieldId}-preview">
|
|
3213
|
+
${value2 ? `<img src="${value2}" alt="Selected media" class="w-32 h-32 object-cover rounded-lg border border-white/20">` : ""}
|
|
3215
3214
|
</div>
|
|
3216
3215
|
<div class="media-actions mt-2 space-x-2">
|
|
3217
3216
|
<button
|
|
@@ -3225,7 +3224,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3225
3224
|
</svg>
|
|
3226
3225
|
Select Media
|
|
3227
3226
|
</button>
|
|
3228
|
-
${
|
|
3227
|
+
${value2 ? `
|
|
3229
3228
|
<button
|
|
3230
3229
|
type="button"
|
|
3231
3230
|
onclick="clearMediaField('${fieldId}')"
|
|
@@ -3246,7 +3245,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3246
3245
|
type="text"
|
|
3247
3246
|
id="${fieldId}"
|
|
3248
3247
|
name="${fieldName}"
|
|
3249
|
-
value="${escapeHtml2(
|
|
3248
|
+
value="${escapeHtml2(value2)}"
|
|
3250
3249
|
class="${baseClasses} bg-zinc-100 dark:bg-zinc-800/50 cursor-not-allowed"
|
|
3251
3250
|
readonly
|
|
3252
3251
|
disabled
|
|
@@ -3256,7 +3255,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3256
3255
|
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z"/>
|
|
3257
3256
|
</svg>
|
|
3258
3257
|
<div class="text-xs text-zinc-600 dark:text-zinc-400">
|
|
3259
|
-
${
|
|
3258
|
+
${value2 ? "This unique identifier was automatically generated and cannot be changed." : "A unique identifier (UUID) will be automatically generated when you save this content."}
|
|
3260
3259
|
</div>
|
|
3261
3260
|
</div>
|
|
3262
3261
|
</div>
|
|
@@ -3268,7 +3267,7 @@ function renderDynamicField(field, options = {}) {
|
|
|
3268
3267
|
type="text"
|
|
3269
3268
|
id="${fieldId}"
|
|
3270
3269
|
name="${fieldName}"
|
|
3271
|
-
value="${escapeHtml2(
|
|
3270
|
+
value="${escapeHtml2(value2)}"
|
|
3272
3271
|
class="${baseClasses} ${errorClasses}"
|
|
3273
3272
|
${required}
|
|
3274
3273
|
${disabled ? "disabled" : ""}
|
|
@@ -3956,9 +3955,9 @@ function renderContentListPage(data) {
|
|
|
3956
3955
|
name: "model",
|
|
3957
3956
|
label: "Model",
|
|
3958
3957
|
options: [
|
|
3959
|
-
{
|
|
3958
|
+
{ __value: "all", label: "All Models", selected: data.modelName === "all" },
|
|
3960
3959
|
...data.models.map((model) => ({
|
|
3961
|
-
|
|
3960
|
+
__value: model.name,
|
|
3962
3961
|
label: model.displayName,
|
|
3963
3962
|
selected: data.modelName === model.name
|
|
3964
3963
|
}))
|
|
@@ -3968,13 +3967,13 @@ function renderContentListPage(data) {
|
|
|
3968
3967
|
name: "status",
|
|
3969
3968
|
label: "Status",
|
|
3970
3969
|
options: [
|
|
3971
|
-
{
|
|
3972
|
-
{
|
|
3973
|
-
{
|
|
3974
|
-
{
|
|
3975
|
-
{
|
|
3976
|
-
{
|
|
3977
|
-
{
|
|
3970
|
+
{ __value: "all", label: "All Status", selected: data.status === "all" },
|
|
3971
|
+
{ __value: "draft", label: "Draft", selected: data.status === "draft" },
|
|
3972
|
+
{ __value: "review", label: "Under Review", selected: data.status === "review" },
|
|
3973
|
+
{ __value: "scheduled", label: "Scheduled", selected: data.status === "scheduled" },
|
|
3974
|
+
{ __value: "published", label: "Published", selected: data.status === "published" },
|
|
3975
|
+
{ __value: "archived", label: "Archived", selected: data.status === "archived" },
|
|
3976
|
+
{ __value: "deleted", label: "Deleted", selected: data.status === "deleted" }
|
|
3978
3977
|
]
|
|
3979
3978
|
}
|
|
3980
3979
|
],
|
|
@@ -3986,9 +3985,9 @@ function renderContentListPage(data) {
|
|
|
3986
3985
|
}
|
|
3987
3986
|
],
|
|
3988
3987
|
bulkActions: [
|
|
3989
|
-
{ label: "Publish",
|
|
3990
|
-
{ label: "Unpublish",
|
|
3991
|
-
{ label: "Delete",
|
|
3988
|
+
{ label: "Publish", ___value: "publish", icon: "check-circle" },
|
|
3989
|
+
{ label: "Unpublish", ___value: "unpublish", icon: "x-circle" },
|
|
3990
|
+
{ label: "Delete", ___value: "delete", icon: "trash", className: "text-pink-600" }
|
|
3992
3991
|
]
|
|
3993
3992
|
};
|
|
3994
3993
|
const tableColumns = [
|
|
@@ -3997,7 +3996,7 @@ function renderContentListPage(data) {
|
|
|
3997
3996
|
label: "Title",
|
|
3998
3997
|
sortable: true,
|
|
3999
3998
|
sortType: "string",
|
|
4000
|
-
render: (
|
|
3999
|
+
render: (value2, row) => `
|
|
4001
4000
|
<div class="flex items-center">
|
|
4002
4001
|
<div>
|
|
4003
4002
|
<div class="text-sm font-medium text-zinc-950 dark:text-white">
|
|
@@ -4020,7 +4019,7 @@ function renderContentListPage(data) {
|
|
|
4020
4019
|
label: "Status",
|
|
4021
4020
|
sortable: true,
|
|
4022
4021
|
sortType: "string",
|
|
4023
|
-
render: (
|
|
4022
|
+
render: (value2) => value2
|
|
4024
4023
|
},
|
|
4025
4024
|
{
|
|
4026
4025
|
key: "authorName",
|
|
@@ -4041,7 +4040,7 @@ function renderContentListPage(data) {
|
|
|
4041
4040
|
label: "Actions",
|
|
4042
4041
|
sortable: false,
|
|
4043
4042
|
className: "text-sm font-medium",
|
|
4044
|
-
render: (
|
|
4043
|
+
render: (value2, row) => `
|
|
4045
4044
|
<div class="flex space-x-2">
|
|
4046
4045
|
<button
|
|
4047
4046
|
class="inline-flex items-center justify-center p-1.5 rounded-lg bg-cyan-50 dark:bg-cyan-500/10 text-cyan-700 dark:text-cyan-400 ring-1 ring-inset ring-cyan-600/20 dark:ring-cyan-500/20 hover:bg-cyan-100 dark:hover:bg-cyan-500/20 transition-colors"
|
|
@@ -4761,6 +4760,28 @@ async function getCollectionFields(db, collectionId) {
|
|
|
4761
4760
|
return cache.getOrSet(
|
|
4762
4761
|
cache.generateKey("fields", collectionId),
|
|
4763
4762
|
async () => {
|
|
4763
|
+
const collectionStmt = db.prepare("SELECT schema FROM collections WHERE id = ?");
|
|
4764
|
+
const collectionRow = await collectionStmt.bind(collectionId).first();
|
|
4765
|
+
if (collectionRow && collectionRow.schema) {
|
|
4766
|
+
try {
|
|
4767
|
+
const schema = typeof collectionRow.schema === "string" ? JSON.parse(collectionRow.schema) : collectionRow.schema;
|
|
4768
|
+
if (schema && schema.properties) {
|
|
4769
|
+
let fieldOrder = 0;
|
|
4770
|
+
return Object.entries(schema.properties).map(([fieldName, fieldConfig]) => ({
|
|
4771
|
+
id: `schema-${fieldName}`,
|
|
4772
|
+
field_name: fieldName,
|
|
4773
|
+
field_type: fieldConfig.type || "string",
|
|
4774
|
+
field_label: fieldConfig.title || fieldName,
|
|
4775
|
+
field_options: fieldConfig,
|
|
4776
|
+
field_order: fieldOrder++,
|
|
4777
|
+
is_required: fieldConfig.required === true || schema.required && schema.required.includes(fieldName),
|
|
4778
|
+
is_searchable: false
|
|
4779
|
+
}));
|
|
4780
|
+
}
|
|
4781
|
+
} catch (e) {
|
|
4782
|
+
console.error("Error parsing collection schema:", e);
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4764
4785
|
const stmt = db.prepare(`
|
|
4765
4786
|
SELECT * FROM content_fields
|
|
4766
4787
|
WHERE collection_id = ?
|
|
@@ -5140,7 +5161,7 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5140
5161
|
const data = {};
|
|
5141
5162
|
const errors = {};
|
|
5142
5163
|
for (const field of fields) {
|
|
5143
|
-
const
|
|
5164
|
+
const value2 = formData.get(field.field_name);
|
|
5144
5165
|
if (field.field_type === "guid") {
|
|
5145
5166
|
const options = field.field_options || {};
|
|
5146
5167
|
if (options.autoGenerate) {
|
|
@@ -5148,33 +5169,33 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5148
5169
|
continue;
|
|
5149
5170
|
}
|
|
5150
5171
|
}
|
|
5151
|
-
if (field.is_required && (!
|
|
5172
|
+
if (field.is_required && (!value2 || value2.toString().trim() === "")) {
|
|
5152
5173
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
5153
5174
|
continue;
|
|
5154
5175
|
}
|
|
5155
5176
|
switch (field.field_type) {
|
|
5156
5177
|
case "number":
|
|
5157
|
-
if (
|
|
5178
|
+
if (value2 && isNaN(Number(value2))) {
|
|
5158
5179
|
errors[field.field_name] = [`${field.field_label} must be a valid number`];
|
|
5159
5180
|
} else {
|
|
5160
|
-
data[field.field_name] =
|
|
5181
|
+
data[field.field_name] = value2 ? Number(value2) : null;
|
|
5161
5182
|
}
|
|
5162
5183
|
break;
|
|
5163
5184
|
case "boolean":
|
|
5164
|
-
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ?
|
|
5185
|
+
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ? value2 === "true" : false;
|
|
5165
5186
|
break;
|
|
5166
5187
|
case "select":
|
|
5167
5188
|
if (field.field_options?.multiple) {
|
|
5168
5189
|
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
5169
5190
|
} else {
|
|
5170
|
-
data[field.field_name] =
|
|
5191
|
+
data[field.field_name] = value2;
|
|
5171
5192
|
}
|
|
5172
5193
|
break;
|
|
5173
5194
|
case "guid":
|
|
5174
|
-
data[field.field_name] =
|
|
5195
|
+
data[field.field_name] = value2 || null;
|
|
5175
5196
|
break;
|
|
5176
5197
|
default:
|
|
5177
|
-
data[field.field_name] =
|
|
5198
|
+
data[field.field_name] = value2;
|
|
5178
5199
|
}
|
|
5179
5200
|
}
|
|
5180
5201
|
if (Object.keys(errors).length > 0) {
|
|
@@ -5208,9 +5229,9 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5208
5229
|
INSERT INTO content (
|
|
5209
5230
|
id, collection_id, slug, title, data, status,
|
|
5210
5231
|
scheduled_publish_at, scheduled_unpublish_at,
|
|
5211
|
-
meta_title, meta_description, author_id,
|
|
5232
|
+
meta_title, meta_description, author_id, created_at, updated_at
|
|
5212
5233
|
)
|
|
5213
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
5234
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5214
5235
|
`);
|
|
5215
5236
|
await insertStmt.bind(
|
|
5216
5237
|
contentId,
|
|
@@ -5224,7 +5245,6 @@ adminContentRoutes.post("/", async (c) => {
|
|
|
5224
5245
|
data.meta_title || null,
|
|
5225
5246
|
data.meta_description || null,
|
|
5226
5247
|
user?.userId || "unknown",
|
|
5227
|
-
user?.userId || "unknown",
|
|
5228
5248
|
now,
|
|
5229
5249
|
now
|
|
5230
5250
|
).run();
|
|
@@ -5302,31 +5322,31 @@ adminContentRoutes.put("/:id", async (c) => {
|
|
|
5302
5322
|
const data = {};
|
|
5303
5323
|
const errors = {};
|
|
5304
5324
|
for (const field of fields) {
|
|
5305
|
-
const
|
|
5306
|
-
if (field.is_required && (!
|
|
5325
|
+
const value2 = formData.get(field.field_name);
|
|
5326
|
+
if (field.is_required && (!value2 || value2.toString().trim() === "")) {
|
|
5307
5327
|
errors[field.field_name] = [`${field.field_label} is required`];
|
|
5308
5328
|
continue;
|
|
5309
5329
|
}
|
|
5310
5330
|
switch (field.field_type) {
|
|
5311
5331
|
case "number":
|
|
5312
|
-
if (
|
|
5332
|
+
if (value2 && isNaN(Number(value2))) {
|
|
5313
5333
|
errors[field.field_name] = [`${field.field_label} must be a valid number`];
|
|
5314
5334
|
} else {
|
|
5315
|
-
data[field.field_name] =
|
|
5335
|
+
data[field.field_name] = value2 ? Number(value2) : null;
|
|
5316
5336
|
}
|
|
5317
5337
|
break;
|
|
5318
5338
|
case "boolean":
|
|
5319
|
-
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ?
|
|
5339
|
+
data[field.field_name] = formData.get(`${field.field_name}_submitted`) ? value2 === "true" : false;
|
|
5320
5340
|
break;
|
|
5321
5341
|
case "select":
|
|
5322
5342
|
if (field.field_options?.multiple) {
|
|
5323
5343
|
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
5324
5344
|
} else {
|
|
5325
|
-
data[field.field_name] =
|
|
5345
|
+
data[field.field_name] = value2;
|
|
5326
5346
|
}
|
|
5327
5347
|
break;
|
|
5328
5348
|
default:
|
|
5329
|
-
data[field.field_name] =
|
|
5349
|
+
data[field.field_name] = value2;
|
|
5330
5350
|
}
|
|
5331
5351
|
}
|
|
5332
5352
|
if (Object.keys(errors).length > 0) {
|
|
@@ -5443,23 +5463,23 @@ adminContentRoutes.post("/preview", async (c) => {
|
|
|
5443
5463
|
const fields = await getCollectionFields(db, collectionId);
|
|
5444
5464
|
const data = {};
|
|
5445
5465
|
for (const field of fields) {
|
|
5446
|
-
const
|
|
5466
|
+
const value2 = formData.get(field.field_name);
|
|
5447
5467
|
switch (field.field_type) {
|
|
5448
5468
|
case "number":
|
|
5449
|
-
data[field.field_name] =
|
|
5469
|
+
data[field.field_name] = value2 ? Number(value2) : null;
|
|
5450
5470
|
break;
|
|
5451
5471
|
case "boolean":
|
|
5452
|
-
data[field.field_name] =
|
|
5472
|
+
data[field.field_name] = value2 === "true";
|
|
5453
5473
|
break;
|
|
5454
5474
|
case "select":
|
|
5455
5475
|
if (field.field_options?.multiple) {
|
|
5456
5476
|
data[field.field_name] = formData.getAll(`${field.field_name}[]`);
|
|
5457
5477
|
} else {
|
|
5458
|
-
data[field.field_name] =
|
|
5478
|
+
data[field.field_name] = value2;
|
|
5459
5479
|
}
|
|
5460
5480
|
break;
|
|
5461
5481
|
default:
|
|
5462
|
-
data[field.field_name] =
|
|
5482
|
+
data[field.field_name] = value2;
|
|
5463
5483
|
}
|
|
5464
5484
|
}
|
|
5465
5485
|
const previewHTML = `
|
|
@@ -5527,9 +5547,9 @@ adminContentRoutes.post("/duplicate", async (c) => {
|
|
|
5527
5547
|
const insertStmt = db.prepare(`
|
|
5528
5548
|
INSERT INTO content (
|
|
5529
5549
|
id, collection_id, slug, title, data, status,
|
|
5530
|
-
author_id,
|
|
5550
|
+
author_id, created_at, updated_at
|
|
5531
5551
|
)
|
|
5532
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?,
|
|
5552
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
5533
5553
|
`);
|
|
5534
5554
|
await insertStmt.bind(
|
|
5535
5555
|
newId,
|
|
@@ -5540,7 +5560,6 @@ adminContentRoutes.post("/duplicate", async (c) => {
|
|
|
5540
5560
|
"draft",
|
|
5541
5561
|
// Always start as draft
|
|
5542
5562
|
user?.userId || "unknown",
|
|
5543
|
-
user?.userId || "unknown",
|
|
5544
5563
|
now,
|
|
5545
5564
|
now
|
|
5546
5565
|
).run();
|
|
@@ -7316,10 +7335,10 @@ function renderUsersListPage(data) {
|
|
|
7316
7335
|
label: "",
|
|
7317
7336
|
className: "w-12",
|
|
7318
7337
|
sortable: false,
|
|
7319
|
-
render: (
|
|
7338
|
+
render: (value2, row) => {
|
|
7320
7339
|
const initials = `${row.firstName.charAt(0)}${row.lastName.charAt(0)}`.toUpperCase();
|
|
7321
|
-
if (
|
|
7322
|
-
return `<img src="${
|
|
7340
|
+
if (value2) {
|
|
7341
|
+
return `<img src="${value2}" alt="${row.firstName} ${row.lastName}" class="w-8 h-8 rounded-full">`;
|
|
7323
7342
|
}
|
|
7324
7343
|
return `
|
|
7325
7344
|
<div class="w-8 h-8 bg-gradient-to-br from-cyan-400 to-blue-500 dark:from-cyan-300 dark:to-blue-400 rounded-full flex items-center justify-center">
|
|
@@ -7333,7 +7352,7 @@ function renderUsersListPage(data) {
|
|
|
7333
7352
|
label: "Name",
|
|
7334
7353
|
sortable: true,
|
|
7335
7354
|
sortType: "string",
|
|
7336
|
-
render: (
|
|
7355
|
+
render: (_value, row) => {
|
|
7337
7356
|
const escapeHtml7 = (text) => text.replace(/[&<>"']/g, (char) => ({
|
|
7338
7357
|
"&": "&",
|
|
7339
7358
|
"<": "<",
|
|
@@ -7360,7 +7379,7 @@ function renderUsersListPage(data) {
|
|
|
7360
7379
|
label: "Email",
|
|
7361
7380
|
sortable: true,
|
|
7362
7381
|
sortType: "string",
|
|
7363
|
-
render: (
|
|
7382
|
+
render: (value2) => {
|
|
7364
7383
|
const escapeHtml7 = (text) => text.replace(/[&<>"']/g, (char) => ({
|
|
7365
7384
|
"&": "&",
|
|
7366
7385
|
"<": "<",
|
|
@@ -7368,7 +7387,7 @@ function renderUsersListPage(data) {
|
|
|
7368
7387
|
'"': """,
|
|
7369
7388
|
"'": "'"
|
|
7370
7389
|
})[char] || char);
|
|
7371
|
-
const escapedEmail = escapeHtml7(
|
|
7390
|
+
const escapedEmail = escapeHtml7(value2);
|
|
7372
7391
|
return `<a href="mailto:${escapedEmail}" class="text-cyan-600 dark:text-cyan-400 hover:text-cyan-700 dark:hover:text-cyan-300 transition-colors">${escapedEmail}</a>`;
|
|
7373
7392
|
}
|
|
7374
7393
|
},
|
|
@@ -7377,7 +7396,7 @@ function renderUsersListPage(data) {
|
|
|
7377
7396
|
label: "Role",
|
|
7378
7397
|
sortable: true,
|
|
7379
7398
|
sortType: "string",
|
|
7380
|
-
render: (
|
|
7399
|
+
render: (_value) => {
|
|
7381
7400
|
const roleColors = {
|
|
7382
7401
|
admin: "bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-400 ring-1 ring-inset ring-red-700/10 dark:ring-red-500/20",
|
|
7383
7402
|
editor: "bg-blue-50 dark:bg-blue-500/10 text-blue-700 dark:text-blue-400 ring-1 ring-inset ring-blue-700/10 dark:ring-blue-500/20",
|
|
@@ -7393,7 +7412,7 @@ function renderUsersListPage(data) {
|
|
|
7393
7412
|
label: "Last Login",
|
|
7394
7413
|
sortable: true,
|
|
7395
7414
|
sortType: "date",
|
|
7396
|
-
render: (
|
|
7415
|
+
render: (_value) => {
|
|
7397
7416
|
if (!value) return '<span class="text-zinc-500 dark:text-zinc-400">Never</span>';
|
|
7398
7417
|
return `<span class="text-sm text-zinc-500 dark:text-zinc-400">${new Date(value).toLocaleDateString()}</span>`;
|
|
7399
7418
|
}
|
|
@@ -7403,14 +7422,14 @@ function renderUsersListPage(data) {
|
|
|
7403
7422
|
label: "Created",
|
|
7404
7423
|
sortable: true,
|
|
7405
7424
|
sortType: "date",
|
|
7406
|
-
render: (
|
|
7425
|
+
render: (_value) => `<span class="text-sm text-zinc-500 dark:text-zinc-400">${new Date(value).toLocaleDateString()}</span>`
|
|
7407
7426
|
},
|
|
7408
7427
|
{
|
|
7409
7428
|
key: "actions",
|
|
7410
7429
|
label: "Actions",
|
|
7411
7430
|
className: "text-right",
|
|
7412
7431
|
sortable: false,
|
|
7413
|
-
render: (
|
|
7432
|
+
render: (_value, row) => `
|
|
7414
7433
|
<div class="flex justify-end space-x-2">
|
|
7415
7434
|
${row.isActive ? `<button onclick="toggleUserStatus('${row.id}', false)" title="Deactivate user" class="inline-flex items-center justify-center p-2 text-sm font-medium rounded-lg bg-gradient-to-r from-red-500 to-pink-500 dark:from-red-400 dark:to-pink-400 text-white hover:from-red-600 hover:to-pink-600 dark:hover:from-red-500 dark:hover:to-pink-500 shadow-sm transition-all duration-200">
|
|
7416
7435
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
@@ -7901,7 +7920,7 @@ userRoutes.post("/profile/avatar", async (c) => {
|
|
|
7901
7920
|
try {
|
|
7902
7921
|
const formData = await c.req.formData();
|
|
7903
7922
|
const avatarFile = formData.get("avatar");
|
|
7904
|
-
if (!avatarFile || !avatarFile.name) {
|
|
7923
|
+
if (!avatarFile || !(avatarFile instanceof File) || !avatarFile.name) {
|
|
7905
7924
|
return c.html(renderAlert2({
|
|
7906
7925
|
type: "error",
|
|
7907
7926
|
message: "Please select an image file.",
|
|
@@ -8019,7 +8038,7 @@ userRoutes.post("/profile/password", async (c) => {
|
|
|
8019
8038
|
VALUES (?, ?, ?, ?)
|
|
8020
8039
|
`);
|
|
8021
8040
|
await historyStmt.bind(
|
|
8022
|
-
|
|
8041
|
+
crypto.randomUUID(),
|
|
8023
8042
|
user.userId,
|
|
8024
8043
|
userData.password_hash,
|
|
8025
8044
|
Date.now()
|
|
@@ -8251,7 +8270,7 @@ userRoutes.post("/users/new", async (c) => {
|
|
|
8251
8270
|
}));
|
|
8252
8271
|
}
|
|
8253
8272
|
const passwordHash = await AuthManager.hashPassword(password);
|
|
8254
|
-
const userId =
|
|
8273
|
+
const userId = crypto.randomUUID();
|
|
8255
8274
|
const createStmt = db.prepare(`
|
|
8256
8275
|
INSERT INTO users (
|
|
8257
8276
|
id, email, username, first_name, last_name, phone, bio,
|
|
@@ -8568,9 +8587,8 @@ userRoutes.post("/invite-user", async (c) => {
|
|
|
8568
8587
|
if (existingUser) {
|
|
8569
8588
|
return c.json({ error: "A user with this email already exists" }, 400);
|
|
8570
8589
|
}
|
|
8571
|
-
const invitationToken =
|
|
8572
|
-
const
|
|
8573
|
-
const userId = globalThis.crypto.randomUUID();
|
|
8590
|
+
const invitationToken = crypto.randomUUID();
|
|
8591
|
+
const userId = crypto.randomUUID();
|
|
8574
8592
|
const createUserStmt = db.prepare(`
|
|
8575
8593
|
INSERT INTO users (
|
|
8576
8594
|
id, email, first_name, last_name, role,
|
|
@@ -8635,7 +8653,7 @@ userRoutes.post("/resend-invitation/:id", async (c) => {
|
|
|
8635
8653
|
if (!invitedUser) {
|
|
8636
8654
|
return c.json({ error: "User not found or invitation not valid" }, 404);
|
|
8637
8655
|
}
|
|
8638
|
-
const newInvitationToken =
|
|
8656
|
+
const newInvitationToken = crypto.randomUUID();
|
|
8639
8657
|
const updateStmt = db.prepare(`
|
|
8640
8658
|
UPDATE users SET
|
|
8641
8659
|
invitation_token = ?,
|
|
@@ -10223,7 +10241,7 @@ adminMediaRoutes.get("/", async (c) => {
|
|
|
10223
10241
|
const type = searchParams.get("type") || "all";
|
|
10224
10242
|
const view = searchParams.get("view") || "grid";
|
|
10225
10243
|
const page = parseInt(searchParams.get("page") || "1");
|
|
10226
|
-
const
|
|
10244
|
+
const ____cacheBust = searchParams.get("t");
|
|
10227
10245
|
const limit = 24;
|
|
10228
10246
|
const offset = (page - 1) * limit;
|
|
10229
10247
|
const db = c.env.DB;
|
|
@@ -10649,7 +10667,7 @@ adminMediaRoutes.post("/upload", async (c) => {
|
|
|
10649
10667
|
});
|
|
10650
10668
|
}
|
|
10651
10669
|
}
|
|
10652
|
-
let
|
|
10670
|
+
let __mediaGridHTML = "";
|
|
10653
10671
|
if (uploadResults.length > 0) {
|
|
10654
10672
|
try {
|
|
10655
10673
|
const folder = formData.get("folder") || "uploads";
|
|
@@ -11092,6 +11110,28 @@ function renderPluginsListPage(data) {
|
|
|
11092
11110
|
</div>
|
|
11093
11111
|
</div>
|
|
11094
11112
|
|
|
11113
|
+
<!-- Experimental Notice -->
|
|
11114
|
+
<div class="mb-6 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 p-4">
|
|
11115
|
+
<div class="flex items-start">
|
|
11116
|
+
<div class="flex-shrink-0">
|
|
11117
|
+
<svg class="h-5 w-5 text-amber-600 dark:text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
11118
|
+
<path fill-rule="evenodd" d="M8.485 2.495c.673-1.167 2.357-1.167 3.03 0l6.28 10.875c.673 1.167-.17 2.625-1.516 2.625H3.72c-1.347 0-2.189-1.458-1.515-2.625L8.485 2.495zM10 5a.75.75 0 01.75.75v3.5a.75.75 0 01-1.5 0v-3.5A.75.75 0 0110 5zm0 9a1 1 0 100-2 1 1 0 000 2z" clip-rule="evenodd" />
|
|
11119
|
+
</svg>
|
|
11120
|
+
</div>
|
|
11121
|
+
<div class="ml-3 flex-1">
|
|
11122
|
+
<h3 class="text-sm font-semibold text-amber-800 dark:text-amber-200">
|
|
11123
|
+
Experimental Feature
|
|
11124
|
+
</h3>
|
|
11125
|
+
<div class="mt-2 text-sm text-amber-700 dark:text-amber-300">
|
|
11126
|
+
<p>
|
|
11127
|
+
Plugin management is currently under active development. While functional, some features may change or have limitations.
|
|
11128
|
+
Please report any issues you encounter on our <a href="https://discord.gg/8bMy6bv3sZ" target="_blank" class="font-medium underline hover:text-amber-900 dark:hover:text-amber-100">Discord community</a>.
|
|
11129
|
+
</p>
|
|
11130
|
+
</div>
|
|
11131
|
+
</div>
|
|
11132
|
+
</div>
|
|
11133
|
+
</div>
|
|
11134
|
+
|
|
11095
11135
|
<!-- Stats -->
|
|
11096
11136
|
<div class="mb-6">
|
|
11097
11137
|
<h3 class="text-base font-semibold text-zinc-950 dark:text-white">Plugin Statistics</h3>
|
|
@@ -12145,10 +12185,10 @@ function renderSettingsTab(plugin) {
|
|
|
12145
12185
|
`;
|
|
12146
12186
|
}
|
|
12147
12187
|
function renderSettingsFields(settings) {
|
|
12148
|
-
return Object.entries(settings).map(([key,
|
|
12188
|
+
return Object.entries(settings).map(([key, value2]) => {
|
|
12149
12189
|
const fieldId = `setting_${key}`;
|
|
12150
12190
|
const displayName = key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
|
|
12151
|
-
if (typeof
|
|
12191
|
+
if (typeof value2 === "boolean") {
|
|
12152
12192
|
return `
|
|
12153
12193
|
<div class="flex items-center justify-between">
|
|
12154
12194
|
<div>
|
|
@@ -12156,12 +12196,12 @@ function renderSettingsFields(settings) {
|
|
|
12156
12196
|
<p class="text-xs text-gray-400">Enable or disable this feature</p>
|
|
12157
12197
|
</div>
|
|
12158
12198
|
<label class="relative inline-flex items-center cursor-pointer">
|
|
12159
|
-
<input type="checkbox" name="${fieldId}" id="${fieldId}" ${
|
|
12199
|
+
<input type="checkbox" name="${fieldId}" id="${fieldId}" ${value2 ? "checked" : ""} class="sr-only peer">
|
|
12160
12200
|
<div class="w-11 h-6 bg-gray-600 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-800 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-blue-600"></div>
|
|
12161
12201
|
</label>
|
|
12162
12202
|
</div>
|
|
12163
12203
|
`;
|
|
12164
|
-
} else if (typeof
|
|
12204
|
+
} else if (typeof value2 === "number") {
|
|
12165
12205
|
return `
|
|
12166
12206
|
<div>
|
|
12167
12207
|
<label for="${fieldId}" class="block text-sm font-medium text-gray-300 mb-2">${displayName}</label>
|
|
@@ -12169,7 +12209,7 @@ function renderSettingsFields(settings) {
|
|
|
12169
12209
|
type="number"
|
|
12170
12210
|
name="${fieldId}"
|
|
12171
12211
|
id="${fieldId}"
|
|
12172
|
-
value="${
|
|
12212
|
+
value="${value2}"
|
|
12173
12213
|
class="backdrop-blur-sm bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-gray-300 focus:border-blue-400 focus:outline-none transition-colors w-full"
|
|
12174
12214
|
>
|
|
12175
12215
|
</div>
|
|
@@ -12182,7 +12222,7 @@ function renderSettingsFields(settings) {
|
|
|
12182
12222
|
type="text"
|
|
12183
12223
|
name="${fieldId}"
|
|
12184
12224
|
id="${fieldId}"
|
|
12185
|
-
value="${
|
|
12225
|
+
value="${value2}"
|
|
12186
12226
|
class="backdrop-blur-sm bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-gray-300 focus:border-blue-400 focus:outline-none transition-colors w-full"
|
|
12187
12227
|
>
|
|
12188
12228
|
</div>
|
|
@@ -16690,8 +16730,8 @@ function renderTable2(data) {
|
|
|
16690
16730
|
</td>
|
|
16691
16731
|
` : ""}
|
|
16692
16732
|
${data.columns.map((column, colIndex) => {
|
|
16693
|
-
const
|
|
16694
|
-
const displayValue = column.render ? column.render(
|
|
16733
|
+
const value2 = row[column.key];
|
|
16734
|
+
const displayValue = column.render ? column.render(value2, row) : value2;
|
|
16695
16735
|
const stopPropagation = column.key === "actions" ? 'onclick="event.stopPropagation()"' : "";
|
|
16696
16736
|
const isFirst = colIndex === 0 && !data.selectable;
|
|
16697
16737
|
const isLast = colIndex === data.columns.length - 1;
|
|
@@ -16828,7 +16868,7 @@ function renderCollectionsListPage(data) {
|
|
|
16828
16868
|
label: "Name",
|
|
16829
16869
|
sortable: true,
|
|
16830
16870
|
sortType: "string",
|
|
16831
|
-
render: (
|
|
16871
|
+
render: (_value, collection) => `
|
|
16832
16872
|
<div class="flex items-center gap-2 ml-2">
|
|
16833
16873
|
<span class="inline-flex items-center rounded-md bg-cyan-50 dark:bg-cyan-500/10 px-2.5 py-1 text-sm font-medium text-cyan-700 dark:text-cyan-300 ring-1 ring-inset ring-cyan-700/10 dark:ring-cyan-400/20">
|
|
16834
16874
|
${collection.name}
|
|
@@ -16855,14 +16895,14 @@ function renderCollectionsListPage(data) {
|
|
|
16855
16895
|
label: "Description",
|
|
16856
16896
|
sortable: true,
|
|
16857
16897
|
sortType: "string",
|
|
16858
|
-
render: (
|
|
16898
|
+
render: (_value, collection) => collection.description || '<span class="text-zinc-500 dark:text-zinc-400">-</span>'
|
|
16859
16899
|
},
|
|
16860
16900
|
{
|
|
16861
16901
|
key: "field_count",
|
|
16862
16902
|
label: "Fields",
|
|
16863
16903
|
sortable: true,
|
|
16864
16904
|
sortType: "number",
|
|
16865
|
-
render: (
|
|
16905
|
+
render: (_value, collection) => {
|
|
16866
16906
|
const count = collection.field_count || 0;
|
|
16867
16907
|
return `
|
|
16868
16908
|
<div class="flex items-center">
|
|
@@ -16878,7 +16918,7 @@ function renderCollectionsListPage(data) {
|
|
|
16878
16918
|
label: "Source",
|
|
16879
16919
|
sortable: true,
|
|
16880
16920
|
sortType: "string",
|
|
16881
|
-
render: (
|
|
16921
|
+
render: (_value, collection) => {
|
|
16882
16922
|
if (collection.managed) {
|
|
16883
16923
|
return `
|
|
16884
16924
|
<div class="flex items-center gap-1.5">
|
|
@@ -16912,7 +16952,7 @@ function renderCollectionsListPage(data) {
|
|
|
16912
16952
|
key: "actions",
|
|
16913
16953
|
label: "Content",
|
|
16914
16954
|
sortable: false,
|
|
16915
|
-
render: (
|
|
16955
|
+
render: (_value, collection) => {
|
|
16916
16956
|
if (!collection || !collection.id) return '<span class="text-zinc-500 dark:text-zinc-400">-</span>';
|
|
16917
16957
|
return `
|
|
16918
16958
|
<div class="flex items-center space-x-2">
|
|
@@ -17307,7 +17347,55 @@ function renderCollectionFormPage(data) {
|
|
|
17307
17347
|
</style>
|
|
17308
17348
|
|
|
17309
17349
|
${renderForm(formData)}
|
|
17310
|
-
|
|
17350
|
+
|
|
17351
|
+
${isEdit && data.managed ? `
|
|
17352
|
+
<!-- Read-Only Fields Display for Managed Collections -->
|
|
17353
|
+
<div class="mt-8 pt-8 border-t border-zinc-950/5 dark:border-white/10">
|
|
17354
|
+
<div class="mb-6">
|
|
17355
|
+
<h3 class="text-base/7 font-semibold text-zinc-950 dark:text-white">Collection Fields</h3>
|
|
17356
|
+
<p class="text-sm/6 text-zinc-500 dark:text-zinc-400 mt-1">Fields defined in the configuration file (read-only)</p>
|
|
17357
|
+
</div>
|
|
17358
|
+
|
|
17359
|
+
<!-- Fields List (Read-Only) -->
|
|
17360
|
+
<div class="space-y-3">
|
|
17361
|
+
${(data.fields || []).map((field) => `
|
|
17362
|
+
<div class="bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-zinc-950/5 dark:border-white/10 p-4">
|
|
17363
|
+
<div class="flex items-center justify-between">
|
|
17364
|
+
<div class="flex items-center gap-x-4">
|
|
17365
|
+
<div>
|
|
17366
|
+
<div class="flex items-center gap-x-2">
|
|
17367
|
+
<span class="text-sm/6 font-medium text-zinc-950 dark:text-white">${field.field_label}</span>
|
|
17368
|
+
<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium bg-cyan-500/10 dark:bg-cyan-400/10 text-cyan-700 dark:text-cyan-300 ring-1 ring-inset ring-cyan-500/20 dark:ring-cyan-400/20">
|
|
17369
|
+
${field.field_type}
|
|
17370
|
+
</span>
|
|
17371
|
+
${field.is_required ? `
|
|
17372
|
+
<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium bg-rose-500/10 dark:bg-rose-400/10 text-rose-700 dark:text-rose-300 ring-1 ring-inset ring-rose-500/20 dark:ring-rose-400/20">
|
|
17373
|
+
Required
|
|
17374
|
+
</span>
|
|
17375
|
+
` : ""}
|
|
17376
|
+
</div>
|
|
17377
|
+
<div class="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
|
17378
|
+
<code class="px-1.5 py-0.5 rounded bg-zinc-100 dark:bg-zinc-800 font-mono">${field.field_name}</code>
|
|
17379
|
+
</div>
|
|
17380
|
+
</div>
|
|
17381
|
+
</div>
|
|
17382
|
+
</div>
|
|
17383
|
+
</div>
|
|
17384
|
+
`).join("")}
|
|
17385
|
+
|
|
17386
|
+
${(data.fields || []).length === 0 ? `
|
|
17387
|
+
<div class="text-center py-12 text-zinc-500 dark:text-zinc-400">
|
|
17388
|
+
<svg class="mx-auto h-12 w-12 text-zinc-400 dark:text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="1.5">
|
|
17389
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m0 12.75h7.5m-7.5 3H12M10.5 2.25H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z"/>
|
|
17390
|
+
</svg>
|
|
17391
|
+
<p class="mt-4 text-base/7 font-semibold text-zinc-950 dark:text-white">No fields defined</p>
|
|
17392
|
+
<p class="mt-2 text-sm/6">Add fields to your collection configuration file to see them here.</p>
|
|
17393
|
+
</div>
|
|
17394
|
+
` : ""}
|
|
17395
|
+
</div>
|
|
17396
|
+
</div>
|
|
17397
|
+
` : ""}
|
|
17398
|
+
|
|
17311
17399
|
${isEdit && !data.managed ? `
|
|
17312
17400
|
<!-- Fields Management Section -->
|
|
17313
17401
|
<div class="mt-8 pt-8 border-t border-zinc-950/5 dark:border-white/10">
|
|
@@ -17866,7 +17954,7 @@ adminCollectionsRoutes.get("/", async (c) => {
|
|
|
17866
17954
|
let results;
|
|
17867
17955
|
if (search) {
|
|
17868
17956
|
stmt = db.prepare(`
|
|
17869
|
-
SELECT id, name, display_name, description, created_at, managed
|
|
17957
|
+
SELECT id, name, display_name, description, created_at, managed, schema
|
|
17870
17958
|
FROM collections
|
|
17871
17959
|
WHERE is_active = 1
|
|
17872
17960
|
AND (name LIKE ? OR display_name LIKE ? OR description LIKE ?)
|
|
@@ -17876,7 +17964,7 @@ adminCollectionsRoutes.get("/", async (c) => {
|
|
|
17876
17964
|
const queryResults = await stmt.bind(searchParam, searchParam, searchParam).all();
|
|
17877
17965
|
results = queryResults.results;
|
|
17878
17966
|
} else {
|
|
17879
|
-
stmt = db.prepare("SELECT id, name, display_name, description, created_at, managed FROM collections WHERE is_active = 1 ORDER BY created_at DESC");
|
|
17967
|
+
stmt = db.prepare("SELECT id, name, display_name, description, created_at, managed, schema FROM collections WHERE is_active = 1 ORDER BY created_at DESC");
|
|
17880
17968
|
const queryResults = await stmt.all();
|
|
17881
17969
|
results = queryResults.results;
|
|
17882
17970
|
}
|
|
@@ -17884,6 +17972,19 @@ adminCollectionsRoutes.get("/", async (c) => {
|
|
|
17884
17972
|
const { results: fieldCountResults } = await fieldCountStmt.all();
|
|
17885
17973
|
const fieldCounts = new Map((fieldCountResults || []).map((row) => [String(row.collection_id), Number(row.count)]));
|
|
17886
17974
|
const collections = (results || []).filter((row) => row && row.id).map((row) => {
|
|
17975
|
+
let fieldCount = 0;
|
|
17976
|
+
if (row.schema) {
|
|
17977
|
+
try {
|
|
17978
|
+
const schema = typeof row.schema === "string" ? JSON.parse(row.schema) : row.schema;
|
|
17979
|
+
if (schema && schema.properties) {
|
|
17980
|
+
fieldCount = Object.keys(schema.properties).length;
|
|
17981
|
+
}
|
|
17982
|
+
} catch (e) {
|
|
17983
|
+
fieldCount = fieldCounts.get(String(row.id)) || 0;
|
|
17984
|
+
}
|
|
17985
|
+
} else {
|
|
17986
|
+
fieldCount = fieldCounts.get(String(row.id)) || 0;
|
|
17987
|
+
}
|
|
17887
17988
|
return {
|
|
17888
17989
|
id: String(row.id || ""),
|
|
17889
17990
|
name: String(row.name || ""),
|
|
@@ -17891,7 +17992,7 @@ adminCollectionsRoutes.get("/", async (c) => {
|
|
|
17891
17992
|
description: row.description ? String(row.description) : void 0,
|
|
17892
17993
|
created_at: Number(row.created_at || 0),
|
|
17893
17994
|
formattedDate: row.created_at ? new Date(Number(row.created_at)).toLocaleDateString() : "Unknown",
|
|
17894
|
-
field_count:
|
|
17995
|
+
field_count: fieldCount,
|
|
17895
17996
|
managed: row.managed === 1
|
|
17896
17997
|
};
|
|
17897
17998
|
});
|
|
@@ -18065,22 +18166,45 @@ adminCollectionsRoutes.get("/:id", async (c) => {
|
|
|
18065
18166
|
};
|
|
18066
18167
|
return c.html(renderCollectionFormPage(formData2));
|
|
18067
18168
|
}
|
|
18068
|
-
|
|
18069
|
-
|
|
18070
|
-
|
|
18071
|
-
|
|
18072
|
-
|
|
18073
|
-
|
|
18074
|
-
|
|
18075
|
-
|
|
18076
|
-
|
|
18077
|
-
|
|
18078
|
-
|
|
18079
|
-
|
|
18080
|
-
|
|
18081
|
-
|
|
18082
|
-
|
|
18083
|
-
|
|
18169
|
+
let fields = [];
|
|
18170
|
+
if (collection.schema) {
|
|
18171
|
+
try {
|
|
18172
|
+
const schema = typeof collection.schema === "string" ? JSON.parse(collection.schema) : collection.schema;
|
|
18173
|
+
if (schema && schema.properties) {
|
|
18174
|
+
let fieldOrder = 0;
|
|
18175
|
+
fields = Object.entries(schema.properties).map(([fieldName, fieldConfig]) => ({
|
|
18176
|
+
id: `schema-${fieldName}`,
|
|
18177
|
+
field_name: fieldName,
|
|
18178
|
+
field_type: fieldConfig.type || "string",
|
|
18179
|
+
field_label: fieldConfig.title || fieldName,
|
|
18180
|
+
field_options: fieldConfig,
|
|
18181
|
+
field_order: fieldOrder++,
|
|
18182
|
+
is_required: fieldConfig.required === true || schema.required && schema.required.includes(fieldName),
|
|
18183
|
+
is_searchable: false
|
|
18184
|
+
}));
|
|
18185
|
+
}
|
|
18186
|
+
} catch (e) {
|
|
18187
|
+
console.error("Error parsing collection schema:", e);
|
|
18188
|
+
}
|
|
18189
|
+
}
|
|
18190
|
+
if (fields.length === 0) {
|
|
18191
|
+
const fieldsStmt = db.prepare(`
|
|
18192
|
+
SELECT * FROM content_fields
|
|
18193
|
+
WHERE collection_id = ?
|
|
18194
|
+
ORDER BY field_order ASC
|
|
18195
|
+
`);
|
|
18196
|
+
const { results: fieldsResults } = await fieldsStmt.bind(id).all();
|
|
18197
|
+
fields = (fieldsResults || []).map((row) => ({
|
|
18198
|
+
id: row.id,
|
|
18199
|
+
field_name: row.field_name,
|
|
18200
|
+
field_type: row.field_type,
|
|
18201
|
+
field_label: row.field_label,
|
|
18202
|
+
field_options: row.field_options ? JSON.parse(row.field_options) : {},
|
|
18203
|
+
field_order: row.field_order,
|
|
18204
|
+
is_required: row.is_required === 1,
|
|
18205
|
+
is_searchable: row.is_searchable === 1
|
|
18206
|
+
}));
|
|
18207
|
+
}
|
|
18084
18208
|
const formData = {
|
|
18085
18209
|
id: collection.id,
|
|
18086
18210
|
name: collection.name,
|
|
@@ -20170,5 +20294,5 @@ var ROUTES_INFO = {
|
|
|
20170
20294
|
};
|
|
20171
20295
|
|
|
20172
20296
|
export { ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_faq_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, router, userRoutes };
|
|
20173
|
-
//# sourceMappingURL=chunk-
|
|
20174
|
-
//# sourceMappingURL=chunk-
|
|
20297
|
+
//# sourceMappingURL=chunk-Z4H6DBVF.js.map
|
|
20298
|
+
//# sourceMappingURL=chunk-Z4H6DBVF.js.map
|