From a899b1b5682dc5879a7cf854188cc2e8aa8a3948 Mon Sep 17 00:00:00 2001 From: cupadev-admin Date: Mon, 9 Mar 2026 07:22:52 +0000 Subject: [PATCH] feat: add src/lib/store.ts --- src/lib/store.ts | 333 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 src/lib/store.ts diff --git a/src/lib/store.ts b/src/lib/store.ts new file mode 100644 index 0000000..04bbf0c --- /dev/null +++ b/src/lib/store.ts @@ -0,0 +1,333 @@ +import { create } from 'zustand' +import { v4 as uuidv4 } from 'uuid' + +export type PostStatus = 'draft' | 'published' +export type PageStatus = 'draft' | 'published' +export type MediaType = 'image' | 'video' + +export interface Post { + id: string + title: string + slug: string + content: string + excerpt: string + coverImage: string + category: string + tags: string[] + status: PostStatus + createdAt: string + updatedAt: string + author: string +} + +export interface GalleryItem { + id: string + title: string + description: string + imageUrl: string + category: string + tags: string[] + createdAt: string +} + +export interface Page { + id: string + title: string + slug: string + content: string + status: PageStatus + showInNav: boolean + navOrder: number + createdAt: string + updatedAt: string +} + +export interface NavItem { + id: string + label: string + href: string + order: number + type: 'page' | 'custom' +} + +export interface SiteSettings { + siteName: string + siteDescription: string + logo: string + favicon: string + primaryColor: string + footerText: string + socialLinks: { + facebook: string + instagram: string + twitter: string + youtube: string + linkedin: string + } + headerNavItems: NavItem[] + showSocialInHeader: boolean + showSocialInFooter: boolean + metaKeywords: string + metaDescription: string +} + +export interface User { + id: string + username: string + email: string + role: 'admin' | 'editor' + avatar: string +} + +interface CMSState { + // Auth + isAuthenticated: boolean + currentUser: User | null + login: (username: string, password: string) => boolean + logout: () => void + + // Posts + posts: Post[] + addPost: (post: Omit) => void + updatePost: (id: string, post: Partial) => void + deletePost: (id: string) => void + getPost: (id: string) => Post | undefined + getPostBySlug: (slug: string) => Post | undefined + + // Gallery + galleryItems: GalleryItem[] + addGalleryItem: (item: Omit) => void + updateGalleryItem: (id: string, item: Partial) => void + deleteGalleryItem: (id: string) => void + + // Pages + pages: Page[] + addPage: (page: Omit) => void + updatePage: (id: string, page: Partial) => void + deletePage: (id: string) => void + getPageBySlug: (slug: string) => Page | undefined + + // Settings + settings: SiteSettings + updateSettings: (settings: Partial) => void + updateNavItems: (items: NavItem[]) => void +} + +const defaultPosts: Post[] = [ + { + id: '1', + title: 'Bienvenue sur mon blog', + slug: 'bienvenue-sur-mon-blog', + content: '

Bonjour et bienvenue !

Ceci est mon premier article de blog. Je suis ravi de partager mes pensées et créations avec vous.

Ce site est propulsé par mon CMS personnel que j\'ai créé pour exprimer ma créativité.

', + excerpt: 'Bienvenue sur mon blog personnel où je partage mes créations et réflexions.', + coverImage: 'https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800', + category: 'General', + tags: ['bienvenue', 'blog'], + status: 'published', + createdAt: new Date(Date.now() - 7 * 24 * 3600000).toISOString(), + updatedAt: new Date(Date.now() - 7 * 24 * 3600000).toISOString(), + author: 'Admin', + }, + { + id: '2', + title: 'Exploration artistique', + slug: 'exploration-artistique', + content: '

L\'art comme expression

L\'art est une fenêtre sur l\'âme. Dans cet article, je vous emmène dans mon univers artistique.

Chaque œuvre raconte une histoire unique, une émotion capturée dans l\'instant.

', + excerpt: 'Un voyage au cœur de mes inspirations artistiques et créations.', + coverImage: 'https://images.unsplash.com/photo-1541367777708-7905fe3296c0?w=800', + category: 'Art', + tags: ['art', 'créativité', 'inspiration'], + status: 'published', + createdAt: new Date(Date.now() - 3 * 24 * 3600000).toISOString(), + updatedAt: new Date(Date.now() - 3 * 24 * 3600000).toISOString(), + author: 'Admin', + }, +] + +const defaultGallery: GalleryItem[] = [ + { + id: '1', + title: 'Paysage Urbain', + description: 'Une exploration des contrastes de la ville moderne.', + imageUrl: 'https://images.unsplash.com/photo-1477959858617-67f85cf4f1df?w=600', + category: 'Photographie', + tags: ['urbain', 'ville'], + createdAt: new Date().toISOString(), + }, + { + id: '2', + title: 'Nature Sauvage', + description: 'La beauté brute de la nature préservée.', + imageUrl: 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=600', + category: 'Nature', + tags: ['nature', 'forêt'], + createdAt: new Date().toISOString(), + }, + { + id: '3', + title: 'Architecture Moderne', + description: 'Les lignes épurées de l\'architecture contemporaine.', + imageUrl: 'https://images.unsplash.com/photo-1486325212027-8081e485255e?w=600', + category: 'Architecture', + tags: ['architecture', 'design'], + createdAt: new Date().toISOString(), + }, + { + id: '4', + title: 'Portrait Émotionnel', + description: 'Capturer l\'essence humaine dans un regard.', + imageUrl: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=600', + category: 'Portrait', + tags: ['portrait', 'humain'], + createdAt: new Date().toISOString(), + }, +] + +const defaultPages: Page[] = [ + { + id: '1', + title: 'À Propos', + slug: 'a-propos', + content: '

À Propos de Moi

Bienvenue sur mon espace personnel. Je suis un artiste et créateur passionné.

Ce site est mon musée numérique où je partage mes œuvres, mes réflexions et mon univers créatif.

', + status: 'published', + showInNav: true, + navOrder: 1, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, + { + id: '2', + title: 'Contact', + slug: 'contact', + content: '

Me Contacter

N\'hésitez pas à me contacter pour toute collaboration ou question.

Email: contact@monsite.com

', + status: 'published', + showInNav: true, + navOrder: 2, + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + }, +] + +const defaultSettings: SiteSettings = { + siteName: 'Mon Musée Personnel', + siteDescription: 'Blog & Galerie d\'Art Personnel', + logo: '', + favicon: '', + primaryColor: '#0ea5e9', + footerText: '© 2024 Mon Musée Personnel. Tous droits réservés.', + socialLinks: { + facebook: 'https://facebook.com', + instagram: 'https://instagram.com', + twitter: '', + youtube: '', + linkedin: '', + }, + headerNavItems: [ + { id: '1', label: 'Accueil', href: '/', order: 0, type: 'custom' }, + { id: '2', label: 'Blog', href: '/blog', order: 1, type: 'custom' }, + { id: '3', label: 'Galerie', href: '/galerie', order: 2, type: 'custom' }, + { id: '4', label: 'À Propos', href: '/pages/a-propos', order: 3, type: 'page' }, + { id: '5', label: 'Contact', href: '/pages/contact', order: 4, type: 'page' }, + ], + showSocialInHeader: true, + showSocialInFooter: true, + metaKeywords: 'blog, art, galerie, musée', + metaDescription: 'Mon espace créatif personnel', +} + +export const useCMSStore = create((set, get) => ({ + isAuthenticated: false, + currentUser: null, + + login: (username: string, password: string) => { + if (username === 'admin' && password === 'admin123') { + const user: User = { + id: '1', + username: 'admin', + email: 'admin@cms.com', + role: 'admin', + avatar: 'https://ui-avatars.com/api/?name=Admin&background=0ea5e9&color=fff', + } + set({ isAuthenticated: true, currentUser: user }) + return true + } + return false + }, + + logout: () => set({ isAuthenticated: false, currentUser: null }), + + posts: defaultPosts, + addPost: (post) => { + const newPost: Post = { + ...post, + id: uuidv4(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + set((state) => ({ posts: [newPost, ...state.posts] })) + }, + updatePost: (id, post) => { + set((state) => ({ + posts: state.posts.map((p) => + p.id === id ? { ...p, ...post, updatedAt: new Date().toISOString() } : p + ), + })) + }, + deletePost: (id) => { + set((state) => ({ posts: state.posts.filter((p) => p.id !== id) })) + }, + getPost: (id) => get().posts.find((p) => p.id === id), + getPostBySlug: (slug) => get().posts.find((p) => p.slug === slug), + + galleryItems: defaultGallery, + addGalleryItem: (item) => { + const newItem: GalleryItem = { + ...item, + id: uuidv4(), + createdAt: new Date().toISOString(), + } + set((state) => ({ galleryItems: [newItem, ...state.galleryItems] })) + }, + updateGalleryItem: (id, item) => { + set((state) => ({ + galleryItems: state.galleryItems.map((g) => + g.id === id ? { ...g, ...item } : g + ), + })) + }, + deleteGalleryItem: (id) => { + set((state) => ({ galleryItems: state.galleryItems.filter((g) => g.id !== id) })) + }, + + pages: defaultPages, + addPage: (page) => { + const newPage: Page = { + ...page, + id: uuidv4(), + createdAt: new Date().toISOString(), + updatedAt: new Date().toISOString(), + } + set((state) => ({ pages: [newPage, ...state.pages] })) + }, + updatePage: (id, page) => { + set((state) => ({ + pages: state.pages.map((p) => + p.id === id ? { ...p, ...page, updatedAt: new Date().toISOString() } : p + ), + })) + }, + deletePage: (id) => { + set((state) => ({ pages: state.pages.filter((p) => p.id !== id) })) + }, + getPageBySlug: (slug) => get().pages.find((p) => p.slug === slug), + + settings: defaultSettings, + updateSettings: (settings) => { + set((state) => ({ settings: { ...state.settings, ...settings } })) + }, + updateNavItems: (items) => { + set((state) => ({ + settings: { ...state.settings, headerNavItems: items }, + })) + }, +}))