nextjs-cms 0.6.0 → 0.6.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"serverActions.d.ts","sourceRoot":"","sources":["../../../src/api/lib/serverActions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serverActions.d.ts","sourceRoot":"","sources":["../../../src/api/lib/serverActions.ts"],"names":[],"mappings":"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"}
@@ -262,7 +262,7 @@ export const getAllPrivileges = async (session) => {
262
262
  if (pluginPrivileges.has(route.pluginName))
263
263
  return;
264
264
  pluginPrivileges.set(route.pluginName, {
265
- title: route.pluginTitle,
265
+ title: resolveLocalizedString(route.title, locale, cmsConfig.i18n.fallbackLanguage),
266
266
  });
267
267
  });
268
268
  Array.from(pluginPrivileges.entries()).forEach(([pluginName, meta], index) => {
@@ -371,13 +371,13 @@ export const deleteSectionItem = async (session, sectionName, sectionItemId, rec
371
371
  */
372
372
  sectionItemId = sectionItemId.toString();
373
373
  try {
374
- const _s = await SectionFactory.getSectionForAdmin({
374
+ const _s = (await SectionFactory.getSectionForAdmin({
375
375
  name: sectionName,
376
376
  admin: {
377
377
  id: session.user.id,
378
378
  requiredRole: 'D',
379
379
  },
380
- });
380
+ }));
381
381
  const section = _s?.build();
382
382
  if (!section) {
383
383
  return {
@@ -530,8 +530,8 @@ export const deleteSectionItem = async (session, sectionName, sectionItemId, rec
530
530
  }
531
531
  const headingFieldName = section.headingField.name;
532
532
  const entityLabel = headingFieldName && sectionItemRow
533
- // @ts-ignore
534
- ? String(sectionItemRow[headingFieldName] ?? '')
533
+ ? // @ts-ignore
534
+ String(sectionItemRow[headingFieldName] ?? '')
535
535
  : null;
536
536
  await recordLog({
537
537
  eventType: 'section.item.delete',
@@ -954,9 +954,12 @@ export const getSidebar = async (session) => {
954
954
  });
955
955
  const pluginRoutes = await getPluginRoutes();
956
956
  const privilegeSet = await getAdminPrivileges(session.user.id);
957
- const allowedPluginRoutes = pluginRoutes.filter(({ pluginName }) => privilegeSet.has(pluginName));
958
- // Get the locale for resolving localized titles
957
+ // Get config and check for dashboard override
959
958
  const cmsConfig = await getCMSConfig();
959
+ const dashboardOverridePath = cmsConfig.dashboard?.override;
960
+ // Include dashboard override plugin even without explicit privilege
961
+ const allowedPluginRoutes = pluginRoutes.filter(({ pluginName, path }) => privilegeSet.has(pluginName) || path === dashboardOverridePath);
962
+ // Get the locale for resolving localized titles
960
963
  const locale = resolveLocale(session.user.locale, cmsConfig.i18n.supportedLanguages, cmsConfig.i18n.fallbackLanguage);
961
964
  // Let's loop through the sections and add path, icon and title to each section item
962
965
  const simpleSectionItems = simple.map((section) => {
@@ -980,22 +983,25 @@ export const getSidebar = async (session) => {
980
983
  icon: section.icon,
981
984
  };
982
985
  });
983
- const pluginSections = allowedPluginRoutes.map((route) => ({
984
- title: route.title,
986
+ // Filter out dashboard override from plugin sections (it becomes the dashboard entry)
987
+ const pluginSections = allowedPluginRoutes
988
+ .filter((route) => route.path !== dashboardOverridePath)
989
+ .map((route) => ({
990
+ title: resolveLocalizedString(route.title, locale, cmsConfig.i18n.fallbackLanguage),
985
991
  path: route.path,
986
992
  icon: route.icon ?? '',
987
993
  }));
988
994
  const fixedSections = [
989
995
  /**
990
- * Add the default dashboard section
996
+ * Add the dashboard section (points to override if configured)
991
997
  */
992
998
  {
993
999
  title: getString('dashboard', session.user.locale),
994
- path: '/dashboard',
1000
+ path: dashboardOverridePath ?? '/dashboard',
995
1001
  icon: 'home',
996
1002
  },
997
1003
  /**
998
- * Add the plugin sections
1004
+ * Add the plugin sections (excluding dashboard override)
999
1005
  */
1000
1006
  ...pluginSections,
1001
1007
  ...fixed.map((section) => ({ title: getString(section, session.user.locale), path: `/${section}`, icon: '' })),
@@ -3,6 +3,6 @@ import { createHydrationHelpers } from '@trpc/react-query/rsc';
3
3
  import { type AppRouter } from '../root.js';
4
4
  export declare const api: ReturnType<typeof createHydrationHelpers<AppRouter>>["trpc"];
5
5
  export declare const HydrateClient: (props: {
6
- children: import("react").ReactNode;
7
- }) => import("react").JSX.Element;
6
+ children: React.ReactNode;
7
+ }) => React.JSX.Element;
8
8
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/api/trpc/server.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AA8DzD,eAAO,MAAM,GAAG,EAAc,UAAU,CAAC,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAC1F,eAAO,MAAM,aAAa;;iCAAmB,CAAA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../src/api/trpc/server.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,CAAA;AAEpB,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAI9D,OAAO,EAAgB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAA;AA8DzD,eAAO,MAAM,GAAG,EAAc,UAAU,CAAC,OAAO,sBAAsB,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;AAC1F,eAAO,MAAM,aAAa;;uBAAmB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/api/trpc.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,WAAW,CAAA;AAQjC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE;aAAT,OAAO;;;;;EAQ/D,CAAA;AAED,KAAK,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAA;AAC5D,MAAM,MAAM,WAAW,GAAG,OAAO,CAAA;AAEjC,eAAO,MAAM,WAAW;;;CAGvB,CAAA;AAkBD;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;iBAtCyB,OAAO;;;;;;;;;;;;;;;;;;;EAsCR,CAAA;AAiBxD;;;GAGG;AACH,eAAO,MAAM,MAAM;;iBA3DsC,OAAO;;;;;;;;;;;;;;;;;;;EA2DlC,CAAA;AAE9B;;GAEG;AACH,eAAO,MAAM,eAAe;aAhE6B,OAAO;;;;;yLAgEtB,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,gBAAgB;aAtE4B,OAAO;;;;;;;;;yKAsEG,CAAA;AAEnE,KAAK,kBAAkB,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AA+BzC,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,eAAe,kBAAkB;aAvG7B,OAAO;;;;;;;;;yKAwGO,CAAA"}
1
+ {"version":3,"file":"trpc.d.ts","sourceRoot":"","sources":["../../src/api/trpc.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,WAAW,CAAA;AASjC;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAU,MAAM;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE;aAAT,OAAO;;;;;EAQ/D,CAAA;AAED,KAAK,OAAO,GAAG,OAAO,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAA;AAC5D,MAAM,MAAM,WAAW,GAAG,OAAO,CAAA;AAEjC,eAAO,MAAM,WAAW;;;CAGvB,CAAA;AAkBD;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;iBAtCyB,OAAO;;;;;;;;;;;;;;;;;;;EAsCR,CAAA;AAiBxD;;;GAGG;AACH,eAAO,MAAM,MAAM;;iBA3DsC,OAAO;;;;;;;;;;;;;;;;;;;EA2DlC,CAAA;AAE9B;;GAEG;AACH,eAAO,MAAM,eAAe;aAhE6B,OAAO;;;;;yLAgEtB,CAAA;AAE1C;;;GAGG;AACH,eAAO,MAAM,gBAAgB;aAtE4B,OAAO;;;;;;;;;yKAsEG,CAAA;AAEnE,KAAK,kBAAkB,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;AAoCzC,eAAO,MAAM,eAAe,GAAI,aAAa,MAAM,EAAE,eAAe,kBAAkB;aA5G7B,OAAO;;;;;;;;;yKA6GO,CAAA"}
package/dist/api/trpc.js CHANGED
@@ -6,6 +6,7 @@ import { db } from '../db/client.js';
6
6
  import { and, eq } from 'drizzle-orm';
7
7
  import { AdminPrivilegesTable } from '../db/schema.js';
8
8
  import getString from '../translations/index.js';
9
+ import { isDashboardOverridePlugin } from '../plugins/loader.js';
9
10
  /**
10
11
  * Creates context for an incoming request
11
12
  * @link https://trpc.io/docs/v11/context
@@ -71,6 +72,10 @@ export const publicProcedure = t.procedure;
71
72
  */
72
73
  export const privateProcedure = t.procedure.use(isAuthedMiddleware);
73
74
  const accessControlMiddleware = (sectionName, requiredRole) => isAuthedMiddleware.unstable_pipe(async ({ ctx, next }) => {
75
+ // Bypass privilege check for dashboard override plugin
76
+ if (await isDashboardOverridePlugin(sectionName)) {
77
+ return next();
78
+ }
74
79
  const privilege = await db
75
80
  .select()
76
81
  .from(AdminPrivilegesTable)
@@ -1,4 +1,5 @@
1
1
  import * as z from 'zod';
2
+ import type { LocalizedString } from '../../translations/localization.js';
2
3
  /** Locale codes for which we ship dictionaries. Config i18n.supportedLanguages keys must use these. */
3
4
  export declare const AVAILABLE_I18N_LOCALES: readonly ["en", "ar"];
4
5
  export type AvailableLocale = (typeof AVAILABLE_I18N_LOCALES)[number];
@@ -53,6 +54,10 @@ declare const cmsConfigSchema: z.ZodObject<{
53
54
  }, z.core.$strip>>;
54
55
  plugins: z.ZodOptional<z.ZodArray<z.ZodObject<{
55
56
  package: z.ZodString;
57
+ title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
58
+ routes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
59
+ title: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
60
+ }, z.core.$strip>>>;
56
61
  }, z.core.$strip>>>;
57
62
  i18n: z.ZodOptional<z.ZodObject<{
58
63
  supportedLanguages: z.ZodOptional<z.ZodRecord<z.ZodEnum<{
@@ -64,11 +69,21 @@ declare const cmsConfigSchema: z.ZodObject<{
64
69
  ar: "ar";
65
70
  }>>;
66
71
  }, z.core.$strip>>;
72
+ dashboard: z.ZodOptional<z.ZodObject<{
73
+ override: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$strip>>;
67
75
  }, z.core.$strip>;
68
76
  export type CMSConfig = z.infer<typeof cmsConfigSchema>;
77
+ export interface PluginRouteOverride {
78
+ title?: LocalizedString;
79
+ }
69
80
  export interface PluginConfigEntry {
70
81
  package: string;
71
82
  options?: Record<string, unknown>;
83
+ /** Override the plugin's default title with localized strings */
84
+ title?: LocalizedString;
85
+ /** Override individual route titles by path */
86
+ routes?: Record<string, PluginRouteOverride>;
72
87
  }
73
88
  export interface ComputedCMSConfig {
74
89
  ui: {
@@ -116,6 +131,9 @@ export interface ComputedCMSConfig {
116
131
  supportedLanguages: readonly string[];
117
132
  fallbackLanguage: string;
118
133
  };
134
+ dashboard: {
135
+ override: string | null;
136
+ };
119
137
  }
120
138
  /**
121
139
  * Gets the CMS configuration, loading it lazily on first access.
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAIxB,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,uBAAwB,CAAA;AAE3D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AAGrE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiMnB,CAAA;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEvD,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC;AAGD,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE;QACA,KAAK,EAAE,MAAM,CAAA;QACb,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,QAAQ,EAAE;YACN,oBAAoB,EAAE,OAAO,CAAA;SAChC,CAAA;KACJ,CAAA;IACD,KAAK,EAAE;QACH,MAAM,EAAE;YACJ,IAAI,EAAE,MAAM,CAAA;YACZ,iBAAiB,EAAE,OAAO,CAAA;SAC7B,CAAA;QACD,MAAM,EAAE;YACJ,SAAS,EAAE;gBACP,KAAK,EAAE,MAAM,CAAA;gBACb,MAAM,EAAE,MAAM,CAAA;gBACd,IAAI,EAAE,OAAO,CAAA;gBACb,OAAO,EAAE,MAAM,CAAA;aAClB,CAAA;YACD,SAAS,EAAE,OAAO,CAAA;SACrB,CAAA;KACJ,CAAA;IACD,gBAAgB,EAAE;QACd,OAAO,EAAE;YACL,OAAO,EAAE,OAAO,CAAA;YAChB,MAAM,EAAE,MAAM,CAAA;YACd,QAAQ,EAAE,MAAM,CAAA;YAChB,MAAM,EAAE;gBACJ,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;gBAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;aACpD,CAAA;SACJ,CAAA;KACJ,CAAA;IACD,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,IAAI,EAAE;QACF,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAA;QACrC,gBAAgB,EAAE,MAAM,CAAA;KAC3B,CAAA;CACJ;AAqKD;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAazE"}
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/core/config/config-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAGxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oCAAoC,CAAA;AAEzE,uGAAuG;AACvG,eAAO,MAAM,sBAAsB,uBAAwB,CAAA;AAE3D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,sBAAsB,CAAC,CAAC,MAAM,CAAC,CAAA;AAGrE,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA+NnB,CAAA;AAGF,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAA;AAEvD,MAAM,WAAW,mBAAmB;IAChC,KAAK,CAAC,EAAE,eAAe,CAAA;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,iEAAiE;IACjE,KAAK,CAAC,EAAE,eAAe,CAAA;IACvB,+CAA+C;IAC/C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;CAC/C;AAGD,MAAM,WAAW,iBAAiB;IAC9B,EAAE,EAAE;QACA,KAAK,EAAE,MAAM,CAAA;QACb,YAAY,EAAE,MAAM,CAAA;QACpB,IAAI,EAAE,MAAM,CAAA;QACZ,QAAQ,EAAE,MAAM,CAAA;KACnB,CAAA;IACD,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE;QACN,IAAI,EAAE,MAAM,CAAA;QACZ,MAAM,EAAE,OAAO,CAAA;QACf,QAAQ,EAAE;YACN,oBAAoB,EAAE,OAAO,CAAA;SAChC,CAAA;KACJ,CAAA;IACD,KAAK,EAAE;QACH,MAAM,EAAE;YACJ,IAAI,EAAE,MAAM,CAAA;YACZ,iBAAiB,EAAE,OAAO,CAAA;SAC7B,CAAA;QACD,MAAM,EAAE;YACJ,SAAS,EAAE;gBACP,KAAK,EAAE,MAAM,CAAA;gBACb,MAAM,EAAE,MAAM,CAAA;gBACd,IAAI,EAAE,OAAO,CAAA;gBACb,OAAO,EAAE,MAAM,CAAA;aAClB,CAAA;YACD,SAAS,EAAE,OAAO,CAAA;SACrB,CAAA;KACJ,CAAA;IACD,gBAAgB,EAAE;QACd,OAAO,EAAE;YACL,OAAO,EAAE,OAAO,CAAA;YAChB,MAAM,EAAE,MAAM,CAAA;YACd,QAAQ,EAAE,MAAM,CAAA;YAChB,MAAM,EAAE;gBACJ,MAAM,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;gBAChD,OAAO,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,CAAA;aACpD,CAAA;SACJ,CAAA;KACJ,CAAA;IACD,OAAO,EAAE,iBAAiB,EAAE,CAAA;IAC5B,IAAI,EAAE;QACF,kBAAkB,EAAE,SAAS,MAAM,EAAE,CAAA;QACrC,gBAAgB,EAAE,MAAM,CAAA;KAC3B,CAAA;IACD,SAAS,EAAE;QACP,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAC1B,CAAA;CACJ;AA2KD;;;;;;GAMG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAazE"}
@@ -163,6 +163,18 @@ const cmsConfigSchema = z.object({
163
163
  * Arbitrary options passed to the plugin factory
164
164
  */
165
165
  // options: z.record(z.unknown()).optional(),
166
+ /**
167
+ * Override the plugin's default title with localized strings
168
+ */
169
+ title: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
170
+ /**
171
+ * Override individual route titles by path
172
+ */
173
+ routes: z
174
+ .record(z.string(), z.object({
175
+ title: z.union([z.string(), z.record(z.string(), z.string())]).optional(),
176
+ }))
177
+ .optional(),
166
178
  }))
167
179
  .optional(),
168
180
  /**
@@ -189,6 +201,20 @@ const cmsConfigSchema = z.object({
189
201
  fallbackLanguage: z.enum(AVAILABLE_I18N_LOCALES).optional(),
190
202
  })
191
203
  .optional(),
204
+ /**
205
+ * Dashboard configuration
206
+ */
207
+ dashboard: z
208
+ .object({
209
+ /**
210
+ * Override the default dashboard with a plugin route.
211
+ * The plugin at this route will be rendered as the main dashboard
212
+ * and will be accessible to ALL authenticated users (no privilege check).
213
+ * @example '/cpanel-dashboard'
214
+ */
215
+ override: z.string().optional(),
216
+ })
217
+ .optional(),
192
218
  });
193
219
  function normalizeI18n(userConfig) {
194
220
  const available = new Set(AVAILABLE_I18N_LOCALES);
@@ -260,6 +286,9 @@ function mergeConfig(defaults, userConfig) {
260
286
  },
261
287
  plugins: userConfig.plugins ?? defaults.plugins,
262
288
  i18n,
289
+ dashboard: {
290
+ override: userConfig.dashboard?.override ?? defaults.dashboard.override,
291
+ },
263
292
  };
264
293
  }
265
294
  // Default configuration values
@@ -309,6 +338,9 @@ const defaultConfig = {
309
338
  supportedLanguages: ['en'],
310
339
  fallbackLanguage: 'en',
311
340
  },
341
+ dashboard: {
342
+ override: null,
343
+ },
312
344
  };
313
345
  /*const error = (error: string, greyMsg = ''): string => {
314
346
  return `${chalk.bgRed.bold(' Error ')} ${error} ${greyMsg ? chalk.grey(greyMsg) : ''}`.trim()
@@ -99,9 +99,9 @@ export class PhotoField extends FileField {
99
99
  this._thumbnailConfig = config.thumbnail;
100
100
  this.extensions = config.fileType ?? ['jpeg'];
101
101
  /**
102
- * Replace 'jpg' with 'jpeg'
102
+ * Replace 'jpg' with 'jpeg' and remove duplicates
103
103
  */
104
- this.extensions = this.extensions.map((e) => (e === 'jpg' ? 'jpeg' : e));
104
+ this.extensions = [...new Set(this.extensions.map((e) => (e === 'jpg' ? 'jpeg' : e)))];
105
105
  /**
106
106
  * Extract the mime types from the extensions
107
107
  */
@@ -293,7 +293,7 @@ export class PhotoField extends FileField {
293
293
  /**
294
294
  * Check extension
295
295
  */
296
- let ext = this._file.name.split('.').pop();
296
+ let ext = this._file.name.split('.').pop()?.toLowerCase();
297
297
  // Treat jpg as jpeg
298
298
  if (ext === 'jpg')
299
299
  ext = 'jpeg';
@@ -346,8 +346,8 @@ declare const selectFieldConfigSchema: z.ZodIntersection<z.ZodUnion<readonly [z.
346
346
  }, z.core.$strict>]>, z.ZodObject<{
347
347
  type: z.ZodLiteral<"select">;
348
348
  optionsType: z.ZodEnum<{
349
- db: "db";
350
349
  section: "section";
350
+ db: "db";
351
351
  static: "static";
352
352
  }>;
353
353
  build: z.ZodFunction<z.core.$ZodFunctionArgs, z.ZodCustom<SelectField, SelectField>>;
@@ -119,9 +119,23 @@ declare const optionsSchema: z.ZodObject<{
119
119
  allowRecursiveDelete: z.ZodOptional<z.ZodBoolean>;
120
120
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
121
121
  name: z.ZodString;
122
- order: z.ZodNumber;
123
- icon: z.ZodOptional<z.ZodString>;
122
+ gallery: z.ZodOptional<z.ZodObject<{
123
+ db: z.ZodObject<{
124
+ tableName: z.ZodString;
125
+ identifierField: z.ZodOptional<z.ZodString>;
126
+ photoNameField: z.ZodOptional<z.ZodString>;
127
+ metaField: z.ZodOptional<z.ZodString>;
128
+ }, z.core.$strict>;
129
+ watermark: z.ZodOptional<z.ZodBoolean>;
130
+ thumbnail: z.ZodOptional<z.ZodObject<{
131
+ width: z.ZodNumber;
132
+ height: z.ZodNumber;
133
+ crop: z.ZodBoolean;
134
+ quality: z.ZodNumber;
135
+ }, z.core.$strict>>;
136
+ }, z.core.$strict>>;
124
137
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
138
+ icon: z.ZodOptional<z.ZodString>;
125
139
  db: z.ZodObject<{
126
140
  table: z.ZodString;
127
141
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -145,22 +159,8 @@ declare const optionsSchema: z.ZodObject<{
145
159
  name: z.ZodOptional<z.ZodString>;
146
160
  }, z.core.$strict>>>;
147
161
  }, z.core.$strict>;
148
- gallery: z.ZodOptional<z.ZodObject<{
149
- db: z.ZodObject<{
150
- tableName: z.ZodString;
151
- identifierField: z.ZodOptional<z.ZodString>;
152
- photoNameField: z.ZodOptional<z.ZodString>;
153
- metaField: z.ZodOptional<z.ZodString>;
154
- }, z.core.$strict>;
155
- watermark: z.ZodOptional<z.ZodBoolean>;
156
- thumbnail: z.ZodOptional<z.ZodObject<{
157
- width: z.ZodNumber;
158
- height: z.ZodNumber;
159
- crop: z.ZodBoolean;
160
- quality: z.ZodNumber;
161
- }, z.core.$strict>>;
162
- }, z.core.$strict>>;
163
162
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
163
+ order: z.ZodNumber;
164
164
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
165
165
  }, z.core.$strict>;
166
166
  export declare const categorySectionConfigSchema: z.ZodObject<{
@@ -216,9 +216,23 @@ export declare const categorySectionConfigSchema: z.ZodObject<{
216
216
  */
217
217
  allowRecursiveDelete: z.ZodOptional<z.ZodBoolean>;
218
218
  name: z.ZodString;
219
- order: z.ZodNumber;
220
- icon: z.ZodOptional<z.ZodString>;
219
+ gallery: z.ZodOptional<z.ZodObject<{
220
+ db: z.ZodObject<{
221
+ tableName: z.ZodString;
222
+ identifierField: z.ZodOptional<z.ZodString>;
223
+ photoNameField: z.ZodOptional<z.ZodString>;
224
+ metaField: z.ZodOptional<z.ZodString>;
225
+ }, z.core.$strict>;
226
+ watermark: z.ZodOptional<z.ZodBoolean>;
227
+ thumbnail: z.ZodOptional<z.ZodObject<{
228
+ width: z.ZodNumber;
229
+ height: z.ZodNumber;
230
+ crop: z.ZodBoolean;
231
+ quality: z.ZodNumber;
232
+ }, z.core.$strict>>;
233
+ }, z.core.$strict>>;
221
234
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
235
+ icon: z.ZodOptional<z.ZodString>;
222
236
  db: z.ZodObject<{
223
237
  table: z.ZodString;
224
238
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -242,22 +256,8 @@ export declare const categorySectionConfigSchema: z.ZodObject<{
242
256
  name: z.ZodOptional<z.ZodString>;
243
257
  }, z.core.$strict>>>;
244
258
  }, z.core.$strict>;
245
- gallery: z.ZodOptional<z.ZodObject<{
246
- db: z.ZodObject<{
247
- tableName: z.ZodString;
248
- identifierField: z.ZodOptional<z.ZodString>;
249
- photoNameField: z.ZodOptional<z.ZodString>;
250
- metaField: z.ZodOptional<z.ZodString>;
251
- }, z.core.$strict>;
252
- watermark: z.ZodOptional<z.ZodBoolean>;
253
- thumbnail: z.ZodOptional<z.ZodObject<{
254
- width: z.ZodNumber;
255
- height: z.ZodNumber;
256
- crop: z.ZodBoolean;
257
- quality: z.ZodNumber;
258
- }, z.core.$strict>>;
259
- }, z.core.$strict>>;
260
259
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
260
+ order: z.ZodNumber;
261
261
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
262
262
  }, z.core.$strict>;
263
263
  export type CategorySectionOptions = z.infer<typeof optionsSchema>;
@@ -370,9 +370,23 @@ declare const optionsSchema: z.ZodObject<{
370
370
  generateQR: z.ZodOptional<z.ZodBoolean>;
371
371
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
372
372
  name: z.ZodString;
373
- order: z.ZodNumber;
374
- icon: z.ZodOptional<z.ZodString>;
373
+ gallery: z.ZodOptional<z.ZodObject<{
374
+ db: z.ZodObject<{
375
+ tableName: z.ZodString;
376
+ identifierField: z.ZodOptional<z.ZodString>;
377
+ photoNameField: z.ZodOptional<z.ZodString>;
378
+ metaField: z.ZodOptional<z.ZodString>;
379
+ }, z.core.$strict>;
380
+ watermark: z.ZodOptional<z.ZodBoolean>;
381
+ thumbnail: z.ZodOptional<z.ZodObject<{
382
+ width: z.ZodNumber;
383
+ height: z.ZodNumber;
384
+ crop: z.ZodBoolean;
385
+ quality: z.ZodNumber;
386
+ }, z.core.$strict>>;
387
+ }, z.core.$strict>>;
375
388
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
389
+ icon: z.ZodOptional<z.ZodString>;
376
390
  db: z.ZodObject<{
377
391
  table: z.ZodString;
378
392
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -396,22 +410,8 @@ declare const optionsSchema: z.ZodObject<{
396
410
  name: z.ZodOptional<z.ZodString>;
397
411
  }, z.core.$strict>>>;
398
412
  }, z.core.$strict>;
399
- gallery: z.ZodOptional<z.ZodObject<{
400
- db: z.ZodObject<{
401
- tableName: z.ZodString;
402
- identifierField: z.ZodOptional<z.ZodString>;
403
- photoNameField: z.ZodOptional<z.ZodString>;
404
- metaField: z.ZodOptional<z.ZodString>;
405
- }, z.core.$strict>;
406
- watermark: z.ZodOptional<z.ZodBoolean>;
407
- thumbnail: z.ZodOptional<z.ZodObject<{
408
- width: z.ZodNumber;
409
- height: z.ZodNumber;
410
- crop: z.ZodBoolean;
411
- quality: z.ZodNumber;
412
- }, z.core.$strict>>;
413
- }, z.core.$strict>>;
414
413
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
414
+ order: z.ZodNumber;
415
415
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
416
416
  }, z.core.$strict>;
417
417
  declare const hasItemsSectionConfigSchema: z.ZodObject<{
@@ -587,9 +587,23 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
587
587
  }>>;
588
588
  generateQR: z.ZodOptional<z.ZodBoolean>;
589
589
  name: z.ZodString;
590
- order: z.ZodNumber;
591
- icon: z.ZodOptional<z.ZodString>;
590
+ gallery: z.ZodOptional<z.ZodObject<{
591
+ db: z.ZodObject<{
592
+ tableName: z.ZodString;
593
+ identifierField: z.ZodOptional<z.ZodString>;
594
+ photoNameField: z.ZodOptional<z.ZodString>;
595
+ metaField: z.ZodOptional<z.ZodString>;
596
+ }, z.core.$strict>;
597
+ watermark: z.ZodOptional<z.ZodBoolean>;
598
+ thumbnail: z.ZodOptional<z.ZodObject<{
599
+ width: z.ZodNumber;
600
+ height: z.ZodNumber;
601
+ crop: z.ZodBoolean;
602
+ quality: z.ZodNumber;
603
+ }, z.core.$strict>>;
604
+ }, z.core.$strict>>;
592
605
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
606
+ icon: z.ZodOptional<z.ZodString>;
593
607
  db: z.ZodObject<{
594
608
  table: z.ZodString;
595
609
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -613,22 +627,8 @@ declare const hasItemsSectionConfigSchema: z.ZodObject<{
613
627
  name: z.ZodOptional<z.ZodString>;
614
628
  }, z.core.$strict>>>;
615
629
  }, z.core.$strict>;
616
- gallery: z.ZodOptional<z.ZodObject<{
617
- db: z.ZodObject<{
618
- tableName: z.ZodString;
619
- identifierField: z.ZodOptional<z.ZodString>;
620
- photoNameField: z.ZodOptional<z.ZodString>;
621
- metaField: z.ZodOptional<z.ZodString>;
622
- }, z.core.$strict>;
623
- watermark: z.ZodOptional<z.ZodBoolean>;
624
- thumbnail: z.ZodOptional<z.ZodObject<{
625
- width: z.ZodNumber;
626
- height: z.ZodNumber;
627
- crop: z.ZodBoolean;
628
- quality: z.ZodNumber;
629
- }, z.core.$strict>>;
630
- }, z.core.$strict>>;
631
630
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
631
+ order: z.ZodNumber;
632
632
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
633
633
  }, z.core.$strict>;
634
634
  export type HasItemsSectionOptions = z.infer<typeof optionsSchema>;
@@ -63,9 +63,23 @@ export declare const baseSectionOptionsSchema: z.ZodObject<{
63
63
  export declare const baseHelperFunctionOptionsSchema: z.ZodObject<{
64
64
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
65
65
  name: z.ZodString;
66
- order: z.ZodNumber;
67
- icon: z.ZodOptional<z.ZodString>;
66
+ gallery: z.ZodOptional<z.ZodObject<{
67
+ db: z.ZodObject<{
68
+ tableName: z.ZodString;
69
+ identifierField: z.ZodOptional<z.ZodString>;
70
+ photoNameField: z.ZodOptional<z.ZodString>;
71
+ metaField: z.ZodOptional<z.ZodString>;
72
+ }, z.core.$strict>;
73
+ watermark: z.ZodOptional<z.ZodBoolean>;
74
+ thumbnail: z.ZodOptional<z.ZodObject<{
75
+ width: z.ZodNumber;
76
+ height: z.ZodNumber;
77
+ crop: z.ZodBoolean;
78
+ quality: z.ZodNumber;
79
+ }, z.core.$strict>>;
80
+ }, z.core.$strict>>;
68
81
  readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
82
+ icon: z.ZodOptional<z.ZodString>;
69
83
  db: z.ZodObject<{
70
84
  table: z.ZodString;
71
85
  identifier: z.ZodOptional<z.ZodCustom<FieldConfig, FieldConfig>>;
@@ -89,22 +103,8 @@ export declare const baseHelperFunctionOptionsSchema: z.ZodObject<{
89
103
  name: z.ZodOptional<z.ZodString>;
90
104
  }, z.core.$strict>>>;
91
105
  }, z.core.$strict>;
92
- gallery: z.ZodOptional<z.ZodObject<{
93
- db: z.ZodObject<{
94
- tableName: z.ZodString;
95
- identifierField: z.ZodOptional<z.ZodString>;
96
- photoNameField: z.ZodOptional<z.ZodString>;
97
- metaField: z.ZodOptional<z.ZodString>;
98
- }, z.core.$strict>;
99
- watermark: z.ZodOptional<z.ZodBoolean>;
100
- thumbnail: z.ZodOptional<z.ZodObject<{
101
- width: z.ZodNumber;
102
- height: z.ZodNumber;
103
- crop: z.ZodBoolean;
104
- quality: z.ZodNumber;
105
- }, z.core.$strict>>;
106
- }, z.core.$strict>>;
107
106
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<Variant, Variant>>>;
107
+ order: z.ZodNumber;
108
108
  hooks: z.ZodOptional<z.ZodCustom<Hooks, Hooks>>;
109
109
  }, z.core.$strict>;
110
110
  export declare function validateSectionConfig(config: BaseSectionOptions): void;
@@ -24,9 +24,6 @@ declare const optionsSchema: z.ZodObject<{
24
24
  }, z.core.$strict>;
25
25
  fields: z.ZodUnion<[z.ZodArray<z.ZodCustom<FieldConfig, FieldConfig>>, z.ZodArray<z.ZodCustom<FieldGroupConfig, FieldGroupConfig>>]>;
26
26
  name: z.ZodString;
27
- order: z.ZodNumber;
28
- icon: z.ZodOptional<z.ZodString>;
29
- readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
30
27
  gallery: z.ZodOptional<z.ZodObject<{
31
28
  db: z.ZodObject<{
32
29
  tableName: z.ZodString;
@@ -42,7 +39,10 @@ declare const optionsSchema: z.ZodObject<{
42
39
  quality: z.ZodNumber;
43
40
  }, z.core.$strict>>;
44
41
  }, z.core.$strict>>;
42
+ readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
43
+ icon: z.ZodOptional<z.ZodString>;
45
44
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
45
+ order: z.ZodNumber;
46
46
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
47
47
  }, z.core.$strict>;
48
48
  declare const simpleSectionConfigSchema: z.ZodObject<{
@@ -55,9 +55,6 @@ declare const simpleSectionConfigSchema: z.ZodObject<{
55
55
  table: z.ZodString;
56
56
  }, z.core.$strict>;
57
57
  name: z.ZodString;
58
- order: z.ZodNumber;
59
- icon: z.ZodOptional<z.ZodString>;
60
- readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
61
58
  gallery: z.ZodOptional<z.ZodObject<{
62
59
  db: z.ZodObject<{
63
60
  tableName: z.ZodString;
@@ -73,7 +70,10 @@ declare const simpleSectionConfigSchema: z.ZodObject<{
73
70
  quality: z.ZodNumber;
74
71
  }, z.core.$strict>>;
75
72
  }, z.core.$strict>>;
73
+ readonly: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
74
+ icon: z.ZodOptional<z.ZodString>;
76
75
  variants: z.ZodOptional<z.ZodArray<z.ZodCustom<import("../types/index.js").Variant, import("../types/index.js").Variant>>>;
76
+ order: z.ZodNumber;
77
77
  hooks: z.ZodOptional<z.ZodCustom<import("./section.js").Hooks, import("./section.js").Hooks>>;
78
78
  }, z.core.$strict>;
79
79
  export type SimpleSectionOptions = z.infer<typeof optionsSchema>;
@@ -1,3 +1,3 @@
1
1
  export type { CMSPlugin, CMSPluginMultiRoute, CMSPluginSingleRoute, LoadedPlugin, LoadedPluginRoute, PluginInitContext, PluginModule, PluginRoute, PluginRouteWithoutComponent, PluginRouteWithComponent, } from './loader.js';
2
- export { definePlugin } from './loader.js';
2
+ export type { LocalizedString } from '../translations/localization.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,2BAA2B,EAC3B,wBAAwB,GAC3B,MAAM,aAAa,CAAA;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,SAAS,EACT,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACX,2BAA2B,EAC3B,wBAAwB,GAC3B,MAAM,aAAa,CAAA;AAGpB,YAAY,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA"}
@@ -1 +0,0 @@
1
- export { definePlugin } from './loader.js';
@@ -1,10 +1,13 @@
1
1
  import type { PluginConfigEntry } from '../core/config/config-loader.js';
2
+ import type { LocalizedString } from '../translations/localization.js';
2
3
  export interface CMSPlugin {
3
4
  /**
4
5
  * The human facing title of the plugin.
6
+ * Can be a plain string or a localized object with language codes as keys.
5
7
  * @example 'Some Plugin Title'
8
+ * @example { en: 'Some Plugin Title', ar: 'عنوان الإضافة' }
6
9
  */
7
- title: string;
10
+ title: LocalizedString;
8
11
  /**
9
12
  * The internal name of the plugin.
10
13
  * It should start with 'plugin_' and be in snake_case.
@@ -33,7 +36,11 @@ export interface PluginModule {
33
36
  }
34
37
  export interface PluginRoute {
35
38
  path: string;
36
- title: string;
39
+ /**
40
+ * The title of the route.
41
+ * Can be a plain string or a localized object with language codes as keys.
42
+ */
43
+ title: LocalizedString;
37
44
  icon?: string;
38
45
  component?: string;
39
46
  }
@@ -47,7 +54,6 @@ export interface LoadedPluginRoute extends PluginRoute {
47
54
  pluginId: string;
48
55
  pluginName: string;
49
56
  pluginRegistryName: string;
50
- pluginTitle: string;
51
57
  }
52
58
  type CMSPluginBase = Omit<CMSPlugin, 'routes'>;
53
59
  export type CMSPluginSingleRoute = CMSPluginBase & {
@@ -62,5 +68,16 @@ export declare const loadPluginModule: (pluginPackage: string) => Promise<unknow
62
68
  export declare function loadPlugins(): Promise<LoadedPlugin[]>;
63
69
  export declare function getPluginRoutes(): Promise<LoadedPluginRoute[]>;
64
70
  export declare function findPluginRouteByPath(path: string): Promise<LoadedPluginRoute | null>;
71
+ /**
72
+ * Gets the dashboard override plugin route if configured.
73
+ * @returns The LoadedPluginRoute for the dashboard override, or null if not configured
74
+ */
75
+ export declare function getDashboardOverride(): Promise<LoadedPluginRoute | null>;
76
+ /**
77
+ * Checks if a plugin is the dashboard override plugin.
78
+ * @param pluginName - The internal name of the plugin (e.g., 'plugin_cpanel_dashboard')
79
+ * @returns true if this plugin is configured as the dashboard override
80
+ */
81
+ export declare function isDashboardOverridePlugin(pluginName: string): Promise<boolean>;
65
82
  export {};
66
83
  //# sourceMappingURL=loader.d.ts.map