gavaengine 2.0.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,7 +3,7 @@ import { Editor, NodeViewProps } from '@tiptap/react';
3
3
  import * as _tiptap_core from '@tiptap/core';
4
4
  import { Node } from '@tiptap/core';
5
5
  import * as _tiptap_extension_youtube from '@tiptap/extension-youtube';
6
- import { g as GEMedia } from './types-X07o_zKf.js';
6
+ import { m as GEMedia } from './registry-Do5nzO7_.js';
7
7
  import { LucideIcon } from 'lucide-react';
8
8
 
9
9
  interface ArticleData {
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import {
3
3
  ActionsProvider,
4
+ ComponentRegistryProvider,
4
5
  ConfigProvider,
5
6
  SplashProvider
6
- } from "./chunk-MC3FBYWV.js";
7
+ } from "./chunk-PHT76VW6.js";
7
8
 
8
9
  // src/providers/GavaEngineProvider.tsx
9
10
  import { useRouter } from "next/navigation";
@@ -30,7 +31,7 @@ function GavaEngineProvider({
30
31
  children
31
32
  }) {
32
33
  const router = useRouter();
33
- return /* @__PURE__ */ jsx3(SessionProvider, { children: /* @__PURE__ */ jsx3(ThemeProvider, { children: /* @__PURE__ */ jsx3(ConfigProvider, { config, children: /* @__PURE__ */ jsx3(ActionsProvider, { actions, children: /* @__PURE__ */ jsx3(SplashProvider, { navigate: (url) => router.push(url), children }) }) }) }) });
34
+ return /* @__PURE__ */ jsx3(SessionProvider, { children: /* @__PURE__ */ jsx3(ThemeProvider, { children: /* @__PURE__ */ jsx3(ConfigProvider, { config, children: /* @__PURE__ */ jsx3(ActionsProvider, { actions, children: /* @__PURE__ */ jsx3(ComponentRegistryProvider, { overrides: config.components ?? {}, children: /* @__PURE__ */ jsx3(SplashProvider, { navigate: (url) => router.push(url), children }) }) }) }) }) });
34
35
  }
35
36
 
36
37
  // src/providers/ThemeToggle.tsx
@@ -64,4 +65,4 @@ export {
64
65
  GavaEngineProvider,
65
66
  ThemeToggle
66
67
  };
67
- //# sourceMappingURL=chunk-4LM22T36.js.map
68
+ //# sourceMappingURL=chunk-3ZYJ4C6R.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/GavaEngineProvider.tsx","../src/providers/SessionProvider.tsx","../src/providers/ThemeProvider.tsx","../src/providers/ThemeToggle.tsx"],"sourcesContent":["\"use client\";\n\nimport { type ReactNode } from \"react\";\nimport { useRouter } from \"next/navigation\";\nimport { type GavaEngineConfig } from \"../config.js\";\nimport type { GEActions } from \"../types.js\";\nimport { ConfigProvider } from \"./ConfigContext.js\";\nimport { ActionsProvider } from \"./ActionsContext.js\";\nimport { SplashProvider } from \"./SplashContext.js\";\nimport { SessionProvider } from \"./SessionProvider.js\";\nimport { ThemeProvider } from \"./ThemeProvider.js\";\nimport { ComponentRegistryProvider } from \"../components/registry.js\";\n\ninterface GavaEngineProviderProps {\n config: GavaEngineConfig;\n actions: GEActions;\n children: ReactNode;\n}\n\nexport function GavaEngineProvider({\n config,\n actions,\n children,\n}: GavaEngineProviderProps) {\n const router = useRouter();\n\n return (\n <SessionProvider>\n <ThemeProvider>\n <ConfigProvider config={config}>\n <ActionsProvider actions={actions}>\n <ComponentRegistryProvider overrides={config.components ?? {}}>\n <SplashProvider navigate={(url) => router.push(url)}>\n {children}\n </SplashProvider>\n </ComponentRegistryProvider>\n </ActionsProvider>\n </ConfigProvider>\n </ThemeProvider>\n </SessionProvider>\n );\n}\n","\"use client\";\n\nimport { SessionProvider as NextAuthSessionProvider } from \"next-auth/react\";\nimport { type ReactNode } from \"react\";\n\nexport function SessionProvider({ children }: { children: ReactNode }) {\n return <NextAuthSessionProvider>{children}</NextAuthSessionProvider>;\n}\n","\"use client\";\n\nimport { ThemeProvider as NextThemesProvider } from \"next-themes\";\nimport { type ReactNode } from \"react\";\n\nexport function ThemeProvider({ children }: { children: ReactNode }) {\n return (\n <NextThemesProvider attribute=\"class\" defaultTheme=\"system\" enableSystem>\n {children}\n </NextThemesProvider>\n );\n}\n","\"use client\";\n\nimport { useTheme } from \"next-themes\";\nimport { useEffect, useState } from \"react\";\nimport { Sun, Moon } from \"lucide-react\";\n\nexport function ThemeToggle() {\n const [mounted, setMounted] = useState(false);\n const { theme, setTheme } = useTheme();\n\n useEffect(() => {\n setMounted(true);\n }, []);\n\n if (!mounted) {\n return (\n <button className=\"p-2 rounded-lg bg-card border border-card-border\">\n <div className=\"w-5 h-5\" />\n </button>\n );\n }\n\n return (\n <button\n onClick={() => setTheme(theme === \"dark\" ? \"light\" : \"dark\")}\n className=\"p-2 rounded-lg bg-card border border-card-border hover:bg-card-border transition-colors\"\n aria-label=\"Toggle theme\"\n >\n {theme === \"dark\" ? (\n <Sun className=\"w-5 h-5 text-foreground\" />\n ) : (\n <Moon className=\"w-5 h-5 text-foreground\" />\n )}\n </button>\n );\n}\n"],"mappings":";;;;;;;;;AAGA,SAAS,iBAAiB;;;ACD1B,SAAS,mBAAmB,+BAA+B;AAIlD;AADF,SAAS,gBAAgB,EAAE,SAAS,GAA4B;AACrE,SAAO,oBAAC,2BAAyB,UAAS;AAC5C;;;ACLA,SAAS,iBAAiB,0BAA0B;AAKhD,gBAAAA,YAAA;AAFG,SAAS,cAAc,EAAE,SAAS,GAA4B;AACnE,SACE,gBAAAA,KAAC,sBAAmB,WAAU,SAAQ,cAAa,UAAS,cAAY,MACrE,UACH;AAEJ;;;AFqBc,gBAAAC,YAAA;AAbP,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AACF,GAA4B;AAC1B,QAAM,SAAS,UAAU;AAEzB,SACE,gBAAAA,KAAC,mBACC,0BAAAA,KAAC,iBACC,0BAAAA,KAAC,kBAAe,QACd,0BAAAA,KAAC,mBAAgB,SACf,0BAAAA,KAAC,6BAA0B,WAAW,OAAO,cAAc,CAAC,GAC1D,0BAAAA,KAAC,kBAAe,UAAU,CAAC,QAAQ,OAAO,KAAK,GAAG,GAC/C,UACH,GACF,GACF,GACF,GACF,GACF;AAEJ;;;AGvCA,SAAS,gBAAgB;AACzB,SAAS,WAAW,gBAAgB;AACpC,SAAS,KAAK,YAAY;AAalB,gBAAAC,YAAA;AAXD,SAAS,cAAc;AAC5B,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,EAAE,OAAO,SAAS,IAAI,SAAS;AAErC,YAAU,MAAM;AACd,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAEL,MAAI,CAAC,SAAS;AACZ,WACE,gBAAAA,KAAC,YAAO,WAAU,oDAChB,0BAAAA,KAAC,SAAI,WAAU,WAAU,GAC3B;AAAA,EAEJ;AAEA,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,SAAS,MAAM,SAAS,UAAU,SAAS,UAAU,MAAM;AAAA,MAC3D,WAAU;AAAA,MACV,cAAW;AAAA,MAEV,oBAAU,SACT,gBAAAA,KAAC,OAAI,WAAU,2BAA0B,IAEzC,gBAAAA,KAAC,QAAK,WAAU,2BAA0B;AAAA;AAAA,EAE9C;AAEJ;","names":["jsx","jsx","jsx"]}
@@ -514,6 +514,27 @@ function SplashProvider({
514
514
  return /* @__PURE__ */ jsx3(SplashContext.Provider, { value: { phase, navigateWithSplash, openSplash }, children });
515
515
  }
516
516
 
517
+ // src/components/registry.tsx
518
+ import {
519
+ createContext as createContext4,
520
+ useContext as useContext4
521
+ } from "react";
522
+ import { jsx as jsx4 } from "react/jsx-runtime";
523
+ var RegistryContext = createContext4({});
524
+ function ComponentRegistryProvider({
525
+ overrides,
526
+ children
527
+ }) {
528
+ return /* @__PURE__ */ jsx4(RegistryContext.Provider, { value: overrides, children });
529
+ }
530
+ function useRegisteredComponent(name, fallback) {
531
+ const registry = useContext4(RegistryContext);
532
+ return registry[name] ?? fallback;
533
+ }
534
+ function useComponentRegistry() {
535
+ return useContext4(RegistryContext);
536
+ }
537
+
517
538
  export {
518
539
  it,
519
540
  en,
@@ -529,6 +550,9 @@ export {
529
550
  useGavaActions,
530
551
  ActionsProvider,
531
552
  useSplash,
532
- SplashProvider
553
+ SplashProvider,
554
+ ComponentRegistryProvider,
555
+ useRegisteredComponent,
556
+ useComponentRegistry
533
557
  };
534
- //# sourceMappingURL=chunk-MC3FBYWV.js.map
558
+ //# sourceMappingURL=chunk-PHT76VW6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/i18n/it.ts","../src/i18n/en.ts","../src/i18n/index.ts","../src/config.ts","../src/providers/ConfigContext.tsx","../src/providers/ActionsContext.tsx","../src/providers/SplashContext.tsx","../src/components/registry.tsx"],"sourcesContent":["import type { GELocale } from \"./types.js\";\n\nexport const it: GELocale = {\n content: {\n articles: \"Articoli\",\n newArticle: \"Nuovo articolo\",\n titlePlaceholder: \"Titolo dell'articolo\",\n excerptPlaceholder: \"Breve descrizione dell'articolo...\",\n slugPlaceholder: \"slug-articolo\",\n authorPlaceholder: \"Nome dell'autore\",\n category: \"Categoria\",\n selectCategory: \"Seleziona categoria\",\n excerpt: \"Estratto\",\n author: \"Autore\",\n slugUrl: \"Slug URL\",\n noArticles: \"Nessun articolo.\",\n noArticlesSearch: \"Nessun articolo trovato.\",\n searchArticles: \"Cerca articoli...\",\n coverImageLabel: \"Immagine di copertina\",\n coverImageHint: \"Scegli dalla libreria o carica un nuovo file\",\n viewArticle: \"Vedi articolo pubblicato\",\n untitled: \"Senza titolo\",\n },\n editor: {\n publish: \"Pubblica\",\n unpublish: \"Ritira\",\n saveDraft: \"Salva bozza\",\n revisions: \"Cronologia\",\n saving: \"Salvando...\",\n saved: \"Salvato\",\n saveError: \"Errore nel salvataggio\",\n noRevisions: \"Nessuna revisione salvata.\",\n restore: \"Ripristina\",\n restoreConfirm:\n \"Ripristinare questa revisione? Lo stato attuale verrà salvato prima del ripristino.\",\n beforeRestore: \"Prima del ripristino\",\n publishedNote: \"Pubblicato\",\n preview: \"Anteprima\",\n placeholder: \"Inizia a scrivere il tuo articolo...\",\n },\n media: {\n media: \"Media\",\n noMedia: \"Nessun file caricato.\",\n noMediaSearch: \"Nessun file trovato.\",\n chooseFile: \"Scegli file\",\n dragHint: \"Trascina un'immagine qui oppure\",\n uploadHint: \"JPEG, PNG, WebP, GIF (max 5MB)\",\n mediaLibrary: \"Libreria media\",\n uploadNew: \"Carica nuovo\",\n searchFiles: \"Cerca file...\",\n selectImage: \"Seleziona immagine\",\n noImages: \"Nessuna immagine nella libreria.\",\n noImagesSearch: \"Nessuna immagine trovata.\",\n copyUrl: \"Copia URL\",\n editImage: \"Modifica immagine\",\n restoreOriginal: \"Ripristina originale\",\n freeAspect: \"Libero\",\n },\n users: {\n users: \"Utenti\",\n name: \"Nome\",\n email: \"Email\",\n password: \"Password\",\n passwordEditHint: \" (lascia vuoto per non modificarla)\",\n role: \"Ruolo\",\n createdAt: \"Creato il\",\n actions: \"Azioni\",\n createUser: \"Crea utente\",\n unauthorized: \"Non autorizzato\",\n notAuthenticated: \"Non autenticato\",\n allFieldsRequired: \"Tutti i campi sono obbligatori.\",\n passwordMinLength: \"La password deve avere almeno 6 caratteri.\",\n emailExists: \"Esiste già un utente con questa email.\",\n invalidRole: \"Ruolo non valido.\",\n cannotDeleteSelf: \"Non puoi eliminare il tuo stesso account.\",\n },\n common: {\n statistics: \"Statistiche\",\n settings: \"Impostazioni\",\n delete: \"Elimina\",\n edit: \"Modifica\",\n search: \"Cerca\",\n draft: \"Bozza\",\n published: \"Pubblicato\",\n all: \"Tutti\",\n drafts: \"Bozze\",\n loading: \"Caricamento...\",\n confirm: \"Conferma\",\n cancel: \"Annulla\",\n apply: \"Applica\",\n change: \"Cambia\",\n logout: \"Esci\",\n saveChanges: \"Salva modifiche\",\n updated: \"Aggiornato\",\n status: \"Stato\",\n title: \"Titolo\",\n deleteConfirm: (title) => `Eliminare \"${title || \"Senza titolo\"}\"?`,\n unpublishConfirm: (title) =>\n `Ritirare \"${title || \"Senza titolo\"}\"? L'articolo tornerà in bozza.`,\n deleteUserConfirm: (name) =>\n `Sei sicuro di voler eliminare l'utente \"${name}\"?`,\n totalArticles: (count) =>\n `${count} articol${count === 1 ? \"o\" : \"i\"} totali`,\n totalFiles: (count) => `${count} file caricati`,\n },\n};\n","import type { GELocale } from \"./types.js\";\n\nexport const en: GELocale = {\n content: {\n articles: \"Articles\",\n newArticle: \"New article\",\n titlePlaceholder: \"Article title\",\n excerptPlaceholder: \"Brief description of the article...\",\n slugPlaceholder: \"article-slug\",\n authorPlaceholder: \"Author name\",\n category: \"Category\",\n selectCategory: \"Select category\",\n excerpt: \"Excerpt\",\n author: \"Author\",\n slugUrl: \"URL Slug\",\n noArticles: \"No articles.\",\n noArticlesSearch: \"No articles found.\",\n searchArticles: \"Search articles...\",\n coverImageLabel: \"Cover image\",\n coverImageHint: \"Choose from library or upload a new file\",\n viewArticle: \"View published article\",\n untitled: \"Untitled\",\n },\n editor: {\n publish: \"Publish\",\n unpublish: \"Unpublish\",\n saveDraft: \"Save draft\",\n revisions: \"History\",\n saving: \"Saving...\",\n saved: \"Saved\",\n saveError: \"Error saving\",\n noRevisions: \"No revisions saved.\",\n restore: \"Restore\",\n restoreConfirm:\n \"Restore this revision? The current state will be saved before restoring.\",\n beforeRestore: \"Before restore\",\n publishedNote: \"Published\",\n preview: \"Preview\",\n placeholder: \"Start writing your article...\",\n },\n media: {\n media: \"Media\",\n noMedia: \"No files uploaded.\",\n noMediaSearch: \"No files found.\",\n chooseFile: \"Choose file\",\n dragHint: \"Drag an image here or\",\n uploadHint: \"JPEG, PNG, WebP, GIF (max 5MB)\",\n mediaLibrary: \"Media library\",\n uploadNew: \"Upload new\",\n searchFiles: \"Search files...\",\n selectImage: \"Select image\",\n noImages: \"No images in library.\",\n noImagesSearch: \"No images found.\",\n copyUrl: \"Copy URL\",\n editImage: \"Edit image\",\n restoreOriginal: \"Restore original\",\n freeAspect: \"Free\",\n },\n users: {\n users: \"Users\",\n name: \"Name\",\n email: \"Email\",\n password: \"Password\",\n passwordEditHint: \" (leave blank to keep current)\",\n role: \"Role\",\n createdAt: \"Created at\",\n actions: \"Actions\",\n createUser: \"Create user\",\n unauthorized: \"Unauthorized\",\n notAuthenticated: \"Not authenticated\",\n allFieldsRequired: \"All fields are required.\",\n passwordMinLength: \"Password must be at least 6 characters.\",\n emailExists: \"A user with this email already exists.\",\n invalidRole: \"Invalid role.\",\n cannotDeleteSelf: \"You cannot delete your own account.\",\n },\n common: {\n statistics: \"Statistics\",\n settings: \"Settings\",\n delete: \"Delete\",\n edit: \"Edit\",\n search: \"Search\",\n draft: \"Draft\",\n published: \"Published\",\n all: \"All\",\n drafts: \"Drafts\",\n loading: \"Loading...\",\n confirm: \"Confirm\",\n cancel: \"Cancel\",\n apply: \"Apply\",\n change: \"Change\",\n logout: \"Logout\",\n saveChanges: \"Save changes\",\n updated: \"Updated\",\n status: \"Status\",\n title: \"Title\",\n deleteConfirm: (title) => `Delete \"${title || \"Untitled\"}\"?`,\n unpublishConfirm: (title) =>\n `Unpublish \"${title || \"Untitled\"}\"? The article will go back to draft.`,\n deleteUserConfirm: (name) =>\n `Are you sure you want to delete user \"${name}\"?`,\n totalArticles: (count) =>\n `${count} total article${count === 1 ? \"\" : \"s\"}`,\n totalFiles: (count) => `${count} uploaded file${count === 1 ? \"\" : \"s\"}`,\n },\n};\n","import type { GELocale } from \"./types.js\";\nimport { it } from \"./it.js\";\nimport { en } from \"./en.js\";\n\nexport type { GELocale } from \"./types.js\";\nexport { it } from \"./it.js\";\nexport { en } from \"./en.js\";\n\nconst builtinLocales: Record<string, GELocale> = { it, en };\n\nexport function loadLocale(locale: string): GELocale {\n const found = builtinLocales[locale];\n if (!found) {\n throw new Error(\n `Locale \"${locale}\" not found. Available: ${Object.keys(builtinLocales).join(\", \")}`\n );\n }\n return found;\n}\n\nexport function defineLocale(locale: GELocale): GELocale {\n return locale;\n}\n\nexport function mergeLocales(\n base: GELocale,\n overrides: DeepPartial<GELocale>\n): GELocale {\n return deepMergeLocale(base, overrides);\n}\n\nexport function registerLocale(key: string, locale: GELocale): void {\n builtinLocales[key] = locale;\n}\n\ntype DeepPartial<T> = {\n [P in keyof T]?: T[P] extends (...args: any[]) => any\n ? T[P]\n : T[P] extends object\n ? DeepPartial<T[P]>\n : T[P];\n};\n\nfunction deepMergeLocale<T extends Record<string, any>>(\n target: T,\n source: DeepPartial<T>\n): T {\n const result = { ...target };\n for (const key of Object.keys(source) as (keyof T)[]) {\n const sourceVal = source[key];\n const targetVal = target[key];\n if (\n sourceVal &&\n typeof sourceVal === \"object\" &&\n !Array.isArray(sourceVal) &&\n typeof targetVal === \"object\" &&\n !Array.isArray(targetVal) &&\n typeof sourceVal !== \"function\"\n ) {\n (result as any)[key] = deepMergeLocale(\n targetVal as Record<string, any>,\n sourceVal as Record<string, any>\n );\n } else if (sourceVal !== undefined) {\n (result as any)[key] = sourceVal;\n }\n }\n return result;\n}\n","import { loadLocale } from \"./i18n/index.js\";\nimport type { GELocale } from \"./i18n/types.js\";\nimport type { GEAuthAdapter } from \"./auth/types.js\";\nimport type { GEContentType } from \"./content/types.js\";\nimport type { GEComponentOverrides } from \"./components/registry.js\";\n\nexport interface GavaEngineConfig {\n branding: {\n name: string;\n logo?: string;\n };\n roles: {\n list: string[];\n labels: Record<string, string>;\n canEdit: (role: string) => boolean;\n canPublish: (role: string) => boolean;\n adminRole: string;\n };\n statuses: {\n draft: string;\n published: string;\n };\n locale: string;\n models: {\n article: string;\n articleRevision: string;\n articleView: string;\n media: string;\n user: string;\n };\n auth?: GEAuthAdapter;\n contentTypes?: GEContentType[];\n components?: GEComponentOverrides;\n categories: string[];\n upload: {\n endpoint: string;\n imageTypes: string[];\n videoTypes: string[];\n maxImageSize: number;\n maxVideoSize: number;\n };\n editor: {\n placeholder: string;\n autoSaveDelay: number;\n revisionThrottleMinutes: number;\n headingLevels: number[];\n };\n routes: {\n articles: string;\n articleEdit: (id: string) => string;\n articleNew: string;\n articleView: (slug: string) => string;\n articlePreview: (id: string) => string;\n users: string;\n userEdit: (id: string) => string;\n userNew: string;\n media: string;\n dashboard: string;\n login: string;\n home: string;\n };\n strings: {\n articles: string;\n newArticle: string;\n media: string;\n users: string;\n statistics: string;\n settings: string;\n revisions: string;\n publish: string;\n unpublish: string;\n saveDraft: string;\n delete: string;\n edit: string;\n search: string;\n titlePlaceholder: string;\n excerptPlaceholder: string;\n slugPlaceholder: string;\n authorPlaceholder: string;\n category: string;\n selectCategory: string;\n excerpt: string;\n author: string;\n slugUrl: string;\n draft: string;\n published: string;\n all: string;\n drafts: string;\n saving: string;\n saved: string;\n saveError: string;\n noArticles: string;\n noArticlesSearch: string;\n noMedia: string;\n noMediaSearch: string;\n noRevisions: string;\n loading: string;\n confirm: string;\n cancel: string;\n apply: string;\n change: string;\n logout: string;\n viewArticle: string;\n preview: string;\n restore: string;\n restoreConfirm: string;\n beforeRestore: string;\n publishedNote: string;\n deleteConfirm: (title: string) => string;\n unpublishConfirm: (title: string) => string;\n deleteUserConfirm: (name: string) => string;\n totalArticles: (count: number) => string;\n totalFiles: (count: number) => string;\n coverImageLabel: string;\n coverImageHint: string;\n chooseFile: string;\n dragHint: string;\n uploadHint: string;\n mediaLibrary: string;\n uploadNew: string;\n searchFiles: string;\n searchArticles: string;\n selectImage: string;\n noImages: string;\n noImagesSearch: string;\n copyUrl: string;\n editImage: string;\n restoreOriginal: string;\n freeAspect: string;\n unauthorized: string;\n notAuthenticated: string;\n allFieldsRequired: string;\n passwordMinLength: string;\n emailExists: string;\n invalidRole: string;\n cannotDeleteSelf: string;\n name: string;\n email: string;\n password: string;\n passwordEditHint: string;\n role: string;\n createdAt: string;\n actions: string;\n createUser: string;\n saveChanges: string;\n updated: string;\n status: string;\n title: string;\n untitled: string;\n };\n}\n\nfunction localeToStrings(locale: GELocale): GavaEngineConfig[\"strings\"] {\n return {\n // content\n articles: locale.content.articles,\n newArticle: locale.content.newArticle,\n titlePlaceholder: locale.content.titlePlaceholder,\n excerptPlaceholder: locale.content.excerptPlaceholder,\n slugPlaceholder: locale.content.slugPlaceholder,\n authorPlaceholder: locale.content.authorPlaceholder,\n category: locale.content.category,\n selectCategory: locale.content.selectCategory,\n excerpt: locale.content.excerpt,\n author: locale.content.author,\n slugUrl: locale.content.slugUrl,\n noArticles: locale.content.noArticles,\n noArticlesSearch: locale.content.noArticlesSearch,\n searchArticles: locale.content.searchArticles,\n coverImageLabel: locale.content.coverImageLabel,\n coverImageHint: locale.content.coverImageHint,\n viewArticle: locale.content.viewArticle,\n untitled: locale.content.untitled,\n // editor\n publish: locale.editor.publish,\n unpublish: locale.editor.unpublish,\n saveDraft: locale.editor.saveDraft,\n revisions: locale.editor.revisions,\n saving: locale.editor.saving,\n saved: locale.editor.saved,\n saveError: locale.editor.saveError,\n noRevisions: locale.editor.noRevisions,\n restore: locale.editor.restore,\n restoreConfirm: locale.editor.restoreConfirm,\n beforeRestore: locale.editor.beforeRestore,\n publishedNote: locale.editor.publishedNote,\n preview: locale.editor.preview,\n // media\n media: locale.media.media,\n noMedia: locale.media.noMedia,\n noMediaSearch: locale.media.noMediaSearch,\n chooseFile: locale.media.chooseFile,\n dragHint: locale.media.dragHint,\n uploadHint: locale.media.uploadHint,\n mediaLibrary: locale.media.mediaLibrary,\n uploadNew: locale.media.uploadNew,\n searchFiles: locale.media.searchFiles,\n selectImage: locale.media.selectImage,\n noImages: locale.media.noImages,\n noImagesSearch: locale.media.noImagesSearch,\n copyUrl: locale.media.copyUrl,\n editImage: locale.media.editImage,\n restoreOriginal: locale.media.restoreOriginal,\n freeAspect: locale.media.freeAspect,\n // users\n users: locale.users.users,\n name: locale.users.name,\n email: locale.users.email,\n password: locale.users.password,\n passwordEditHint: locale.users.passwordEditHint,\n role: locale.users.role,\n createdAt: locale.users.createdAt,\n actions: locale.users.actions,\n createUser: locale.users.createUser,\n unauthorized: locale.users.unauthorized,\n notAuthenticated: locale.users.notAuthenticated,\n allFieldsRequired: locale.users.allFieldsRequired,\n passwordMinLength: locale.users.passwordMinLength,\n emailExists: locale.users.emailExists,\n invalidRole: locale.users.invalidRole,\n cannotDeleteSelf: locale.users.cannotDeleteSelf,\n // common\n statistics: locale.common.statistics,\n settings: locale.common.settings,\n delete: locale.common.delete,\n edit: locale.common.edit,\n search: locale.common.search,\n draft: locale.common.draft,\n published: locale.common.published,\n all: locale.common.all,\n drafts: locale.common.drafts,\n loading: locale.common.loading,\n confirm: locale.common.confirm,\n cancel: locale.common.cancel,\n apply: locale.common.apply,\n change: locale.common.change,\n logout: locale.common.logout,\n saveChanges: locale.common.saveChanges,\n updated: locale.common.updated,\n status: locale.common.status,\n title: locale.common.title,\n deleteConfirm: locale.common.deleteConfirm,\n unpublishConfirm: locale.common.unpublishConfirm,\n deleteUserConfirm: locale.common.deleteUserConfirm,\n totalArticles: locale.common.totalArticles,\n totalFiles: locale.common.totalFiles,\n };\n}\n\nconst DEFAULT_STRINGS = localeToStrings(loadLocale(\"it\"));\n\nconst DEFAULT_CONFIG: GavaEngineConfig = {\n branding: {\n name: \"GavaEngine\",\n },\n roles: {\n list: [\"admin\", \"redattore\", \"scrittore\", \"lettore\"],\n labels: {\n admin: \"Amministratore\",\n redattore: \"Redattore\",\n scrittore: \"Scrittore\",\n lettore: \"Lettore\",\n },\n canEdit: (role: string) => role !== \"lettore\",\n canPublish: (role: string) => role === \"admin\" || role === \"redattore\",\n adminRole: \"admin\",\n },\n statuses: {\n draft: \"bozza\",\n published: \"pubblicato\",\n },\n locale: \"it\",\n models: {\n article: \"article\",\n articleRevision: \"articleRevision\",\n articleView: \"articleView\",\n media: \"media\",\n user: \"user\",\n },\n categories: [\n \"Cultura ed Intercultura\",\n \"Fatti ed Eventi\",\n \"Poeti e Prosatori\",\n \"Famiglia, Istituzioni e Territorio\",\n \"Amici del Meucci\",\n ],\n upload: {\n endpoint: \"/api/upload\",\n imageTypes: [\"image/jpeg\", \"image/png\", \"image/webp\", \"image/gif\"],\n videoTypes: [\"video/mp4\", \"video/webm\", \"video/quicktime\"],\n maxImageSize: 5 * 1024 * 1024,\n maxVideoSize: 50 * 1024 * 1024,\n },\n editor: {\n placeholder: \"Inizia a scrivere il tuo articolo...\",\n autoSaveDelay: 2000,\n revisionThrottleMinutes: 5,\n headingLevels: [2, 3],\n },\n routes: {\n articles: \"/dashboard/articoli\",\n articleEdit: (id) => `/dashboard/articoli/${id}`,\n articleNew: \"/dashboard/articoli/nuovo\",\n articleView: (slug) => `/articoli/${slug}`,\n articlePreview: (id) => `/anteprima/${id}`,\n users: \"/dashboard/utenti\",\n userEdit: (id) => `/dashboard/utenti/${id}`,\n userNew: \"/dashboard/utenti/nuovo\",\n media: \"/dashboard/media\",\n dashboard: \"/dashboard\",\n login: \"/login\",\n home: \"/\",\n },\n strings: DEFAULT_STRINGS,\n};\n\nfunction deepMerge<T extends Record<string, any>>(\n target: T,\n source: Partial<T>\n): T {\n const result = { ...target };\n for (const key of Object.keys(source) as (keyof T)[]) {\n const sourceVal = source[key];\n const targetVal = target[key];\n if (\n sourceVal &&\n typeof sourceVal === \"object\" &&\n !Array.isArray(sourceVal) &&\n typeof targetVal === \"object\" &&\n !Array.isArray(targetVal) &&\n typeof sourceVal !== \"function\"\n ) {\n (result as any)[key] = deepMerge(\n targetVal as Record<string, any>,\n sourceVal as Record<string, any>\n );\n } else if (sourceVal !== undefined) {\n (result as any)[key] = sourceVal;\n }\n }\n return result;\n}\n\nexport function defineConfig(\n overrides: Partial<GavaEngineConfig> = {}\n): GavaEngineConfig {\n // If a different locale is specified, load it as base strings\n const locale = overrides.locale ?? DEFAULT_CONFIG.locale;\n const baseStrings = localeToStrings(loadLocale(locale));\n\n // Build config with locale-based strings as default\n const configWithLocale = {\n ...DEFAULT_CONFIG,\n strings: baseStrings,\n };\n\n // Also update editor.placeholder from locale if not overridden\n if (!overrides.editor?.placeholder) {\n const localeData = loadLocale(locale);\n configWithLocale.editor = {\n ...configWithLocale.editor,\n placeholder: localeData.editor.placeholder,\n };\n }\n\n return deepMerge(configWithLocale, overrides);\n}\n\nexport { DEFAULT_CONFIG, localeToStrings };\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport { type GavaEngineConfig, DEFAULT_CONFIG } from \"../config.js\";\n\nconst ConfigContext = createContext<GavaEngineConfig>(DEFAULT_CONFIG);\n\nexport function useGavaConfig() {\n return useContext(ConfigContext);\n}\n\nexport function ConfigProvider({\n config,\n children,\n}: {\n config: GavaEngineConfig;\n children: ReactNode;\n}) {\n return (\n <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>\n );\n}\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type { GEActions } from \"../types.js\";\n\nconst ActionsContext = createContext<GEActions | null>(null);\n\nexport function useGavaActions(): GEActions {\n const ctx = useContext(ActionsContext);\n if (!ctx) {\n throw new Error(\n \"useGavaActions must be used within a <GavaEngineProvider> with actions provided.\"\n );\n }\n return ctx;\n}\n\nexport function ActionsProvider({\n actions,\n children,\n}: {\n actions: GEActions;\n children: ReactNode;\n}) {\n return (\n <ActionsContext.Provider value={actions}>{children}</ActionsContext.Provider>\n );\n}\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n useState,\n useCallback,\n type ReactNode,\n} from \"react\";\n\ntype Phase = \"idle\" | \"closing\" | \"closed\" | \"opening\";\n\ninterface SplashContextValue {\n phase: Phase;\n navigateWithSplash: (url: string) => void;\n openSplash: () => void;\n}\n\nconst SplashContext = createContext<SplashContextValue>({\n phase: \"idle\",\n navigateWithSplash: () => {},\n openSplash: () => {},\n});\n\nexport function useSplash() {\n return useContext(SplashContext);\n}\n\nexport function SplashProvider({\n children,\n navigate,\n}: {\n children: ReactNode;\n navigate: (url: string) => void;\n}) {\n const [phase, setPhase] = useState<Phase>(\"idle\");\n\n const navigateWithSplash = useCallback(\n (url: string) => {\n if (phase !== \"idle\") return;\n setPhase(\"closing\");\n\n setTimeout(() => {\n setPhase(\"closed\");\n navigate(url);\n }, 700);\n },\n [phase, navigate]\n );\n\n const openSplash = useCallback(() => {\n if (phase !== \"closed\") return;\n requestAnimationFrame(() => {\n setPhase(\"opening\");\n setTimeout(() => setPhase(\"idle\"), 900);\n });\n }, [phase]);\n\n return (\n <SplashContext.Provider value={{ phase, navigateWithSplash, openSplash }}>\n {children}\n </SplashContext.Provider>\n );\n}\n","\"use client\";\n\nimport {\n createContext,\n useContext,\n type ComponentType,\n type ReactNode,\n} from \"react\";\nimport type { ContentEditorProps } from \"./content/ContentEditor.js\";\nimport type { ContentListProps } from \"./content/ContentList.js\";\n\nexport interface GEComponentOverrides {\n DashboardNavbar?: ComponentType<any>;\n DashboardLayout?: ComponentType<{ children: ReactNode }>;\n EditorToolbar?: ComponentType<{ editor: any }>;\n ContentEditor?: ComponentType<ContentEditorProps>;\n ContentList?: ComponentType<ContentListProps>;\n MediaGrid?: ComponentType<any>;\n UserTable?: ComponentType<any>;\n LoginPage?: ComponentType<any>;\n StatCard?: ComponentType<any>;\n}\n\nconst RegistryContext = createContext<GEComponentOverrides>({});\n\nexport function ComponentRegistryProvider({\n overrides,\n children,\n}: {\n overrides: GEComponentOverrides;\n children: ReactNode;\n}) {\n return (\n <RegistryContext.Provider value={overrides}>\n {children}\n </RegistryContext.Provider>\n );\n}\n\nexport function useRegisteredComponent<K extends keyof GEComponentOverrides>(\n name: K,\n fallback: NonNullable<GEComponentOverrides[K]>\n): NonNullable<GEComponentOverrides[K]> {\n const registry = useContext(RegistryContext);\n return (registry[name] as NonNullable<GEComponentOverrides[K]>) ?? fallback;\n}\n\nexport function useComponentRegistry(): GEComponentOverrides {\n return useContext(RegistryContext);\n}\n"],"mappings":";;;AAEO,IAAM,KAAe;AAAA,EAC1B,SAAS;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,aAAa;AAAA,IACb,SAAS;AAAA,IACT,gBACE;AAAA,IACF,eAAe;AAAA,IACf,eAAe;AAAA,IACf,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,IACb,aAAa;AAAA,IACb,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,YAAY;AAAA,EACd;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,aAAa;AAAA,IACb,aAAa;AAAA,IACb,kBAAkB;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,eAAe,CAAC,UAAU,cAAc,SAAS,cAAc;AAAA,IAC/D,kBAAkB,CAAC,UACjB,aAAa,SAAS,cAAc;AAAA,IACtC,mBAAmB,CAAC,SAClB,2CAA2C,IAAI;AAAA,IACjD,eAAe,CAAC,UACd,GAAG,KAAK,WAAW,UAAU,IAAI,MAAM,GAAG;AAAA,IAC5C,YAAY,CAAC,UAAU,GAAG,KAAK;AAAA,EACjC;AACF;;;ACvGO,IAAM,KAAe;AAAA,EAC1B,SAAS;AAAA,IACP,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,oBAAoB;AAAA,IACpB,iBAAiB;AAAA,IACjB,mBAAmB;AAAA,IACnB,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,iBAAiB;AAAA,IACjB,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,UAAU;AAAA,EACZ;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,aAAa;AAAA,IACb,SAAS;AAAA,IACT,gBACE;AAAA,IACF,eAAe;AAAA,IACf,eAAe;AAAA,IACf,SAAS;AAAA,IACT,aAAa;AAAA,EACf;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,WAAW;AAAA,IACX,aAAa;AAAA,IACb,aAAa;AAAA,IACb,UAAU;AAAA,IACV,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,iBAAiB;AAAA,IACjB,YAAY;AAAA,EACd;AAAA,EACA,OAAO;AAAA,IACL,OAAO;AAAA,IACP,MAAM;AAAA,IACN,OAAO;AAAA,IACP,UAAU;AAAA,IACV,kBAAkB;AAAA,IAClB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,aAAa;AAAA,IACb,aAAa;AAAA,IACb,kBAAkB;AAAA,EACpB;AAAA,EACA,QAAQ;AAAA,IACN,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,IACX,KAAK;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,QAAQ;AAAA,IACR,aAAa;AAAA,IACb,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,eAAe,CAAC,UAAU,WAAW,SAAS,UAAU;AAAA,IACxD,kBAAkB,CAAC,UACjB,cAAc,SAAS,UAAU;AAAA,IACnC,mBAAmB,CAAC,SAClB,yCAAyC,IAAI;AAAA,IAC/C,eAAe,CAAC,UACd,GAAG,KAAK,iBAAiB,UAAU,IAAI,KAAK,GAAG;AAAA,IACjD,YAAY,CAAC,UAAU,GAAG,KAAK,iBAAiB,UAAU,IAAI,KAAK,GAAG;AAAA,EACxE;AACF;;;ACjGA,IAAM,iBAA2C,EAAE,IAAI,GAAG;AAEnD,SAAS,WAAW,QAA0B;AACnD,QAAM,QAAQ,eAAe,MAAM;AACnC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,WAAW,MAAM,2BAA2B,OAAO,KAAK,cAAc,EAAE,KAAK,IAAI,CAAC;AAAA,IACpF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aAAa,QAA4B;AACvD,SAAO;AACT;AAEO,SAAS,aACd,MACA,WACU;AACV,SAAO,gBAAgB,MAAM,SAAS;AACxC;AAEO,SAAS,eAAe,KAAa,QAAwB;AAClE,iBAAe,GAAG,IAAI;AACxB;AAUA,SAAS,gBACP,QACA,QACG;AACH,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAkB;AACpD,UAAM,YAAY,OAAO,GAAG;AAC5B,UAAM,YAAY,OAAO,GAAG;AAC5B,QACE,aACA,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,OAAO,cAAc,YACrB;AACA,MAAC,OAAe,GAAG,IAAI;AAAA,QACrB;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,cAAc,QAAW;AAClC,MAAC,OAAe,GAAG,IAAI;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;;;ACoFA,SAAS,gBAAgB,QAA+C;AACtE,SAAO;AAAA;AAAA,IAEL,UAAU,OAAO,QAAQ;AAAA,IACzB,YAAY,OAAO,QAAQ;AAAA,IAC3B,kBAAkB,OAAO,QAAQ;AAAA,IACjC,oBAAoB,OAAO,QAAQ;AAAA,IACnC,iBAAiB,OAAO,QAAQ;AAAA,IAChC,mBAAmB,OAAO,QAAQ;AAAA,IAClC,UAAU,OAAO,QAAQ;AAAA,IACzB,gBAAgB,OAAO,QAAQ;AAAA,IAC/B,SAAS,OAAO,QAAQ;AAAA,IACxB,QAAQ,OAAO,QAAQ;AAAA,IACvB,SAAS,OAAO,QAAQ;AAAA,IACxB,YAAY,OAAO,QAAQ;AAAA,IAC3B,kBAAkB,OAAO,QAAQ;AAAA,IACjC,gBAAgB,OAAO,QAAQ;AAAA,IAC/B,iBAAiB,OAAO,QAAQ;AAAA,IAChC,gBAAgB,OAAO,QAAQ;AAAA,IAC/B,aAAa,OAAO,QAAQ;AAAA,IAC5B,UAAU,OAAO,QAAQ;AAAA;AAAA,IAEzB,SAAS,OAAO,OAAO;AAAA,IACvB,WAAW,OAAO,OAAO;AAAA,IACzB,WAAW,OAAO,OAAO;AAAA,IACzB,WAAW,OAAO,OAAO;AAAA,IACzB,QAAQ,OAAO,OAAO;AAAA,IACtB,OAAO,OAAO,OAAO;AAAA,IACrB,WAAW,OAAO,OAAO;AAAA,IACzB,aAAa,OAAO,OAAO;AAAA,IAC3B,SAAS,OAAO,OAAO;AAAA,IACvB,gBAAgB,OAAO,OAAO;AAAA,IAC9B,eAAe,OAAO,OAAO;AAAA,IAC7B,eAAe,OAAO,OAAO;AAAA,IAC7B,SAAS,OAAO,OAAO;AAAA;AAAA,IAEvB,OAAO,OAAO,MAAM;AAAA,IACpB,SAAS,OAAO,MAAM;AAAA,IACtB,eAAe,OAAO,MAAM;AAAA,IAC5B,YAAY,OAAO,MAAM;AAAA,IACzB,UAAU,OAAO,MAAM;AAAA,IACvB,YAAY,OAAO,MAAM;AAAA,IACzB,cAAc,OAAO,MAAM;AAAA,IAC3B,WAAW,OAAO,MAAM;AAAA,IACxB,aAAa,OAAO,MAAM;AAAA,IAC1B,aAAa,OAAO,MAAM;AAAA,IAC1B,UAAU,OAAO,MAAM;AAAA,IACvB,gBAAgB,OAAO,MAAM;AAAA,IAC7B,SAAS,OAAO,MAAM;AAAA,IACtB,WAAW,OAAO,MAAM;AAAA,IACxB,iBAAiB,OAAO,MAAM;AAAA,IAC9B,YAAY,OAAO,MAAM;AAAA;AAAA,IAEzB,OAAO,OAAO,MAAM;AAAA,IACpB,MAAM,OAAO,MAAM;AAAA,IACnB,OAAO,OAAO,MAAM;AAAA,IACpB,UAAU,OAAO,MAAM;AAAA,IACvB,kBAAkB,OAAO,MAAM;AAAA,IAC/B,MAAM,OAAO,MAAM;AAAA,IACnB,WAAW,OAAO,MAAM;AAAA,IACxB,SAAS,OAAO,MAAM;AAAA,IACtB,YAAY,OAAO,MAAM;AAAA,IACzB,cAAc,OAAO,MAAM;AAAA,IAC3B,kBAAkB,OAAO,MAAM;AAAA,IAC/B,mBAAmB,OAAO,MAAM;AAAA,IAChC,mBAAmB,OAAO,MAAM;AAAA,IAChC,aAAa,OAAO,MAAM;AAAA,IAC1B,aAAa,OAAO,MAAM;AAAA,IAC1B,kBAAkB,OAAO,MAAM;AAAA;AAAA,IAE/B,YAAY,OAAO,OAAO;AAAA,IAC1B,UAAU,OAAO,OAAO;AAAA,IACxB,QAAQ,OAAO,OAAO;AAAA,IACtB,MAAM,OAAO,OAAO;AAAA,IACpB,QAAQ,OAAO,OAAO;AAAA,IACtB,OAAO,OAAO,OAAO;AAAA,IACrB,WAAW,OAAO,OAAO;AAAA,IACzB,KAAK,OAAO,OAAO;AAAA,IACnB,QAAQ,OAAO,OAAO;AAAA,IACtB,SAAS,OAAO,OAAO;AAAA,IACvB,SAAS,OAAO,OAAO;AAAA,IACvB,QAAQ,OAAO,OAAO;AAAA,IACtB,OAAO,OAAO,OAAO;AAAA,IACrB,QAAQ,OAAO,OAAO;AAAA,IACtB,QAAQ,OAAO,OAAO;AAAA,IACtB,aAAa,OAAO,OAAO;AAAA,IAC3B,SAAS,OAAO,OAAO;AAAA,IACvB,QAAQ,OAAO,OAAO;AAAA,IACtB,OAAO,OAAO,OAAO;AAAA,IACrB,eAAe,OAAO,OAAO;AAAA,IAC7B,kBAAkB,OAAO,OAAO;AAAA,IAChC,mBAAmB,OAAO,OAAO;AAAA,IACjC,eAAe,OAAO,OAAO;AAAA,IAC7B,YAAY,OAAO,OAAO;AAAA,EAC5B;AACF;AAEA,IAAM,kBAAkB,gBAAgB,WAAW,IAAI,CAAC;AAExD,IAAM,iBAAmC;AAAA,EACvC,UAAU;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA,OAAO;AAAA,IACL,MAAM,CAAC,SAAS,aAAa,aAAa,SAAS;AAAA,IACnD,QAAQ;AAAA,MACN,OAAO;AAAA,MACP,WAAW;AAAA,MACX,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,IACA,SAAS,CAAC,SAAiB,SAAS;AAAA,IACpC,YAAY,CAAC,SAAiB,SAAS,WAAW,SAAS;AAAA,IAC3D,WAAW;AAAA,EACb;AAAA,EACA,UAAU;AAAA,IACR,OAAO;AAAA,IACP,WAAW;AAAA,EACb;AAAA,EACA,QAAQ;AAAA,EACR,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,iBAAiB;AAAA,IACjB,aAAa;AAAA,IACb,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,YAAY;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,YAAY,CAAC,cAAc,aAAa,cAAc,WAAW;AAAA,IACjE,YAAY,CAAC,aAAa,cAAc,iBAAiB;AAAA,IACzD,cAAc,IAAI,OAAO;AAAA,IACzB,cAAc,KAAK,OAAO;AAAA,EAC5B;AAAA,EACA,QAAQ;AAAA,IACN,aAAa;AAAA,IACb,eAAe;AAAA,IACf,yBAAyB;AAAA,IACzB,eAAe,CAAC,GAAG,CAAC;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,IACN,UAAU;AAAA,IACV,aAAa,CAAC,OAAO,uBAAuB,EAAE;AAAA,IAC9C,YAAY;AAAA,IACZ,aAAa,CAAC,SAAS,aAAa,IAAI;AAAA,IACxC,gBAAgB,CAAC,OAAO,cAAc,EAAE;AAAA,IACxC,OAAO;AAAA,IACP,UAAU,CAAC,OAAO,qBAAqB,EAAE;AAAA,IACzC,SAAS;AAAA,IACT,OAAO;AAAA,IACP,WAAW;AAAA,IACX,OAAO;AAAA,IACP,MAAM;AAAA,EACR;AAAA,EACA,SAAS;AACX;AAEA,SAAS,UACP,QACA,QACG;AACH,QAAM,SAAS,EAAE,GAAG,OAAO;AAC3B,aAAW,OAAO,OAAO,KAAK,MAAM,GAAkB;AACpD,UAAM,YAAY,OAAO,GAAG;AAC5B,UAAM,YAAY,OAAO,GAAG;AAC5B,QACE,aACA,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,OAAO,cAAc,YACrB,CAAC,MAAM,QAAQ,SAAS,KACxB,OAAO,cAAc,YACrB;AACA,MAAC,OAAe,GAAG,IAAI;AAAA,QACrB;AAAA,QACA;AAAA,MACF;AAAA,IACF,WAAW,cAAc,QAAW;AAClC,MAAC,OAAe,GAAG,IAAI;AAAA,IACzB;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,aACd,YAAuC,CAAC,GACtB;AAElB,QAAM,SAAS,UAAU,UAAU,eAAe;AAClD,QAAM,cAAc,gBAAgB,WAAW,MAAM,CAAC;AAGtD,QAAM,mBAAmB;AAAA,IACvB,GAAG;AAAA,IACH,SAAS;AAAA,EACX;AAGA,MAAI,CAAC,UAAU,QAAQ,aAAa;AAClC,UAAM,aAAa,WAAW,MAAM;AACpC,qBAAiB,SAAS;AAAA,MACxB,GAAG,iBAAiB;AAAA,MACpB,aAAa,WAAW,OAAO;AAAA,IACjC;AAAA,EACF;AAEA,SAAO,UAAU,kBAAkB,SAAS;AAC9C;;;AC5WA,SAAS,eAAe,kBAAkC;AAiBtD;AAdJ,IAAM,gBAAgB,cAAgC,cAAc;AAE7D,SAAS,gBAAgB;AAC9B,SAAO,WAAW,aAAa;AACjC;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AACF,GAGG;AACD,SACE,oBAAC,cAAc,UAAd,EAAuB,OAAO,QAAS,UAAS;AAErD;;;ACnBA,SAAS,iBAAAA,gBAAe,cAAAC,mBAAkC;AAuBtD,gBAAAC,YAAA;AApBJ,IAAM,iBAAiBF,eAAgC,IAAI;AAEpD,SAAS,iBAA4B;AAC1C,QAAM,MAAMC,YAAW,cAAc;AACrC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AACF,GAGG;AACD,SACE,gBAAAC,KAAC,eAAe,UAAf,EAAwB,OAAO,SAAU,UAAS;AAEvD;;;ACzBA;AAAA,EACE,iBAAAC;AAAA,EACA,cAAAC;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAmDH,gBAAAC,YAAA;AAzCJ,IAAM,gBAAgBF,eAAkC;AAAA,EACtD,OAAO;AAAA,EACP,oBAAoB,MAAM;AAAA,EAAC;AAAA,EAC3B,YAAY,MAAM;AAAA,EAAC;AACrB,CAAC;AAEM,SAAS,YAAY;AAC1B,SAAOC,YAAW,aAAa;AACjC;AAEO,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA;AACF,GAGG;AACD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAgB,MAAM;AAEhD,QAAM,qBAAqB;AAAA,IACzB,CAAC,QAAgB;AACf,UAAI,UAAU,OAAQ;AACtB,eAAS,SAAS;AAElB,iBAAW,MAAM;AACf,iBAAS,QAAQ;AACjB,iBAAS,GAAG;AAAA,MACd,GAAG,GAAG;AAAA,IACR;AAAA,IACA,CAAC,OAAO,QAAQ;AAAA,EAClB;AAEA,QAAM,aAAa,YAAY,MAAM;AACnC,QAAI,UAAU,SAAU;AACxB,0BAAsB,MAAM;AAC1B,eAAS,SAAS;AAClB,iBAAW,MAAM,SAAS,MAAM,GAAG,GAAG;AAAA,IACxC,CAAC;AAAA,EACH,GAAG,CAAC,KAAK,CAAC;AAEV,SACE,gBAAAC,KAAC,cAAc,UAAd,EAAuB,OAAO,EAAE,OAAO,oBAAoB,WAAW,GACpE,UACH;AAEJ;;;AC7DA;AAAA,EACE,iBAAAC;AAAA,EACA,cAAAC;AAAA,OAGK;AA0BH,gBAAAC,YAAA;AAVJ,IAAM,kBAAkBF,eAAoC,CAAC,CAAC;AAEvD,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAGG;AACD,SACE,gBAAAE,KAAC,gBAAgB,UAAhB,EAAyB,OAAO,WAC9B,UACH;AAEJ;AAEO,SAAS,uBACd,MACA,UACsC;AACtC,QAAM,WAAWD,YAAW,eAAe;AAC3C,SAAQ,SAAS,IAAI,KAA8C;AACrE;AAEO,SAAS,uBAA6C;AAC3D,SAAOA,YAAW,eAAe;AACnC;","names":["createContext","useContext","jsx","createContext","useContext","jsx","createContext","useContext","jsx"]}
@@ -3,7 +3,7 @@ import {
3
3
  useGavaActions,
4
4
  useGavaConfig,
5
5
  useSplash
6
- } from "./chunk-MC3FBYWV.js";
6
+ } from "./chunk-PHT76VW6.js";
7
7
 
8
8
  // src/components/editor/ArticleEditor.tsx
9
9
  import { useState as useState6, useEffect as useEffect4, useCallback as useCallback4, useRef as useRef3 } from "react";
@@ -2858,17 +2858,138 @@ function ContentList({
2858
2858
  ] });
2859
2859
  }
2860
2860
 
2861
+ // src/components/categories/CategoryManager.tsx
2862
+ import { useState as useState14, useEffect as useEffect7, useCallback as useCallback10 } from "react";
2863
+ import { Fragment as Fragment6, jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
2864
+ function CategoryManager({
2865
+ getCategories,
2866
+ createCategory,
2867
+ updateCategory,
2868
+ deleteCategory
2869
+ }) {
2870
+ const [categories, setCategories] = useState14([]);
2871
+ const [newName, setNewName] = useState14("");
2872
+ const [editingId, setEditingId] = useState14(null);
2873
+ const [editName, setEditName] = useState14("");
2874
+ const load = useCallback10(async () => {
2875
+ const cats = await getCategories();
2876
+ setCategories(cats);
2877
+ }, [getCategories]);
2878
+ useEffect7(() => {
2879
+ load();
2880
+ }, [load]);
2881
+ const handleCreate = async () => {
2882
+ if (!newName.trim()) return;
2883
+ await createCategory({ name: newName.trim() });
2884
+ setNewName("");
2885
+ load();
2886
+ };
2887
+ const handleUpdate = async (id) => {
2888
+ if (!editName.trim()) return;
2889
+ await updateCategory(id, { name: editName.trim() });
2890
+ setEditingId(null);
2891
+ load();
2892
+ };
2893
+ const handleDelete = async (id, name) => {
2894
+ if (!confirm(`Delete category "${name}"?`)) return;
2895
+ await deleteCategory(id);
2896
+ load();
2897
+ };
2898
+ return /* @__PURE__ */ jsxs22("div", { className: "ge-category-manager", children: [
2899
+ /* @__PURE__ */ jsx24("h2", { children: "Categories" }),
2900
+ /* @__PURE__ */ jsxs22("div", { className: "ge-category-create", style: { display: "flex", gap: "0.5rem", marginBottom: "1rem" }, children: [
2901
+ /* @__PURE__ */ jsx24(
2902
+ "input",
2903
+ {
2904
+ type: "text",
2905
+ value: newName,
2906
+ onChange: (e) => setNewName(e.target.value),
2907
+ placeholder: "New category name",
2908
+ className: "ge-field-input",
2909
+ onKeyDown: (e) => e.key === "Enter" && handleCreate(),
2910
+ style: { flex: 1 }
2911
+ }
2912
+ ),
2913
+ /* @__PURE__ */ jsx24("button", { onClick: handleCreate, className: "ge-btn ge-btn-primary", children: "Add" })
2914
+ ] }),
2915
+ /* @__PURE__ */ jsx24("ul", { className: "ge-category-list", style: { listStyle: "none", padding: 0 }, children: categories.map((cat) => /* @__PURE__ */ jsx24(
2916
+ "li",
2917
+ {
2918
+ style: {
2919
+ display: "flex",
2920
+ alignItems: "center",
2921
+ gap: "0.5rem",
2922
+ padding: "0.5rem 0",
2923
+ borderBottom: "1px solid var(--ge-card-border, #e5e7eb)"
2924
+ },
2925
+ children: editingId === cat.id ? /* @__PURE__ */ jsxs22(Fragment6, { children: [
2926
+ /* @__PURE__ */ jsx24(
2927
+ "input",
2928
+ {
2929
+ type: "text",
2930
+ value: editName,
2931
+ onChange: (e) => setEditName(e.target.value),
2932
+ className: "ge-field-input",
2933
+ style: { flex: 1 },
2934
+ onKeyDown: (e) => e.key === "Enter" && handleUpdate(cat.id),
2935
+ autoFocus: true
2936
+ }
2937
+ ),
2938
+ /* @__PURE__ */ jsx24(
2939
+ "button",
2940
+ {
2941
+ onClick: () => handleUpdate(cat.id),
2942
+ className: "ge-btn ge-btn-small",
2943
+ children: "Save"
2944
+ }
2945
+ ),
2946
+ /* @__PURE__ */ jsx24(
2947
+ "button",
2948
+ {
2949
+ onClick: () => setEditingId(null),
2950
+ className: "ge-btn ge-btn-small",
2951
+ children: "Cancel"
2952
+ }
2953
+ )
2954
+ ] }) : /* @__PURE__ */ jsxs22(Fragment6, { children: [
2955
+ /* @__PURE__ */ jsx24("span", { style: { flex: 1 }, children: cat.name }),
2956
+ /* @__PURE__ */ jsx24(
2957
+ "button",
2958
+ {
2959
+ onClick: () => {
2960
+ setEditingId(cat.id);
2961
+ setEditName(cat.name);
2962
+ },
2963
+ className: "ge-btn ge-btn-small",
2964
+ children: "Edit"
2965
+ }
2966
+ ),
2967
+ /* @__PURE__ */ jsx24(
2968
+ "button",
2969
+ {
2970
+ onClick: () => handleDelete(cat.id, cat.name),
2971
+ className: "ge-btn ge-btn-small ge-btn-danger",
2972
+ children: "Delete"
2973
+ }
2974
+ )
2975
+ ] })
2976
+ },
2977
+ cat.id
2978
+ )) })
2979
+ ] });
2980
+ }
2981
+
2861
2982
  // src/components/dashboard/DashboardNavbar.tsx
2862
2983
  import Link4 from "next/link";
2863
2984
  import { usePathname } from "next/navigation";
2864
2985
  import { useSession, signOut } from "next-auth/react";
2865
2986
  import { FileText as FileText3, Users, LogOut, BarChart3, ImageIcon as ImageIcon3 } from "lucide-react";
2866
- import { useState as useState14 } from "react";
2867
- import { jsx as jsx24, jsxs as jsxs22 } from "react/jsx-runtime";
2987
+ import { useState as useState15 } from "react";
2988
+ import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
2868
2989
  function DashboardNavbar() {
2869
2990
  const pathname = usePathname();
2870
2991
  const { data: session } = useSession();
2871
- const [mobileOpen, setMobileOpen] = useState14(false);
2992
+ const [mobileOpen, setMobileOpen] = useState15(false);
2872
2993
  const { branding, roles, strings, routes } = useGavaConfig();
2873
2994
  const role = session?.user?.role;
2874
2995
  const navigation = [
@@ -2884,10 +3005,10 @@ function DashboardNavbar() {
2884
3005
  ...role === roles.adminRole ? [{ name: strings.users, href: routes.users, icon: Users }] : []
2885
3006
  ];
2886
3007
  const isActive = (href) => pathname.startsWith(href);
2887
- return /* @__PURE__ */ jsx24("header", { className: "sticky top-0 z-50 px-4 pb-4", children: /* @__PURE__ */ jsx24("div", { className: "mx-auto max-w-7xl", children: /* @__PURE__ */ jsxs22("nav", { className: "bg-background/95 backdrop-blur-sm border border-t-transparent border-card-border rounded-b-xl", children: [
2888
- /* @__PURE__ */ jsxs22("div", { className: "flex h-14 items-center justify-between px-6", children: [
2889
- /* @__PURE__ */ jsxs22(Link4, { href: routes.home, className: "flex items-center gap-3", children: [
2890
- branding.logo && /* @__PURE__ */ jsx24("span", { className: "relative z-[9999] -my-[100px] py-[100px] bg-white rounded-b-lg px-1.5 pb-1.5", children: /* @__PURE__ */ jsx24(
3008
+ return /* @__PURE__ */ jsx25("header", { className: "sticky top-0 z-50 px-4 pb-4", children: /* @__PURE__ */ jsx25("div", { className: "mx-auto max-w-7xl", children: /* @__PURE__ */ jsxs23("nav", { className: "bg-background/95 backdrop-blur-sm border border-t-transparent border-card-border rounded-b-xl", children: [
3009
+ /* @__PURE__ */ jsxs23("div", { className: "flex h-14 items-center justify-between px-6", children: [
3010
+ /* @__PURE__ */ jsxs23(Link4, { href: routes.home, className: "flex items-center gap-3", children: [
3011
+ branding.logo && /* @__PURE__ */ jsx25("span", { className: "relative z-[9999] -my-[100px] py-[100px] bg-white rounded-b-lg px-1.5 pb-1.5", children: /* @__PURE__ */ jsx25(
2891
3012
  "img",
2892
3013
  {
2893
3014
  src: branding.logo,
@@ -2896,36 +3017,36 @@ function DashboardNavbar() {
2896
3017
  height: 44
2897
3018
  }
2898
3019
  ) }),
2899
- /* @__PURE__ */ jsx24("span", { className: "font-bold text-lg text-foreground", children: branding.name })
3020
+ /* @__PURE__ */ jsx25("span", { className: "font-bold text-lg text-foreground", children: branding.name })
2900
3021
  ] }),
2901
- /* @__PURE__ */ jsx24("div", { className: "hidden md:flex md:items-center md:gap-6", children: navigation.map((item) => /* @__PURE__ */ jsxs22(
3022
+ /* @__PURE__ */ jsx25("div", { className: "hidden md:flex md:items-center md:gap-6", children: navigation.map((item) => /* @__PURE__ */ jsxs23(
2902
3023
  Link4,
2903
3024
  {
2904
3025
  href: item.href,
2905
3026
  className: `flex items-center gap-2 text-sm font-medium transition-colors ${isActive(item.href) ? "text-accent" : "text-muted hover:text-foreground"}`,
2906
3027
  children: [
2907
- /* @__PURE__ */ jsx24(item.icon, { className: "w-4 h-4" }),
3028
+ /* @__PURE__ */ jsx25(item.icon, { className: "w-4 h-4" }),
2908
3029
  item.name
2909
3030
  ]
2910
3031
  },
2911
3032
  item.name
2912
3033
  )) }),
2913
- /* @__PURE__ */ jsxs22("div", { className: "hidden md:flex md:items-center md:gap-3", children: [
2914
- /* @__PURE__ */ jsxs22("div", { className: "text-right", children: [
2915
- /* @__PURE__ */ jsx24("p", { className: "text-sm font-medium text-foreground leading-tight", children: session?.user?.name }),
2916
- /* @__PURE__ */ jsx24("p", { className: "text-xs text-muted leading-tight", children: roles.labels[role || ""] || role })
3034
+ /* @__PURE__ */ jsxs23("div", { className: "hidden md:flex md:items-center md:gap-3", children: [
3035
+ /* @__PURE__ */ jsxs23("div", { className: "text-right", children: [
3036
+ /* @__PURE__ */ jsx25("p", { className: "text-sm font-medium text-foreground leading-tight", children: session?.user?.name }),
3037
+ /* @__PURE__ */ jsx25("p", { className: "text-xs text-muted leading-tight", children: roles.labels[role || ""] || role })
2917
3038
  ] }),
2918
- /* @__PURE__ */ jsx24(
3039
+ /* @__PURE__ */ jsx25(
2919
3040
  "button",
2920
3041
  {
2921
3042
  onClick: () => signOut({ callbackUrl: routes.login }),
2922
3043
  className: "p-2 rounded-full text-muted hover:text-foreground hover:bg-card transition-colors",
2923
3044
  title: strings.logout,
2924
- children: /* @__PURE__ */ jsx24(LogOut, { className: "w-4 h-4" })
3045
+ children: /* @__PURE__ */ jsx25(LogOut, { className: "w-4 h-4" })
2925
3046
  }
2926
3047
  )
2927
3048
  ] }),
2928
- /* @__PURE__ */ jsxs22(
3049
+ /* @__PURE__ */ jsxs23(
2929
3050
  "button",
2930
3051
  {
2931
3052
  type: "button",
@@ -2933,19 +3054,19 @@ function DashboardNavbar() {
2933
3054
  className: "md:hidden p-2 rounded-full text-foreground flex flex-col justify-center items-center w-10 h-10 gap-1.5",
2934
3055
  "aria-label": "Menu",
2935
3056
  children: [
2936
- /* @__PURE__ */ jsx24(
3057
+ /* @__PURE__ */ jsx25(
2937
3058
  "span",
2938
3059
  {
2939
3060
  className: `block h-0.5 w-5 bg-foreground rounded-full transition-all duration-300 ease-in-out ${mobileOpen ? "rotate-45 translate-y-2" : ""}`
2940
3061
  }
2941
3062
  ),
2942
- /* @__PURE__ */ jsx24(
3063
+ /* @__PURE__ */ jsx25(
2943
3064
  "span",
2944
3065
  {
2945
3066
  className: `block h-0.5 w-5 bg-foreground rounded-full transition-all duration-300 ease-in-out ${mobileOpen ? "opacity-0 scale-0" : ""}`
2946
3067
  }
2947
3068
  ),
2948
- /* @__PURE__ */ jsx24(
3069
+ /* @__PURE__ */ jsx25(
2949
3070
  "span",
2950
3071
  {
2951
3072
  className: `block h-0.5 w-5 bg-foreground rounded-full transition-all duration-300 ease-in-out ${mobileOpen ? "-rotate-45 -translate-y-2" : ""}`
@@ -2955,36 +3076,36 @@ function DashboardNavbar() {
2955
3076
  }
2956
3077
  )
2957
3078
  ] }),
2958
- /* @__PURE__ */ jsx24(
3079
+ /* @__PURE__ */ jsx25(
2959
3080
  "div",
2960
3081
  {
2961
3082
  className: `md:hidden overflow-hidden transition-all duration-300 ease-in-out ${mobileOpen ? "max-h-80 opacity-100" : "max-h-0 opacity-0"}`,
2962
- children: /* @__PURE__ */ jsxs22("div", { className: "py-4 border-t border-card-border mx-6 space-y-1", children: [
2963
- navigation.map((item) => /* @__PURE__ */ jsxs22(
3083
+ children: /* @__PURE__ */ jsxs23("div", { className: "py-4 border-t border-card-border mx-6 space-y-1", children: [
3084
+ navigation.map((item) => /* @__PURE__ */ jsxs23(
2964
3085
  Link4,
2965
3086
  {
2966
3087
  href: item.href,
2967
3088
  onClick: () => setMobileOpen(false),
2968
3089
  className: `flex items-center gap-3 px-3 py-2 rounded-full text-sm font-medium transition-colors ${isActive(item.href) ? "text-accent" : "text-muted hover:text-foreground"}`,
2969
3090
  children: [
2970
- /* @__PURE__ */ jsx24(item.icon, { className: "w-4 h-4" }),
3091
+ /* @__PURE__ */ jsx25(item.icon, { className: "w-4 h-4" }),
2971
3092
  item.name
2972
3093
  ]
2973
3094
  },
2974
3095
  item.name
2975
3096
  )),
2976
- /* @__PURE__ */ jsxs22("div", { className: "pt-3 mt-2 border-t border-card-border", children: [
2977
- /* @__PURE__ */ jsxs22("div", { className: "px-3 mb-2", children: [
2978
- /* @__PURE__ */ jsx24("p", { className: "text-sm font-medium text-foreground", children: session?.user?.name }),
2979
- /* @__PURE__ */ jsx24("p", { className: "text-xs text-muted", children: roles.labels[role || ""] || role })
3097
+ /* @__PURE__ */ jsxs23("div", { className: "pt-3 mt-2 border-t border-card-border", children: [
3098
+ /* @__PURE__ */ jsxs23("div", { className: "px-3 mb-2", children: [
3099
+ /* @__PURE__ */ jsx25("p", { className: "text-sm font-medium text-foreground", children: session?.user?.name }),
3100
+ /* @__PURE__ */ jsx25("p", { className: "text-xs text-muted", children: roles.labels[role || ""] || role })
2980
3101
  ] }),
2981
- /* @__PURE__ */ jsxs22(
3102
+ /* @__PURE__ */ jsxs23(
2982
3103
  "button",
2983
3104
  {
2984
3105
  onClick: () => signOut({ callbackUrl: routes.login }),
2985
3106
  className: "flex items-center gap-3 px-3 py-2 text-sm text-muted hover:text-foreground transition-colors w-full rounded-full hover:bg-card",
2986
3107
  children: [
2987
- /* @__PURE__ */ jsx24(LogOut, { className: "w-4 h-4" }),
3108
+ /* @__PURE__ */ jsx25(LogOut, { className: "w-4 h-4" }),
2988
3109
  strings.logout
2989
3110
  ]
2990
3111
  }
@@ -2997,7 +3118,7 @@ function DashboardNavbar() {
2997
3118
  }
2998
3119
 
2999
3120
  // src/components/dashboard/StatCard.tsx
3000
- import { jsx as jsx25, jsxs as jsxs23 } from "react/jsx-runtime";
3121
+ import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
3001
3122
  var accentStyles = {
3002
3123
  blue: {
3003
3124
  border: "border-l-blue-500",
@@ -3027,23 +3148,23 @@ function StatCard({
3027
3148
  accent = "blue"
3028
3149
  }) {
3029
3150
  const s = accentStyles[accent];
3030
- return /* @__PURE__ */ jsx25(
3151
+ return /* @__PURE__ */ jsx26(
3031
3152
  "div",
3032
3153
  {
3033
3154
  className: `bg-card border border-card-border rounded-xl p-5 border-l-[3px] ${s.border} transition-all duration-200 hover:shadow-sm`,
3034
- children: /* @__PURE__ */ jsxs23("div", { className: "flex items-start justify-between", children: [
3035
- /* @__PURE__ */ jsxs23("div", { children: [
3036
- /* @__PURE__ */ jsx25("p", { className: "text-xs font-medium text-muted uppercase tracking-wide mb-2", children: label }),
3037
- /* @__PURE__ */ jsx25("p", { className: "text-3xl font-bold text-foreground tabular-nums", children: value })
3155
+ children: /* @__PURE__ */ jsxs24("div", { className: "flex items-start justify-between", children: [
3156
+ /* @__PURE__ */ jsxs24("div", { children: [
3157
+ /* @__PURE__ */ jsx26("p", { className: "text-xs font-medium text-muted uppercase tracking-wide mb-2", children: label }),
3158
+ /* @__PURE__ */ jsx26("p", { className: "text-3xl font-bold text-foreground tabular-nums", children: value })
3038
3159
  ] }),
3039
- /* @__PURE__ */ jsx25("div", { className: `p-2 rounded-lg ${s.bg}`, children: /* @__PURE__ */ jsx25(Icon, { className: `w-5 h-5 ${s.text}` }) })
3160
+ /* @__PURE__ */ jsx26("div", { className: `p-2 rounded-lg ${s.bg}`, children: /* @__PURE__ */ jsx26(Icon, { className: `w-5 h-5 ${s.text}` }) })
3040
3161
  ] })
3041
3162
  }
3042
3163
  );
3043
3164
  }
3044
3165
 
3045
3166
  // src/components/splash/SplashScreen.tsx
3046
- import { jsx as jsx26, jsxs as jsxs24 } from "react/jsx-runtime";
3167
+ import { jsx as jsx27, jsxs as jsxs25 } from "react/jsx-runtime";
3047
3168
  var SPLASH_WORDS = ["Powerful", "Flexible", "Modular", "Secure", "Customizable"];
3048
3169
  function pickRandom3() {
3049
3170
  const shuffled = [...SPLASH_WORDS].sort(() => Math.random() - 0.5);
@@ -3055,28 +3176,28 @@ function SplashScreen() {
3055
3176
  if (phase === "idle") return null;
3056
3177
  const topClass = phase === "closing" ? "splash-enter-down" : phase === "opening" ? "splash-exit-up" : "";
3057
3178
  const bottomClass = phase === "closing" ? "splash-enter-up" : phase === "opening" ? "splash-exit-down" : "";
3058
- return /* @__PURE__ */ jsxs24("div", { className: "fixed inset-0 z-[9999] pointer-events-none", children: [
3059
- /* @__PURE__ */ jsx26(
3179
+ return /* @__PURE__ */ jsxs25("div", { className: "fixed inset-0 z-[9999] pointer-events-none", children: [
3180
+ /* @__PURE__ */ jsx27(
3060
3181
  "div",
3061
3182
  {
3062
3183
  className: `splash-half splash-half-top absolute inset-x-0 top-0 h-1/2 bg-background overflow-hidden ${topClass}`,
3063
- children: /* @__PURE__ */ jsx26("div", { className: "absolute inset-0 flex flex-col items-center justify-end pb-2", children: /* @__PURE__ */ jsx26("span", { className: "splash-title", children: "GAVA" }) })
3184
+ children: /* @__PURE__ */ jsx27("div", { className: "absolute inset-0 flex flex-col items-center justify-end pb-2", children: /* @__PURE__ */ jsx27("span", { className: "splash-title", children: "GAVA" }) })
3064
3185
  }
3065
3186
  ),
3066
- /* @__PURE__ */ jsx26(
3187
+ /* @__PURE__ */ jsx27(
3067
3188
  "div",
3068
3189
  {
3069
3190
  className: `splash-half splash-half-bottom absolute inset-x-0 bottom-0 h-1/2 bg-background overflow-hidden ${bottomClass}`,
3070
- children: /* @__PURE__ */ jsxs24("div", { className: "absolute inset-0 flex flex-col items-center justify-start pt-2", children: [
3071
- /* @__PURE__ */ jsx26("span", { className: "splash-title", children: "ENGINE" }),
3072
- /* @__PURE__ */ jsx26("div", { className: "flex items-center gap-3 mt-6", children: words.map((word, i) => /* @__PURE__ */ jsxs24(
3191
+ children: /* @__PURE__ */ jsxs25("div", { className: "absolute inset-0 flex flex-col items-center justify-start pt-2", children: [
3192
+ /* @__PURE__ */ jsx27("span", { className: "splash-title", children: "ENGINE" }),
3193
+ /* @__PURE__ */ jsx27("div", { className: "flex items-center gap-3 mt-6", children: words.map((word, i) => /* @__PURE__ */ jsxs25(
3073
3194
  "span",
3074
3195
  {
3075
3196
  className: "splash-word text-xs sm:text-sm font-medium tracking-widest uppercase",
3076
3197
  style: { animationDelay: `${0.2 + i * 0.1}s` },
3077
3198
  children: [
3078
3199
  word,
3079
- i < 2 && /* @__PURE__ */ jsx26("span", { className: "ml-3 text-muted-foreground", children: "\xB7" })
3200
+ i < 2 && /* @__PURE__ */ jsx27("span", { className: "ml-3 text-muted-foreground", children: "\xB7" })
3080
3201
  ]
3081
3202
  },
3082
3203
  word
@@ -3088,10 +3209,10 @@ function SplashScreen() {
3088
3209
  }
3089
3210
 
3090
3211
  // src/components/splash/DashboardSplashTrigger.tsx
3091
- import { useEffect as useEffect7 } from "react";
3212
+ import { useEffect as useEffect8 } from "react";
3092
3213
  function DashboardSplashTrigger() {
3093
3214
  const { openSplash } = useSplash();
3094
- useEffect7(() => {
3215
+ useEffect8(() => {
3095
3216
  if (typeof window.requestIdleCallback === "function") {
3096
3217
  const id = window.requestIdleCallback(() => openSplash());
3097
3218
  return () => window.cancelIdleCallback(id);
@@ -3131,9 +3252,10 @@ export {
3131
3252
  JsonRenderer,
3132
3253
  ContentEditor,
3133
3254
  ContentList,
3255
+ CategoryManager,
3134
3256
  DashboardNavbar,
3135
3257
  StatCard,
3136
3258
  SplashScreen,
3137
3259
  DashboardSplashTrigger
3138
3260
  };
3139
- //# sourceMappingURL=chunk-YSVQQBBU.js.map
3261
+ //# sourceMappingURL=chunk-XUWBLDOW.js.map