@sonicjs-cms/core 2.3.13 → 2.3.15
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/{chunk-RP66TPEJ.js → chunk-4M2UOUXV.js} +415 -320
- package/dist/chunk-4M2UOUXV.js.map +1 -0
- package/dist/{chunk-ARLXQU2S.cjs → chunk-5OLN5JO3.cjs} +560 -465
- package/dist/chunk-5OLN5JO3.cjs.map +1 -0
- package/dist/{chunk-W4CE7XME.cjs → chunk-63LV4YVI.cjs} +2 -2
- package/dist/{chunk-W4CE7XME.cjs.map → chunk-63LV4YVI.cjs.map} +1 -1
- package/dist/{chunk-FHCN7KR2.js → chunk-67SKO5RQ.js} +3 -3
- package/dist/{chunk-FHCN7KR2.js.map → chunk-67SKO5RQ.js.map} +1 -1
- package/dist/{chunk-VMEBHBYY.js → chunk-72I2MOSH.js} +2 -2
- package/dist/{chunk-VMEBHBYY.js.map → chunk-72I2MOSH.js.map} +1 -1
- package/dist/{chunk-2NTBZ2Y7.js → chunk-A27RBGBA.js} +3 -3
- package/dist/{chunk-2NTBZ2Y7.js.map → chunk-A27RBGBA.js.map} +1 -1
- package/dist/{chunk-F56JKQTA.js → chunk-AVPUX57O.js} +3 -3
- package/dist/{chunk-F56JKQTA.js.map → chunk-AVPUX57O.js.map} +1 -1
- package/dist/{chunk-MF7DWI5P.cjs → chunk-AZLU3ROK.cjs} +4 -2
- package/dist/chunk-AZLU3ROK.cjs.map +1 -0
- package/dist/{chunk-W2IAEG4W.cjs → chunk-H2X4BFCW.cjs} +3 -3
- package/dist/{chunk-W2IAEG4W.cjs.map → chunk-H2X4BFCW.cjs.map} +1 -1
- package/dist/{chunk-5NCBFP37.cjs → chunk-QG3YQKL4.cjs} +4 -4
- package/dist/{chunk-5NCBFP37.cjs.map → chunk-QG3YQKL4.cjs.map} +1 -1
- package/dist/{chunk-DN45O5XV.js → chunk-V5LBQN3I.js} +4 -2
- package/dist/chunk-V5LBQN3I.js.map +1 -0
- package/dist/{chunk-XR6XACXJ.cjs → chunk-YIXSSJWD.cjs} +5 -5
- package/dist/{chunk-XR6XACXJ.cjs.map → chunk-YIXSSJWD.cjs.map} +1 -1
- package/dist/index.cjs +1111 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1034 -10
- package/dist/index.js.map +1 -1
- package/dist/middleware.cjs +23 -23
- package/dist/middleware.js +2 -2
- package/dist/migrations-H3Q5FZGZ.js +4 -0
- package/dist/{migrations-43GTELB5.js.map → migrations-H3Q5FZGZ.js.map} +1 -1
- package/dist/migrations-VN5VTX3C.cjs +13 -0
- package/dist/{migrations-ZAYXZXON.cjs.map → migrations-VN5VTX3C.cjs.map} +1 -1
- package/dist/{plugin-manifest-BCMx9CAq.d.cts → plugin-manifest-Dpy8wxIB.d.cts} +2 -2
- package/dist/{plugin-manifest-BCMx9CAq.d.ts → plugin-manifest-Dpy8wxIB.d.ts} +2 -2
- package/dist/routes.cjs +25 -25
- package/dist/routes.js +5 -5
- package/dist/services.cjs +2 -2
- package/dist/services.js +1 -1
- 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 +11 -11
- package/dist/utils.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ARLXQU2S.cjs.map +0 -1
- package/dist/chunk-DN45O5XV.js.map +0 -1
- package/dist/chunk-MF7DWI5P.cjs.map +0 -1
- package/dist/chunk-RP66TPEJ.js.map +0 -1
- package/dist/migrations-43GTELB5.js +0 -4
- package/dist/migrations-ZAYXZXON.cjs +0 -13
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { getCacheService, CACHE_CONFIGS, getLogger, SettingsService } from './chunk-3YNNVSMC.js';
|
|
2
|
-
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-
|
|
2
|
+
import { requireAuth, isPluginActive, requireRole, AuthManager, logActivity } from './chunk-A27RBGBA.js';
|
|
3
3
|
import { PluginService } from './chunk-SGAG6FD3.js';
|
|
4
|
-
import { MigrationService } from './chunk-
|
|
5
|
-
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-
|
|
6
|
-
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-
|
|
4
|
+
import { MigrationService } from './chunk-72I2MOSH.js';
|
|
5
|
+
import { init_admin_layout_catalyst_template, renderDesignPage, renderCheckboxPage, renderTestimonialsList, renderCodeExamplesList, renderAlert, renderTable, renderPagination, renderConfirmationDialog, getConfirmationDialogScript, renderAdminLayoutCatalyst, renderAdminLayout, adminLayoutV2, renderForm } from './chunk-V5LBQN3I.js';
|
|
6
|
+
import { QueryFilterBuilder, sanitizeInput, getCoreVersion, escapeHtml } from './chunk-67SKO5RQ.js';
|
|
7
7
|
import { metricsTracker } from './chunk-FICTAGD4.js';
|
|
8
8
|
import { Hono } from 'hono';
|
|
9
9
|
import { cors } from 'hono/cors';
|
|
@@ -1720,7 +1720,7 @@ adminApiRoutes.delete("/collections/:id", async (c) => {
|
|
|
1720
1720
|
});
|
|
1721
1721
|
adminApiRoutes.get("/migrations/status", async (c) => {
|
|
1722
1722
|
try {
|
|
1723
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1723
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-H3Q5FZGZ.js');
|
|
1724
1724
|
const db = c.env.DB;
|
|
1725
1725
|
const migrationService = new MigrationService2(db);
|
|
1726
1726
|
const status = await migrationService.getMigrationStatus();
|
|
@@ -1745,7 +1745,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1745
1745
|
error: "Unauthorized. Admin access required."
|
|
1746
1746
|
}, 403);
|
|
1747
1747
|
}
|
|
1748
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1748
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-H3Q5FZGZ.js');
|
|
1749
1749
|
const db = c.env.DB;
|
|
1750
1750
|
const migrationService = new MigrationService2(db);
|
|
1751
1751
|
const result = await migrationService.runPendingMigrations();
|
|
@@ -1764,7 +1764,7 @@ adminApiRoutes.post("/migrations/run", async (c) => {
|
|
|
1764
1764
|
});
|
|
1765
1765
|
adminApiRoutes.get("/migrations/validate", async (c) => {
|
|
1766
1766
|
try {
|
|
1767
|
-
const { MigrationService: MigrationService2 } = await import('./migrations-
|
|
1767
|
+
const { MigrationService: MigrationService2 } = await import('./migrations-H3Q5FZGZ.js');
|
|
1768
1768
|
const db = c.env.DB;
|
|
1769
1769
|
const migrationService = new MigrationService2(db);
|
|
1770
1770
|
const validation = await migrationService.validateSchema();
|
|
@@ -2117,6 +2117,27 @@ function renderRegisterPage(data) {
|
|
|
2117
2117
|
</html>
|
|
2118
2118
|
`;
|
|
2119
2119
|
}
|
|
2120
|
+
async function isRegistrationEnabled(db) {
|
|
2121
|
+
try {
|
|
2122
|
+
const plugin = await db.prepare("SELECT settings FROM plugins WHERE id = ?").bind("core-auth").first();
|
|
2123
|
+
if (plugin?.settings) {
|
|
2124
|
+
const settings = JSON.parse(plugin.settings);
|
|
2125
|
+
const enabled = settings?.registration?.enabled;
|
|
2126
|
+
return enabled !== false && enabled !== 0;
|
|
2127
|
+
}
|
|
2128
|
+
return true;
|
|
2129
|
+
} catch {
|
|
2130
|
+
return true;
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
async function isFirstUserRegistration(db) {
|
|
2134
|
+
try {
|
|
2135
|
+
const result = await db.prepare("SELECT COUNT(*) as count FROM users").first();
|
|
2136
|
+
return result?.count === 0;
|
|
2137
|
+
} catch {
|
|
2138
|
+
return false;
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2120
2141
|
var baseRegistrationSchema = z.object({
|
|
2121
2142
|
email: z.string().email("Valid email is required"),
|
|
2122
2143
|
password: z.string().min(8, "Password must be at least 8 characters"),
|
|
@@ -2168,7 +2189,15 @@ authRoutes.get("/login", async (c) => {
|
|
|
2168
2189
|
}
|
|
2169
2190
|
return c.html(renderLoginPage(pageData, demoLoginActive));
|
|
2170
2191
|
});
|
|
2171
|
-
authRoutes.get("/register", (c) => {
|
|
2192
|
+
authRoutes.get("/register", async (c) => {
|
|
2193
|
+
const db = c.env.DB;
|
|
2194
|
+
const isFirstUser = await isFirstUserRegistration(db);
|
|
2195
|
+
if (!isFirstUser) {
|
|
2196
|
+
const registrationEnabled = await isRegistrationEnabled(db);
|
|
2197
|
+
if (!registrationEnabled) {
|
|
2198
|
+
return c.redirect("/auth/login?error=Registration is currently disabled");
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2172
2201
|
const error = c.req.query("error");
|
|
2173
2202
|
const pageData = {
|
|
2174
2203
|
error: error || void 0
|
|
@@ -2184,6 +2213,13 @@ authRoutes.post(
|
|
|
2184
2213
|
async (c) => {
|
|
2185
2214
|
try {
|
|
2186
2215
|
const db = c.env.DB;
|
|
2216
|
+
const isFirstUser = await isFirstUserRegistration(db);
|
|
2217
|
+
if (!isFirstUser) {
|
|
2218
|
+
const registrationEnabled = await isRegistrationEnabled(db);
|
|
2219
|
+
if (!registrationEnabled) {
|
|
2220
|
+
return c.json({ error: "Registration is currently disabled" }, 403);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2187
2223
|
let requestData;
|
|
2188
2224
|
try {
|
|
2189
2225
|
requestData = await c.req.json();
|
|
@@ -2376,6 +2412,17 @@ authRoutes.post("/refresh", requireAuth(), async (c) => {
|
|
|
2376
2412
|
authRoutes.post("/register/form", async (c) => {
|
|
2377
2413
|
try {
|
|
2378
2414
|
const db = c.env.DB;
|
|
2415
|
+
const isFirstUser = await isFirstUserRegistration(db);
|
|
2416
|
+
if (!isFirstUser) {
|
|
2417
|
+
const registrationEnabled = await isRegistrationEnabled(db);
|
|
2418
|
+
if (!registrationEnabled) {
|
|
2419
|
+
return c.html(html`
|
|
2420
|
+
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
|
|
2421
|
+
Registration is currently disabled. Please contact an administrator.
|
|
2422
|
+
</div>
|
|
2423
|
+
`);
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2379
2426
|
const formData = await c.req.formData();
|
|
2380
2427
|
const requestData = {
|
|
2381
2428
|
email: formData.get("email"),
|
|
@@ -2409,6 +2456,7 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2409
2456
|
`);
|
|
2410
2457
|
}
|
|
2411
2458
|
const passwordHash = await AuthManager.hashPassword(password);
|
|
2459
|
+
const role = isFirstUser ? "admin" : "viewer";
|
|
2412
2460
|
const userId = crypto.randomUUID();
|
|
2413
2461
|
const now = /* @__PURE__ */ new Date();
|
|
2414
2462
|
await db.prepare(`
|
|
@@ -2421,14 +2469,13 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2421
2469
|
firstName,
|
|
2422
2470
|
lastName,
|
|
2423
2471
|
passwordHash,
|
|
2424
|
-
|
|
2425
|
-
// First user gets admin role
|
|
2472
|
+
role,
|
|
2426
2473
|
1,
|
|
2427
2474
|
// is_active
|
|
2428
2475
|
now.getTime(),
|
|
2429
2476
|
now.getTime()
|
|
2430
2477
|
).run();
|
|
2431
|
-
const token = await AuthManager.generateToken(userId, normalizedEmail,
|
|
2478
|
+
const token = await AuthManager.generateToken(userId, normalizedEmail, role);
|
|
2432
2479
|
setCookie(c, "auth_token", token, {
|
|
2433
2480
|
httpOnly: true,
|
|
2434
2481
|
secure: false,
|
|
@@ -2437,12 +2484,13 @@ authRoutes.post("/register/form", async (c) => {
|
|
|
2437
2484
|
maxAge: 60 * 60 * 24
|
|
2438
2485
|
// 24 hours
|
|
2439
2486
|
});
|
|
2487
|
+
const redirectUrl = role === "admin" ? "/admin/dashboard" : "/admin/dashboard";
|
|
2440
2488
|
return c.html(html`
|
|
2441
2489
|
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded">
|
|
2442
|
-
Account created successfully! Redirecting
|
|
2490
|
+
Account created successfully! Redirecting...
|
|
2443
2491
|
<script>
|
|
2444
2492
|
setTimeout(() => {
|
|
2445
|
-
window.location.href = '
|
|
2493
|
+
window.location.href = '${redirectUrl}';
|
|
2446
2494
|
}, 2000);
|
|
2447
2495
|
</script>
|
|
2448
2496
|
</div>
|
|
@@ -4426,6 +4474,13 @@ function getMDXEditorInitScript(config) {
|
|
|
4426
4474
|
// Store reference to editor instance
|
|
4427
4475
|
textarea.easyMDEInstance = easyMDE;
|
|
4428
4476
|
|
|
4477
|
+
// Sync changes back to textarea
|
|
4478
|
+
easyMDE.codemirror.on("change", () => {
|
|
4479
|
+
textarea.value = easyMDE.value();
|
|
4480
|
+
textarea.dispatchEvent(new Event("input", { bubbles: true }));
|
|
4481
|
+
textarea.dispatchEvent(new Event("change", { bubbles: true }));
|
|
4482
|
+
});
|
|
4483
|
+
|
|
4429
4484
|
console.log('EasyMDE initialized for field:', textarea.id || textarea.name);
|
|
4430
4485
|
} catch (error) {
|
|
4431
4486
|
console.error('Error initializing EasyMDE:', error);
|
|
@@ -12332,10 +12387,37 @@ function formatFileSize(bytes) {
|
|
|
12332
12387
|
// src/templates/pages/admin-plugins-list.template.ts
|
|
12333
12388
|
init_admin_layout_catalyst_template();
|
|
12334
12389
|
function renderPluginsListPage(data) {
|
|
12390
|
+
const categories = [
|
|
12391
|
+
{ value: "content", label: "Content Management" },
|
|
12392
|
+
{ value: "media", label: "Media" },
|
|
12393
|
+
{ value: "editor", label: "Editors" },
|
|
12394
|
+
{ value: "seo", label: "SEO & Analytics" },
|
|
12395
|
+
{ value: "security", label: "Security" },
|
|
12396
|
+
{ value: "utilities", label: "Utilities" },
|
|
12397
|
+
{ value: "system", label: "System" },
|
|
12398
|
+
{ value: "development", label: "Development" },
|
|
12399
|
+
{ value: "demo", label: "Demo" }
|
|
12400
|
+
];
|
|
12401
|
+
const statuses = [
|
|
12402
|
+
{ value: "active", label: "Active" },
|
|
12403
|
+
{ value: "inactive", label: "Inactive" },
|
|
12404
|
+
{ value: "uninstalled", label: "Available to Install" },
|
|
12405
|
+
{ value: "error", label: "Error" }
|
|
12406
|
+
];
|
|
12407
|
+
const categoryCounts = {};
|
|
12408
|
+
categories.forEach((cat) => {
|
|
12409
|
+
categoryCounts[cat.value] = data.plugins.filter((p) => p.category === cat.value).length;
|
|
12410
|
+
});
|
|
12411
|
+
categories.sort((a, b) => (categoryCounts[b.value] || 0) - (categoryCounts[a.value] || 0));
|
|
12412
|
+
const statusCounts = {};
|
|
12413
|
+
statuses.forEach((status) => {
|
|
12414
|
+
statusCounts[status.value] = data.plugins.filter((p) => p.status === status.value).length;
|
|
12415
|
+
});
|
|
12416
|
+
statuses.sort((a, b) => (statusCounts[b.value] || 0) - (statusCounts[a.value] || 0));
|
|
12335
12417
|
const pageContent = `
|
|
12336
12418
|
<div>
|
|
12337
12419
|
<!-- Header -->
|
|
12338
|
-
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-
|
|
12420
|
+
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-8">
|
|
12339
12421
|
<div>
|
|
12340
12422
|
<h1 class="text-2xl/8 font-semibold text-zinc-950 dark:text-white sm:text-xl/8">Plugins</h1>
|
|
12341
12423
|
<p class="mt-2 text-sm/6 text-zinc-500 dark:text-zinc-400">Manage and extend functionality with plugins</p>
|
|
@@ -12343,7 +12425,7 @@ function renderPluginsListPage(data) {
|
|
|
12343
12425
|
</div>
|
|
12344
12426
|
|
|
12345
12427
|
<!-- Experimental Notice -->
|
|
12346
|
-
<div class="mb-
|
|
12428
|
+
<div class="mb-8 rounded-lg bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800/50 p-4">
|
|
12347
12429
|
<div class="flex items-start">
|
|
12348
12430
|
<div class="flex-shrink-0">
|
|
12349
12431
|
<svg class="h-5 w-5 text-amber-600 dark:text-amber-400" viewBox="0 0 20 20" fill="currentColor">
|
|
@@ -12364,176 +12446,174 @@ function renderPluginsListPage(data) {
|
|
|
12364
12446
|
</div>
|
|
12365
12447
|
</div>
|
|
12366
12448
|
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
<
|
|
12370
|
-
|
|
12371
|
-
<div
|
|
12372
|
-
<
|
|
12373
|
-
<
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
<
|
|
12379
|
-
<
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
</svg>
|
|
12396
|
-
<span class="sr-only">Increased by</span>
|
|
12397
|
-
12.3%
|
|
12398
|
-
</div>
|
|
12399
|
-
</dd>
|
|
12449
|
+
<div class="flex flex-col lg:flex-row gap-8">
|
|
12450
|
+
<!-- Sidebar Filters -->
|
|
12451
|
+
<aside class="w-full lg:w-48 flex-shrink-0 space-y-8 lg:sticky lg:top-6 lg:self-start">
|
|
12452
|
+
<!-- Categories Filter -->
|
|
12453
|
+
<div>
|
|
12454
|
+
<h3 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Categories</h3>
|
|
12455
|
+
<div class="space-y-3">
|
|
12456
|
+
${categories.map((cat) => {
|
|
12457
|
+
const count = categoryCounts[cat.value] || 0;
|
|
12458
|
+
const isDisabled = count === 0;
|
|
12459
|
+
return `
|
|
12460
|
+
<div class="flex items-center ${isDisabled ? "opacity-50" : ""}">
|
|
12461
|
+
<input
|
|
12462
|
+
id="category-${cat.value}"
|
|
12463
|
+
name="category"
|
|
12464
|
+
value="${cat.value}"
|
|
12465
|
+
type="checkbox"
|
|
12466
|
+
onchange="filterAndSortPlugins()"
|
|
12467
|
+
class="h-4 w-4 rounded border-zinc-300 dark:border-zinc-700 text-zinc-900 focus:ring-zinc-600 dark:bg-zinc-900 disabled:cursor-not-allowed"
|
|
12468
|
+
${isDisabled ? "disabled" : ""}
|
|
12469
|
+
>
|
|
12470
|
+
<label for="category-${cat.value}" class="ml-3 text-sm text-zinc-600 dark:text-zinc-400 select-none ${isDisabled ? "cursor-not-allowed" : ""}">
|
|
12471
|
+
${cat.label} <span class="text-zinc-400 dark:text-zinc-500">(${count})</span>
|
|
12472
|
+
</label>
|
|
12473
|
+
</div>
|
|
12474
|
+
`;
|
|
12475
|
+
}).join("")}
|
|
12476
|
+
</div>
|
|
12400
12477
|
</div>
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12478
|
+
|
|
12479
|
+
<div class="h-px bg-zinc-200 dark:bg-zinc-800 lg:hidden"></div>
|
|
12480
|
+
|
|
12481
|
+
<!-- Status Filter -->
|
|
12482
|
+
<div>
|
|
12483
|
+
<h3 class="text-sm font-semibold text-zinc-950 dark:text-white mb-4">Status</h3>
|
|
12484
|
+
<div class="space-y-3">
|
|
12485
|
+
${statuses.map((status) => {
|
|
12486
|
+
const count = statusCounts[status.value] || 0;
|
|
12487
|
+
const isDisabled = count === 0;
|
|
12488
|
+
let colorClass = "";
|
|
12489
|
+
let ringClass = "";
|
|
12490
|
+
let dotClass = "";
|
|
12491
|
+
switch (status.value) {
|
|
12492
|
+
case "active":
|
|
12493
|
+
colorClass = "text-emerald-700 dark:text-emerald-400 bg-emerald-50 dark:bg-emerald-500/10";
|
|
12494
|
+
ringClass = "ring-emerald-600/20";
|
|
12495
|
+
dotClass = "bg-emerald-500 dark:bg-emerald-400";
|
|
12496
|
+
break;
|
|
12497
|
+
case "inactive":
|
|
12498
|
+
colorClass = "text-zinc-700 dark:text-zinc-400 bg-zinc-50 dark:bg-zinc-500/10";
|
|
12499
|
+
ringClass = "ring-zinc-600/20";
|
|
12500
|
+
dotClass = "bg-zinc-500 dark:bg-zinc-400";
|
|
12501
|
+
break;
|
|
12502
|
+
case "error":
|
|
12503
|
+
colorClass = "text-red-700 dark:text-red-400 bg-red-50 dark:bg-red-500/10";
|
|
12504
|
+
ringClass = "ring-red-600/20";
|
|
12505
|
+
dotClass = "bg-red-500 dark:bg-red-400";
|
|
12506
|
+
break;
|
|
12507
|
+
case "uninstalled":
|
|
12508
|
+
colorClass = "text-yellow-700 dark:text-yellow-400 bg-yellow-50 dark:bg-yellow-500/10";
|
|
12509
|
+
ringClass = "ring-yellow-600/20";
|
|
12510
|
+
dotClass = "bg-yellow-500 dark:bg-yellow-400";
|
|
12511
|
+
break;
|
|
12512
|
+
default:
|
|
12513
|
+
colorClass = "text-zinc-700 dark:text-zinc-400 bg-zinc-50 dark:bg-zinc-500/10";
|
|
12514
|
+
ringClass = "ring-zinc-600/20";
|
|
12515
|
+
dotClass = "bg-zinc-500 dark:bg-zinc-400";
|
|
12516
|
+
}
|
|
12517
|
+
return `
|
|
12518
|
+
<div class="flex items-center ${isDisabled ? "opacity-50" : ""}">
|
|
12519
|
+
<input
|
|
12520
|
+
id="status-${status.value}"
|
|
12521
|
+
name="status"
|
|
12522
|
+
value="${status.value}"
|
|
12523
|
+
type="checkbox"
|
|
12524
|
+
onchange="filterAndSortPlugins()"
|
|
12525
|
+
class="h-4 w-4 rounded border-zinc-300 dark:border-zinc-700 text-zinc-900 focus:ring-zinc-600 dark:bg-zinc-900 disabled:cursor-not-allowed"
|
|
12526
|
+
${isDisabled ? "disabled" : ""}
|
|
12527
|
+
>
|
|
12528
|
+
<label for="status-${status.value}" class="ml-3 cursor-pointer select-none flex items-center ${isDisabled ? "cursor-not-allowed" : ""}">
|
|
12529
|
+
<span class="inline-flex items-center rounded-md px-2 py-1 text-xs font-medium ring-1 ring-inset ${colorClass} ${ringClass}">
|
|
12530
|
+
<span class="mr-1.5 h-1.5 w-1.5 rounded-full ${dotClass}"></span>
|
|
12531
|
+
${status.label}
|
|
12532
|
+
</span>
|
|
12533
|
+
<span class="ml-2 text-xs text-zinc-500 dark:text-zinc-400">(${count})</span>
|
|
12534
|
+
</label>
|
|
12535
|
+
</div>
|
|
12536
|
+
`;
|
|
12537
|
+
}).join("")}
|
|
12538
|
+
</div>
|
|
12415
12539
|
</div>
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
</div>
|
|
12429
|
-
</
|
|
12540
|
+
</aside>
|
|
12541
|
+
|
|
12542
|
+
<!-- Main Content -->
|
|
12543
|
+
<div class="flex-1 min-w-0">
|
|
12544
|
+
<!-- Stats Row (Compact) -->
|
|
12545
|
+
<div class="flex flex-wrap gap-4 mb-6">
|
|
12546
|
+
<div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
|
|
12547
|
+
<div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Total</div>
|
|
12548
|
+
<div class="mt-1 text-lg font-semibold text-zinc-900 dark:text-white">${data.stats?.total || 0}</div>
|
|
12549
|
+
</div>
|
|
12550
|
+
<div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
|
|
12551
|
+
<div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Active</div>
|
|
12552
|
+
<div class="mt-1 text-lg font-semibold text-emerald-600 dark:text-emerald-400">${data.stats?.active || 0}</div>
|
|
12553
|
+
</div>
|
|
12554
|
+
<div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
|
|
12555
|
+
<div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Available</div>
|
|
12556
|
+
<div class="mt-1 text-lg font-semibold text-zinc-600 dark:text-zinc-400">${data.stats?.uninstalled || 0}</div>
|
|
12557
|
+
</div>
|
|
12558
|
+
<div class="min-w-[140px] rounded-lg bg-zinc-50 dark:bg-zinc-800/50 p-3 ring-1 ring-inset ring-zinc-950/5 dark:ring-white/5">
|
|
12559
|
+
<div class="text-xs font-medium text-zinc-500 dark:text-zinc-400">Errors</div>
|
|
12560
|
+
<div class="mt-1 text-lg font-semibold text-red-600 dark:text-red-400">${data.stats?.errors || 0}</div>
|
|
12561
|
+
</div>
|
|
12430
12562
|
</div>
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
<svg viewBox="0 0 20 20" fill="currentColor" class="-ml-1 mr-0.5 size-5 shrink-0 self-center">
|
|
12439
|
-
<path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
|
|
12563
|
+
|
|
12564
|
+
<!-- Toolbar -->
|
|
12565
|
+
<div class="flex flex-col sm:flex-row gap-4 items-start sm:items-center justify-between mb-6">
|
|
12566
|
+
<div class="relative flex-1 w-full">
|
|
12567
|
+
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
|
12568
|
+
<svg class="h-4 w-4 text-zinc-400" viewBox="0 0 20 20" fill="currentColor">
|
|
12569
|
+
<path fill-rule="evenodd" d="M9 3.5a5.5 5.5 0 100 11 5.5 5.5 0 000-11zM2 9a7 7 0 1112.452 4.391l3.328 3.329a.75.75 0 11-1.06 1.06l-3.329-3.328A7 7 0 012 9z" clip-rule="evenodd" />
|
|
12440
12570
|
</svg>
|
|
12441
|
-
<span class="sr-only">Available</span>
|
|
12442
|
-
Ready
|
|
12443
12571
|
</div>
|
|
12444
|
-
|
|
12445
|
-
|
|
12446
|
-
|
|
12447
|
-
|
|
12572
|
+
<input
|
|
12573
|
+
id="search-input"
|
|
12574
|
+
type="text"
|
|
12575
|
+
placeholder="Search plugins..."
|
|
12576
|
+
oninput="filterAndSortPlugins()"
|
|
12577
|
+
class="block w-full h-9 rounded-md border-0 py-1.5 pl-10 text-zinc-900 ring-1 ring-inset ring-zinc-300 placeholder:text-zinc-400 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-900 dark:text-white dark:ring-zinc-700 dark:focus:ring-zinc-500 sm:text-sm sm:leading-6"
|
|
12578
|
+
>
|
|
12579
|
+
</div>
|
|
12448
12580
|
|
|
12449
|
-
|
|
12450
|
-
|
|
12451
|
-
|
|
12452
|
-
|
|
12581
|
+
<div class="flex items-center gap-3 w-full sm:w-auto">
|
|
12582
|
+
<select id="sort-filter" onchange="filterAndSortPlugins()" class="block w-full sm:w-auto h-9 rounded-md border-0 py-1.5 pl-3 pr-8 text-zinc-900 ring-1 ring-inset ring-zinc-300 focus:ring-2 focus:ring-inset focus:ring-zinc-600 dark:bg-zinc-900 dark:text-white dark:ring-zinc-700 dark:focus:ring-zinc-500 sm:text-sm sm:leading-6">
|
|
12583
|
+
<option value="name-asc">Name (A-Z)</option>
|
|
12584
|
+
<option value="name-desc">Name (Z-A)</option>
|
|
12585
|
+
<option value="newest">Newest Installed</option>
|
|
12586
|
+
<option value="updated">Recently Updated</option>
|
|
12587
|
+
<option value="popular">Popularity</option>
|
|
12588
|
+
<option value="rating">Highest Rated</option>
|
|
12589
|
+
</select>
|
|
12453
12590
|
|
|
12454
|
-
|
|
12455
|
-
|
|
12456
|
-
|
|
12457
|
-
|
|
12458
|
-
<
|
|
12459
|
-
<
|
|
12460
|
-
|
|
12461
|
-
|
|
12462
|
-
<option value="">All Categories</option>
|
|
12463
|
-
<option value="content">Content Management</option>
|
|
12464
|
-
<option value="media">Media</option>
|
|
12465
|
-
<option value="seo">SEO & Analytics</option>
|
|
12466
|
-
<option value="security">Security</option>
|
|
12467
|
-
<option value="utilities">Utilities</option>
|
|
12468
|
-
<option value="system">System</option>
|
|
12469
|
-
<option value="development">Development</option>
|
|
12470
|
-
<option value="demo">Demo</option>
|
|
12471
|
-
</select>
|
|
12472
|
-
<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">
|
|
12473
|
-
<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" />
|
|
12474
|
-
</svg>
|
|
12475
|
-
</div>
|
|
12476
|
-
</div>
|
|
12477
|
-
<div>
|
|
12478
|
-
<label class="block text-sm/6 font-medium text-zinc-950 dark:text-white">Status</label>
|
|
12479
|
-
<div class="mt-2 grid grid-cols-1">
|
|
12480
|
-
<select id="status-filter" onchange="filterPlugins()" 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-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">
|
|
12481
|
-
<option value="">All Status</option>
|
|
12482
|
-
<option value="active">Active</option>
|
|
12483
|
-
<option value="inactive">Inactive</option>
|
|
12484
|
-
<option value="uninstalled">Available to Install</option>
|
|
12485
|
-
<option value="error">Error</option>
|
|
12486
|
-
</select>
|
|
12487
|
-
<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">
|
|
12488
|
-
<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" />
|
|
12489
|
-
</svg>
|
|
12490
|
-
</div>
|
|
12491
|
-
</div>
|
|
12492
|
-
<div class="flex-1 max-w-md">
|
|
12493
|
-
<label class="block text-sm font-medium text-zinc-950 dark:text-white mb-2">Search</label>
|
|
12494
|
-
<div class="relative group">
|
|
12495
|
-
<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">
|
|
12496
|
-
<svg class="h-3 w-3 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2.5">
|
|
12497
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/>
|
|
12498
|
-
</svg>
|
|
12499
|
-
</div>
|
|
12500
|
-
<input
|
|
12501
|
-
id="search-input"
|
|
12502
|
-
type="text"
|
|
12503
|
-
placeholder="Search plugins..."
|
|
12504
|
-
oninput="filterPlugins()"
|
|
12505
|
-
class="w-full rounded-full bg-transparent px-11 py-2 text-sm text-zinc-950 dark:text-white placeholder-zinc-500 dark:placeholder-zinc-400 border-2 border-cyan-200/50 dark:border-cyan-700/50 focus:outline-none focus:border-cyan-500 dark:focus:border-cyan-400 focus:shadow-lg focus:shadow-cyan-500/20 dark:focus:shadow-cyan-400/20 transition-all duration-300"
|
|
12506
|
-
/>
|
|
12507
|
-
</div>
|
|
12508
|
-
</div>
|
|
12509
|
-
</div>
|
|
12510
|
-
<div class="flex items-center gap-x-3 ml-4">
|
|
12511
|
-
<button
|
|
12512
|
-
onclick="location.reload()"
|
|
12513
|
-
class="inline-flex items-center gap-x-1.5 px-3 py-1.5 bg-white/90 dark:bg-zinc-800/90 backdrop-blur-sm text-zinc-950 dark:text-white text-sm font-medium rounded-full ring-1 ring-inset ring-cyan-200/50 dark:ring-cyan-700/50 hover:bg-gradient-to-r hover:from-cyan-50 hover:to-blue-50 dark:hover:from-cyan-900/30 dark:hover:to-blue-900/30 hover:ring-cyan-300 dark:hover:ring-cyan-600 transition-all duration-200"
|
|
12514
|
-
>
|
|
12515
|
-
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12516
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
12517
|
-
</svg>
|
|
12518
|
-
Refresh
|
|
12519
|
-
</button>
|
|
12520
|
-
</div>
|
|
12591
|
+
<button
|
|
12592
|
+
onclick="location.reload()"
|
|
12593
|
+
class="inline-flex items-center gap-x-1.5 rounded-md bg-white dark:bg-zinc-900 px-3 py-1.5 h-9 text-sm font-semibold text-zinc-900 dark:text-white shadow-sm ring-1 ring-inset ring-zinc-300 dark:ring-zinc-700 hover:bg-zinc-50 dark:hover:bg-zinc-800"
|
|
12594
|
+
>
|
|
12595
|
+
<svg class="h-4 w-4 text-zinc-500 dark:text-zinc-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12596
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"/>
|
|
12597
|
+
</svg>
|
|
12598
|
+
</button>
|
|
12521
12599
|
</div>
|
|
12522
12600
|
</div>
|
|
12601
|
+
|
|
12602
|
+
<!-- Plugins Grid -->
|
|
12603
|
+
<div id="plugins-grid" class="grid gap-6" style="grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));">
|
|
12604
|
+
${data.plugins.map((plugin) => renderPluginCard(plugin)).join("")}
|
|
12605
|
+
</div>
|
|
12523
12606
|
</div>
|
|
12524
12607
|
</div>
|
|
12525
|
-
|
|
12526
|
-
<!-- Plugins Grid -->
|
|
12527
|
-
<div id="plugins-grid" class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
|
12528
|
-
${data.plugins.map((plugin) => renderPluginCard(plugin)).join("")}
|
|
12529
12608
|
</div>
|
|
12530
12609
|
|
|
12531
12610
|
<script>
|
|
12532
|
-
async function togglePlugin(pluginId, action) {
|
|
12533
|
-
const button = event.target;
|
|
12534
|
-
|
|
12611
|
+
async function togglePlugin(pluginId, action, event) {
|
|
12612
|
+
const button = event.target.closest('button');
|
|
12613
|
+
if (!button) return;
|
|
12614
|
+
|
|
12535
12615
|
button.disabled = true;
|
|
12536
|
-
button.
|
|
12616
|
+
button.classList.add('opacity-50', 'cursor-wait');
|
|
12537
12617
|
|
|
12538
12618
|
try {
|
|
12539
12619
|
const response = await fetch(\`/admin/plugins/\${pluginId}/\${action}\`, {
|
|
@@ -12549,27 +12629,36 @@ function renderPluginsListPage(data) {
|
|
|
12549
12629
|
// Update UI
|
|
12550
12630
|
const card = button.closest('.plugin-card');
|
|
12551
12631
|
const statusBadge = card.querySelector('.status-badge');
|
|
12632
|
+
const knob = button.querySelector('.toggle-knob');
|
|
12552
12633
|
|
|
12553
12634
|
if (action === 'activate') {
|
|
12554
12635
|
// Update status badge
|
|
12555
|
-
statusBadge.className = 'status-badge inline-flex items-center rounded-
|
|
12556
|
-
statusBadge.innerHTML = '<div class="w-
|
|
12557
|
-
|
|
12558
|
-
|
|
12559
|
-
|
|
12560
|
-
button.
|
|
12561
|
-
button.onclick = () => togglePlugin(pluginId, 'deactivate');
|
|
12562
|
-
|
|
12636
|
+
statusBadge.className = 'status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 ring-emerald-600/20';
|
|
12637
|
+
statusBadge.innerHTML = '<div class="w-1.5 h-1.5 bg-emerald-500 dark:bg-emerald-400 rounded-full mr-1.5"></div>Active';
|
|
12638
|
+
|
|
12639
|
+
// Update button state to Active
|
|
12640
|
+
button.className = 'bg-emerald-600 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button';
|
|
12641
|
+
button.setAttribute('aria-checked', 'true');
|
|
12642
|
+
button.onclick = (event) => togglePlugin(pluginId, 'deactivate', event);
|
|
12643
|
+
|
|
12644
|
+
// Update knob position
|
|
12645
|
+
if (knob) {
|
|
12646
|
+
knob.className = 'translate-x-5 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob';
|
|
12647
|
+
}
|
|
12563
12648
|
} else {
|
|
12564
12649
|
// Update status badge
|
|
12565
|
-
statusBadge.className = 'status-badge inline-flex items-center rounded-
|
|
12566
|
-
statusBadge.innerHTML = '<div class="w-
|
|
12567
|
-
|
|
12568
|
-
|
|
12569
|
-
|
|
12570
|
-
button.
|
|
12571
|
-
button.onclick = () => togglePlugin(pluginId, 'activate');
|
|
12572
|
-
|
|
12650
|
+
statusBadge.className = 'status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-600/20';
|
|
12651
|
+
statusBadge.innerHTML = '<div class="w-1.5 h-1.5 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-1.5"></div>Inactive';
|
|
12652
|
+
|
|
12653
|
+
// Update button state to Inactive
|
|
12654
|
+
button.className = 'bg-zinc-200 dark:bg-zinc-700 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button';
|
|
12655
|
+
button.setAttribute('aria-checked', 'false');
|
|
12656
|
+
button.onclick = (event) => togglePlugin(pluginId, 'activate', event);
|
|
12657
|
+
|
|
12658
|
+
// Update knob position
|
|
12659
|
+
if (knob) {
|
|
12660
|
+
knob.className = 'translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob';
|
|
12661
|
+
}
|
|
12573
12662
|
}
|
|
12574
12663
|
|
|
12575
12664
|
showNotification(\`Plugin \${action}d successfully\`, 'success');
|
|
@@ -12578,9 +12667,9 @@ function renderPluginsListPage(data) {
|
|
|
12578
12667
|
}
|
|
12579
12668
|
} catch (error) {
|
|
12580
12669
|
showNotification(error.message, 'error');
|
|
12581
|
-
button.textContent = originalText;
|
|
12582
12670
|
} finally {
|
|
12583
12671
|
button.disabled = false;
|
|
12672
|
+
button.classList.remove('opacity-50', 'cursor-wait');
|
|
12584
12673
|
}
|
|
12585
12674
|
}
|
|
12586
12675
|
|
|
@@ -12664,81 +12753,92 @@ function renderPluginsListPage(data) {
|
|
|
12664
12753
|
showNotification('Plugin details coming soon!', 'info');
|
|
12665
12754
|
}
|
|
12666
12755
|
|
|
12667
|
-
function
|
|
12668
|
-
|
|
12669
|
-
const
|
|
12670
|
-
|
|
12671
|
-
|
|
12672
|
-
|
|
12673
|
-
|
|
12674
|
-
|
|
12675
|
-
|
|
12676
|
-
}, 3000);
|
|
12677
|
-
}
|
|
12678
|
-
|
|
12679
|
-
function filterPlugins() {
|
|
12680
|
-
const categoryFilter = document.getElementById('category-filter').value.toLowerCase();
|
|
12681
|
-
const statusFilter = document.getElementById('status-filter').value.toLowerCase();
|
|
12756
|
+
function filterAndSortPlugins() {
|
|
12757
|
+
// Get checked categories
|
|
12758
|
+
const checkedCategories = Array.from(document.querySelectorAll('input[name="category"]:checked'))
|
|
12759
|
+
.map(cb => cb.value.toLowerCase());
|
|
12760
|
+
|
|
12761
|
+
// Get checked statuses
|
|
12762
|
+
const checkedStatuses = Array.from(document.querySelectorAll('input[name="status"]:checked'))
|
|
12763
|
+
.map(cb => cb.value.toLowerCase());
|
|
12764
|
+
|
|
12682
12765
|
const searchInput = document.getElementById('search-input').value.toLowerCase();
|
|
12766
|
+
const sortValue = document.getElementById('sort-filter').value;
|
|
12683
12767
|
|
|
12684
|
-
const
|
|
12685
|
-
|
|
12686
|
-
|
|
12687
|
-
|
|
12688
|
-
|
|
12768
|
+
const pluginsGrid = document.getElementById('plugins-grid');
|
|
12769
|
+
const pluginCards = Array.from(pluginsGrid.querySelectorAll('.plugin-card'));
|
|
12770
|
+
|
|
12771
|
+
// Filter
|
|
12772
|
+
const visibleCards = pluginCards.filter(card => {
|
|
12689
12773
|
const category = card.getAttribute('data-category')?.toLowerCase() || '';
|
|
12690
12774
|
const status = card.getAttribute('data-status')?.toLowerCase() || '';
|
|
12691
12775
|
const name = card.getAttribute('data-name')?.toLowerCase() || '';
|
|
12692
12776
|
const description = card.getAttribute('data-description')?.toLowerCase() || '';
|
|
12693
12777
|
|
|
12694
|
-
//
|
|
12695
|
-
|
|
12696
|
-
|
|
12697
|
-
//
|
|
12698
|
-
if (
|
|
12699
|
-
|
|
12700
|
-
|
|
12701
|
-
|
|
12702
|
-
|
|
12703
|
-
|
|
12704
|
-
|
|
12705
|
-
}
|
|
12706
|
-
|
|
12707
|
-
// Search filter - check if search term is in name or description
|
|
12708
|
-
if (searchInput && !name.includes(searchInput) && !description.includes(searchInput)) {
|
|
12709
|
-
matches = false;
|
|
12710
|
-
}
|
|
12778
|
+
// Category filter: if any selected, must match one of them
|
|
12779
|
+
if (checkedCategories.length > 0 && !checkedCategories.includes(category)) return false;
|
|
12780
|
+
|
|
12781
|
+
// Status filter: if any selected, must match one of them
|
|
12782
|
+
if (checkedStatuses.length > 0 && !checkedStatuses.includes(status)) return false;
|
|
12783
|
+
|
|
12784
|
+
// Search filter
|
|
12785
|
+
if (searchInput && !name.includes(searchInput) && !description.includes(searchInput)) return false;
|
|
12786
|
+
|
|
12787
|
+
return true;
|
|
12788
|
+
});
|
|
12711
12789
|
|
|
12712
|
-
|
|
12713
|
-
|
|
12714
|
-
|
|
12715
|
-
|
|
12716
|
-
|
|
12717
|
-
|
|
12790
|
+
// Sort
|
|
12791
|
+
visibleCards.sort((a, b) => {
|
|
12792
|
+
const aName = a.getAttribute('data-name') || '';
|
|
12793
|
+
const bName = b.getAttribute('data-name') || '';
|
|
12794
|
+
const aInstalled = parseInt(a.getAttribute('data-installed') || '0');
|
|
12795
|
+
const bInstalled = parseInt(b.getAttribute('data-installed') || '0');
|
|
12796
|
+
const aUpdated = parseInt(a.getAttribute('data-updated') || '0');
|
|
12797
|
+
const bUpdated = parseInt(b.getAttribute('data-updated') || '0');
|
|
12798
|
+
const aDownloads = parseInt(a.getAttribute('data-downloads') || '0');
|
|
12799
|
+
const bDownloads = parseInt(b.getAttribute('data-downloads') || '0');
|
|
12800
|
+
const aRating = parseFloat(a.getAttribute('data-rating') || '0');
|
|
12801
|
+
const bRating = parseFloat(b.getAttribute('data-rating') || '0');
|
|
12802
|
+
|
|
12803
|
+
switch (sortValue) {
|
|
12804
|
+
case 'name-desc': return bName.localeCompare(aName);
|
|
12805
|
+
case 'newest': return bInstalled - aInstalled;
|
|
12806
|
+
case 'updated': return bUpdated - aUpdated;
|
|
12807
|
+
case 'popular': return bDownloads - aDownloads;
|
|
12808
|
+
case 'rating': return bRating - aRating;
|
|
12809
|
+
case 'name-asc':
|
|
12810
|
+
default: return aName.localeCompare(bName);
|
|
12718
12811
|
}
|
|
12719
12812
|
});
|
|
12720
12813
|
|
|
12721
|
-
//
|
|
12814
|
+
// Re-append
|
|
12815
|
+
pluginCards.forEach(card => card.style.display = 'none'); // Hide all first
|
|
12816
|
+
|
|
12817
|
+
// If no results
|
|
12722
12818
|
let noResultsMsg = document.getElementById('no-results-message');
|
|
12723
|
-
if (
|
|
12819
|
+
if (visibleCards.length === 0) {
|
|
12724
12820
|
if (!noResultsMsg) {
|
|
12725
12821
|
noResultsMsg = document.createElement('div');
|
|
12726
12822
|
noResultsMsg.id = 'no-results-message';
|
|
12727
|
-
noResultsMsg.className = 'col-span-full text-center py-12';
|
|
12823
|
+
noResultsMsg.className = 'col-span-full text-center py-12 bg-zinc-50 dark:bg-zinc-800/50 rounded-lg border border-dashed border-zinc-300 dark:border-zinc-700';
|
|
12728
12824
|
noResultsMsg.innerHTML = \`
|
|
12729
12825
|
<div class="flex flex-col items-center">
|
|
12730
|
-
<svg class="w-
|
|
12826
|
+
<svg class="w-12 h-12 text-zinc-400 dark:text-zinc-600 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12731
12827
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 10h.01M15 10h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
12732
12828
|
</svg>
|
|
12733
|
-
<h3 class="text-
|
|
12829
|
+
<h3 class="text-base font-semibold text-zinc-950 dark:text-white mb-1">No plugins found</h3>
|
|
12734
12830
|
<p class="text-sm text-zinc-500 dark:text-zinc-400">Try adjusting your filters or search terms</p>
|
|
12735
12831
|
</div>
|
|
12736
12832
|
\`;
|
|
12737
|
-
|
|
12833
|
+
pluginsGrid.appendChild(noResultsMsg);
|
|
12738
12834
|
}
|
|
12739
12835
|
noResultsMsg.style.display = '';
|
|
12740
|
-
} else
|
|
12741
|
-
noResultsMsg.style.display = 'none';
|
|
12836
|
+
} else {
|
|
12837
|
+
if (noResultsMsg) noResultsMsg.style.display = 'none';
|
|
12838
|
+
visibleCards.forEach(card => {
|
|
12839
|
+
card.style.display = '';
|
|
12840
|
+
pluginsGrid.appendChild(card); // Re-appending moves it to the end, effectively sorting
|
|
12841
|
+
});
|
|
12742
12842
|
}
|
|
12743
12843
|
}
|
|
12744
12844
|
</script>
|
|
@@ -12769,116 +12869,111 @@ function renderPluginsListPage(data) {
|
|
|
12769
12869
|
}
|
|
12770
12870
|
function renderPluginCard(plugin) {
|
|
12771
12871
|
const statusColors = {
|
|
12772
|
-
active: "bg-
|
|
12773
|
-
inactive: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-
|
|
12774
|
-
error: "bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-400 ring-red-
|
|
12775
|
-
uninstalled: "bg-zinc-
|
|
12872
|
+
active: "bg-emerald-50 dark:bg-emerald-500/10 text-emerald-700 dark:text-emerald-400 ring-emerald-600/20",
|
|
12873
|
+
inactive: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-700 dark:text-zinc-400 ring-zinc-600/20",
|
|
12874
|
+
error: "bg-red-50 dark:bg-red-500/10 text-red-700 dark:text-red-400 ring-red-600/20",
|
|
12875
|
+
uninstalled: "bg-zinc-50 dark:bg-zinc-500/10 text-zinc-600 dark:text-zinc-500 ring-zinc-600/20"
|
|
12776
12876
|
};
|
|
12777
12877
|
const statusIcons = {
|
|
12778
|
-
active: '<div class="w-
|
|
12779
|
-
inactive: '<div class="w-
|
|
12780
|
-
error: '<div class="w-
|
|
12781
|
-
uninstalled: '<div class="w-
|
|
12782
|
-
};
|
|
12783
|
-
const borderColors = {
|
|
12784
|
-
active: "ring-[3px] ring-lime-500 dark:ring-lime-400",
|
|
12785
|
-
inactive: "ring-[3px] ring-pink-500 dark:ring-pink-400",
|
|
12786
|
-
error: "ring-[3px] ring-red-500 dark:ring-red-400",
|
|
12787
|
-
uninstalled: "ring-[3px] ring-zinc-400 dark:ring-zinc-600"
|
|
12878
|
+
active: '<div class="w-1.5 h-1.5 bg-emerald-500 dark:bg-emerald-400 rounded-full mr-1.5"></div>',
|
|
12879
|
+
inactive: '<div class="w-1.5 h-1.5 bg-zinc-500 dark:bg-zinc-400 rounded-full mr-1.5"></div>',
|
|
12880
|
+
error: '<div class="w-1.5 h-1.5 bg-red-500 dark:bg-red-400 rounded-full mr-1.5"></div>',
|
|
12881
|
+
uninstalled: '<div class="w-1.5 h-1.5 bg-zinc-400 dark:bg-zinc-600 rounded-full mr-1.5"></div>'
|
|
12788
12882
|
};
|
|
12789
12883
|
const criticalCorePlugins = ["core-auth", "core-media"];
|
|
12790
12884
|
const canToggle = !criticalCorePlugins.includes(plugin.id);
|
|
12791
12885
|
let actionButton = "";
|
|
12792
12886
|
if (plugin.status === "uninstalled") {
|
|
12793
|
-
actionButton = `<button onclick="installPlugin('${plugin.name}')" class="bg-
|
|
12794
|
-
} else if (plugin.status === "active") {
|
|
12795
|
-
actionButton = `<button onclick="togglePlugin('${plugin.id}', 'deactivate')" class="bg-red-600 dark:bg-red-700 hover:bg-red-700 dark:hover:bg-red-600 text-white px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">Deactivate</button>`;
|
|
12887
|
+
actionButton = `<button onclick="installPlugin('${plugin.name}')" class="w-full sm:w-auto bg-zinc-900 dark:bg-white hover:bg-zinc-800 dark:hover:bg-zinc-100 text-white dark:text-zinc-900 px-3 py-1.5 rounded-md text-xs font-medium transition-colors shadow-sm">Install</button>`;
|
|
12796
12888
|
} else {
|
|
12797
|
-
|
|
12889
|
+
const isActive = plugin.status === "active";
|
|
12890
|
+
const action = isActive ? "deactivate" : "activate";
|
|
12891
|
+
const bgClass = isActive ? "bg-emerald-600" : "bg-zinc-200 dark:bg-zinc-700";
|
|
12892
|
+
const translateClass = isActive ? "translate-x-5" : "translate-x-0";
|
|
12893
|
+
if (canToggle) {
|
|
12894
|
+
actionButton = `
|
|
12895
|
+
<button onclick="togglePlugin('${plugin.id}', '${action}', event)" type="button" class="${bgClass} relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-emerald-600 focus:ring-offset-2 toggle-button" role="switch" aria-checked="${isActive}">
|
|
12896
|
+
<span class="sr-only">Toggle plugin</span>
|
|
12897
|
+
<span aria-hidden="true" class="${translateClass} pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out toggle-knob"></span>
|
|
12898
|
+
</button>
|
|
12899
|
+
`;
|
|
12900
|
+
} else {
|
|
12901
|
+
actionButton = `
|
|
12902
|
+
<div class="relative inline-flex h-6 w-11 flex-shrink-0 cursor-not-allowed rounded-full border-2 border-transparent bg-emerald-600/50 opacity-50" title="Core plugin cannot be disabled">
|
|
12903
|
+
<span class="translate-x-5 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0"></span>
|
|
12904
|
+
</div>
|
|
12905
|
+
`;
|
|
12906
|
+
}
|
|
12798
12907
|
}
|
|
12799
12908
|
return `
|
|
12800
|
-
<div class="plugin-card rounded-
|
|
12909
|
+
<div class="plugin-card flex flex-col h-full rounded-md bg-white dark:bg-zinc-900 ring-1 ring-zinc-950/10 dark:ring-white/10 p-5 transition-all hover:shadow-md"
|
|
12910
|
+
data-category="${plugin.category}"
|
|
12911
|
+
data-status="${plugin.status}"
|
|
12912
|
+
data-name="${plugin.displayName}"
|
|
12913
|
+
data-description="${plugin.description}"
|
|
12914
|
+
data-downloads="${plugin.downloadCount || 0}"
|
|
12915
|
+
data-rating="${plugin.rating || 0}">
|
|
12801
12916
|
<div class="flex items-start justify-between mb-4">
|
|
12802
12917
|
<div class="flex items-center gap-3">
|
|
12803
|
-
<div class="w-
|
|
12918
|
+
<div class="w-10 h-10 rounded-md flex items-center justify-center bg-zinc-50 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 ring-1 ring-inset ring-zinc-200 dark:ring-zinc-700/50">
|
|
12804
12919
|
${plugin.icon || getDefaultPluginIcon(plugin.category)}
|
|
12805
12920
|
</div>
|
|
12806
12921
|
<div>
|
|
12807
|
-
<
|
|
12808
|
-
|
|
12922
|
+
<div class="flex items-center gap-2">
|
|
12923
|
+
<h3 class="text-sm font-semibold text-zinc-900 dark:text-white">${plugin.displayName}</h3>
|
|
12924
|
+
<span class="status-badge inline-flex items-center rounded-full px-1.5 py-0.5 text-[10px] font-medium ring-1 ring-inset ${statusColors[plugin.status]}">
|
|
12925
|
+
${statusIcons[plugin.status]}${plugin.status.charAt(0).toUpperCase() + plugin.status.slice(1)}
|
|
12926
|
+
</span>
|
|
12927
|
+
</div>
|
|
12928
|
+
<p class="text-xs text-zinc-500 dark:text-zinc-400">v${plugin.version} \u2022 ${plugin.author}</p>
|
|
12809
12929
|
</div>
|
|
12810
12930
|
</div>
|
|
12811
|
-
|
|
12812
|
-
|
|
12813
|
-
|
|
12814
|
-
|
|
12815
|
-
|
|
12931
|
+
|
|
12932
|
+
<div class="flex items-center gap-1">
|
|
12933
|
+
${plugin.status !== "uninstalled" ? `
|
|
12934
|
+
<button onclick="showPluginDetails('${plugin.id}')" class="text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Plugin Details">
|
|
12935
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12936
|
+
<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"/>
|
|
12937
|
+
</svg>
|
|
12938
|
+
</button>
|
|
12939
|
+
` : ""}
|
|
12940
|
+
|
|
12941
|
+
${!plugin.isCore && plugin.status !== "uninstalled" ? `
|
|
12942
|
+
<button onclick="uninstallPlugin('${plugin.id}')" class="text-zinc-400 hover:text-red-600 dark:text-zinc-500 dark:hover:text-red-400 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Uninstall Plugin">
|
|
12943
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12944
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
|
12945
|
+
</svg>
|
|
12946
|
+
</button>
|
|
12947
|
+
` : ""}
|
|
12816
12948
|
</div>
|
|
12817
12949
|
</div>
|
|
12818
12950
|
|
|
12819
|
-
<p class="text-zinc-600 dark:text-zinc-
|
|
12951
|
+
<p class="text-zinc-600 dark:text-zinc-400 text-sm mb-4 line-clamp-2 flex-grow">${plugin.description}</p>
|
|
12820
12952
|
|
|
12821
|
-
<div class="flex items-center gap-
|
|
12822
|
-
<span class="flex items-center
|
|
12823
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12824
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z"/>
|
|
12825
|
-
</svg>
|
|
12953
|
+
<div class="flex flex-wrap items-center gap-2 mb-5">
|
|
12954
|
+
<span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">
|
|
12826
12955
|
${plugin.category}
|
|
12827
12956
|
</span>
|
|
12828
|
-
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
<
|
|
12832
|
-
|
|
12833
|
-
</
|
|
12834
|
-
|
|
12835
|
-
</span>
|
|
12836
|
-
` : ""}
|
|
12837
|
-
|
|
12838
|
-
${plugin.rating ? `
|
|
12839
|
-
<span class="flex items-center gap-1">
|
|
12840
|
-
<svg class="w-4 h-4 text-yellow-500 dark:text-yellow-400 fill-current" viewBox="0 0 24 24">
|
|
12841
|
-
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
|
12842
|
-
</svg>
|
|
12843
|
-
${plugin.rating}
|
|
12844
|
-
</span>
|
|
12845
|
-
` : ""}
|
|
12846
|
-
|
|
12847
|
-
<span>${plugin.lastUpdated}</span>
|
|
12848
|
-
</div>
|
|
12849
|
-
|
|
12850
|
-
${plugin.dependencies && plugin.dependencies.length > 0 ? `
|
|
12851
|
-
<div class="mb-4">
|
|
12852
|
-
<p class="text-xs text-zinc-500 dark:text-zinc-400 mb-2">Dependencies:</p>
|
|
12853
|
-
<div class="flex flex-wrap gap-1">
|
|
12854
|
-
${plugin.dependencies.map((dep) => `<span class="inline-block bg-zinc-100 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 text-xs px-2 py-1 rounded">${dep}</span>`).join("")}
|
|
12855
|
-
</div>
|
|
12957
|
+
${plugin.isCore ? '<span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">Core</span>' : ""}
|
|
12958
|
+
|
|
12959
|
+
${plugin.dependencies && plugin.dependencies.map((dep) => `
|
|
12960
|
+
<span class="inline-flex items-center rounded-md bg-zinc-100 dark:bg-zinc-800 px-2 py-1 text-xs font-medium text-zinc-600 dark:text-zinc-400">
|
|
12961
|
+
${dep}
|
|
12962
|
+
</span>
|
|
12963
|
+
`).join("") || ""}
|
|
12856
12964
|
</div>
|
|
12857
|
-
` : ""}
|
|
12858
12965
|
|
|
12859
|
-
<div class="flex items-center justify-between">
|
|
12966
|
+
<div class="flex items-center justify-between pt-4 border-t border-zinc-100 dark:border-zinc-800 mt-auto">
|
|
12860
12967
|
<div class="flex gap-2">
|
|
12861
|
-
${
|
|
12862
|
-
${plugin.status !== "uninstalled" ? `
|
|
12863
|
-
<button onclick="openPluginSettings('${plugin.id}')" class="bg-white dark:bg-zinc-800 hover:bg-zinc-50 dark:hover:bg-zinc-700 text-zinc-950 dark:text-white ring-1 ring-inset ring-zinc-950/10 dark:ring-white/10 px-3 py-1.5 rounded-lg text-sm font-medium transition-colors">
|
|
12864
|
-
Settings
|
|
12865
|
-
</button>
|
|
12866
|
-
` : ""}
|
|
12968
|
+
${actionButton}
|
|
12867
12969
|
</div>
|
|
12868
12970
|
|
|
12869
12971
|
<div class="flex items-center gap-2">
|
|
12870
12972
|
${plugin.status !== "uninstalled" ? `
|
|
12871
|
-
<button onclick="
|
|
12872
|
-
<svg class="w-
|
|
12873
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="
|
|
12874
|
-
|
|
12875
|
-
</button>
|
|
12876
|
-
` : ""}
|
|
12877
|
-
|
|
12878
|
-
${!plugin.isCore && plugin.status !== "uninstalled" ? `
|
|
12879
|
-
<button onclick="uninstallPlugin('${plugin.id}')" class="text-zinc-500 dark:text-zinc-400 hover:text-red-600 dark:hover:text-red-400 p-1.5 rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Uninstall Plugin">
|
|
12880
|
-
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12881
|
-
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
|
|
12973
|
+
<button onclick="openPluginSettings('${plugin.id}')" class="text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300 p-1.5 rounded-md hover:bg-zinc-100 dark:hover:bg-zinc-800 transition-colors" title="Settings">
|
|
12974
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
12975
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/>
|
|
12976
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
|
|
12882
12977
|
</svg>
|
|
12883
12978
|
</button>
|
|
12884
12979
|
` : ""}
|
|
@@ -21662,5 +21757,5 @@ var ROUTES_INFO = {
|
|
|
21662
21757
|
};
|
|
21663
21758
|
|
|
21664
21759
|
export { PluginBuilder, ROUTES_INFO, adminCheckboxRoutes, adminCollectionsRoutes, adminDesignRoutes, adminLogsRoutes, adminMediaRoutes, adminPluginRoutes, adminSettingsRoutes, admin_api_default, admin_code_examples_default, admin_content_default, admin_testimonials_default, api_content_crud_default, api_default, api_media_default, api_system_default, auth_default, router, test_cleanup_default, userRoutes };
|
|
21665
|
-
//# sourceMappingURL=chunk-
|
|
21666
|
-
//# sourceMappingURL=chunk-
|
|
21760
|
+
//# sourceMappingURL=chunk-4M2UOUXV.js.map
|
|
21761
|
+
//# sourceMappingURL=chunk-4M2UOUXV.js.map
|