feat: add src/store/useStore.ts
This commit is contained in:
parent
07d47a2dec
commit
f96dfd8002
|
|
@ -0,0 +1,391 @@
|
||||||
|
import { create } from 'zustand';
|
||||||
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
import type { Page, Post, GalleryItem, NavItem, SiteSettings } from '@/types';
|
||||||
|
|
||||||
|
interface CMSState {
|
||||||
|
// Auth
|
||||||
|
isAuthenticated: boolean;
|
||||||
|
currentUser: { username: string; email: string; role: string } | null;
|
||||||
|
login: (username: string, password: string) => boolean;
|
||||||
|
logout: () => void;
|
||||||
|
|
||||||
|
// Pages
|
||||||
|
pages: Page[];
|
||||||
|
addPage: (page: Omit<Page, 'id' | 'createdAt' | 'updatedAt'>) => Page;
|
||||||
|
updatePage: (id: string, data: Partial<Page>) => void;
|
||||||
|
deletePage: (id: string) => void;
|
||||||
|
getPageBySlug: (slug: string) => Page | undefined;
|
||||||
|
getPageById: (id: string) => Page | undefined;
|
||||||
|
|
||||||
|
// Posts
|
||||||
|
posts: Post[];
|
||||||
|
addPost: (post: Omit<Post, 'id' | 'createdAt' | 'updatedAt'>) => Post;
|
||||||
|
updatePost: (id: string, data: Partial<Post>) => void;
|
||||||
|
deletePost: (id: string) => void;
|
||||||
|
getPostBySlug: (slug: string) => Post | undefined;
|
||||||
|
getPostById: (id: string) => Post | undefined;
|
||||||
|
|
||||||
|
// Gallery
|
||||||
|
gallery: GalleryItem[];
|
||||||
|
addGalleryItem: (item: Omit<GalleryItem, 'id' | 'createdAt' | 'updatedAt'>) => GalleryItem;
|
||||||
|
updateGalleryItem: (id: string, data: Partial<GalleryItem>) => void;
|
||||||
|
deleteGalleryItem: (id: string) => void;
|
||||||
|
reorderGallery: (items: GalleryItem[]) => void;
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
navigation: NavItem[];
|
||||||
|
addNavItem: (item: Omit<NavItem, 'id'>) => void;
|
||||||
|
updateNavItem: (id: string, data: Partial<NavItem>) => void;
|
||||||
|
deleteNavItem: (id: string) => void;
|
||||||
|
reorderNav: (items: NavItem[]) => void;
|
||||||
|
|
||||||
|
// Settings
|
||||||
|
settings: SiteSettings;
|
||||||
|
updateSettings: (data: Partial<SiteSettings>) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const defaultSettings: SiteSettings = {
|
||||||
|
siteName: 'Musée & Blog',
|
||||||
|
tagline: 'Art, Culture & Découvertes',
|
||||||
|
description: 'Un espace dédié à l\'art, à la culture et aux découvertes.',
|
||||||
|
primaryColor: '#1a1a2e',
|
||||||
|
accentColor: '#a08660',
|
||||||
|
footerText: '© 2024 Musée & Blog. Tous droits réservés.',
|
||||||
|
socialLinks: {},
|
||||||
|
contactEmail: 'contact@musee-blog.fr',
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultPages: Page[] = [
|
||||||
|
{
|
||||||
|
id: 'home',
|
||||||
|
title: 'Accueil',
|
||||||
|
slug: '',
|
||||||
|
content: '<h1>Bienvenue au Musée & Blog</h1><p>Explorez notre collection unique d\'œuvres d\'art et découvrez nos articles passionnants sur l\'art et la culture.</p>',
|
||||||
|
excerpt: 'Bienvenue sur notre site dédié à l\'art et à la culture.',
|
||||||
|
type: 'home',
|
||||||
|
status: 'published',
|
||||||
|
featuredImage: 'https://images.unsplash.com/photo-1518998053901-5348d3961a04?w=1200',
|
||||||
|
order: 0,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'about',
|
||||||
|
title: 'À propos',
|
||||||
|
slug: 'a-propos',
|
||||||
|
content: '<h2>Notre Mission</h2><p>Nous sommes dédiés à la promotion de l\'art et de la culture à travers notre plateforme unique qui combine blog et musée virtuel.</p><h2>Notre Histoire</h2><p>Fondé en 2024, notre espace culturel numérique rassemble passionnés d\'art, artistes et curieux du monde entier.</p>',
|
||||||
|
excerpt: 'Découvrez notre mission et notre histoire.',
|
||||||
|
type: 'page',
|
||||||
|
status: 'published',
|
||||||
|
order: 1,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'contact',
|
||||||
|
title: 'Contact',
|
||||||
|
slug: 'contact',
|
||||||
|
content: '<h2>Contactez-nous</h2><p>Pour toute question ou collaboration, n\'hésitez pas à nous contacter.</p>',
|
||||||
|
excerpt: 'Prenez contact avec nous.',
|
||||||
|
type: 'contact',
|
||||||
|
status: 'published',
|
||||||
|
order: 4,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultPosts: Post[] = [
|
||||||
|
{
|
||||||
|
id: 'post-1',
|
||||||
|
title: 'L\'impressionnisme : Une révolution artistique',
|
||||||
|
slug: 'impressionnisme-revolution-artistique',
|
||||||
|
content: '<p>L\'impressionnisme est un mouvement pictural né en France au XIXe siècle qui révolutionna la peinture traditionnelle...</p><h2>Les Origines</h2><p>Le mouvement impressionniste est né d\'un groupe de peintres parisiens dans les années 1860...</p>',
|
||||||
|
excerpt: 'Découvrez comment l\'impressionnisme a transformé le monde de l\'art au XIXe siècle.',
|
||||||
|
category: 'article',
|
||||||
|
tags: ['impressionnisme', 'peinture', 'histoire de l\'art'],
|
||||||
|
status: 'published',
|
||||||
|
featuredImage: 'https://images.unsplash.com/photo-1541367777708-7905fe3296c0?w=800',
|
||||||
|
author: 'Admin',
|
||||||
|
publishedAt: new Date().toISOString(),
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'post-2',
|
||||||
|
title: 'Exposition : Les Maîtres de la Lumière',
|
||||||
|
slug: 'exposition-maitres-lumiere',
|
||||||
|
content: '<p>Notre nouvelle exposition temporaire met en lumière les artistes qui ont su capturer et sublimer la lumière dans leurs œuvres...</p>',
|
||||||
|
excerpt: 'Une exposition exceptionnelle dédiée aux maîtres de la lumière dans l\'art.',
|
||||||
|
category: 'exhibit',
|
||||||
|
tags: ['exposition', 'lumière', 'peinture'],
|
||||||
|
status: 'published',
|
||||||
|
featuredImage: 'https://images.unsplash.com/photo-1578662996442-48f60103fc96?w=800',
|
||||||
|
author: 'Admin',
|
||||||
|
publishedAt: new Date().toISOString(),
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'post-3',
|
||||||
|
title: 'Vernissage : Art Contemporain 2024',
|
||||||
|
slug: 'vernissage-art-contemporain-2024',
|
||||||
|
content: '<p>Rejoignez-nous pour le vernissage de notre exposition d\'art contemporain qui réunit des artistes émergents et confirmés...</p>',
|
||||||
|
excerpt: 'Vernissage de notre exposition annuelle d\'art contemporain.',
|
||||||
|
category: 'event',
|
||||||
|
tags: ['vernissage', 'art contemporain', 'événement'],
|
||||||
|
status: 'published',
|
||||||
|
featuredImage: 'https://images.unsplash.com/photo-1531058020387-3be344556be6?w=800',
|
||||||
|
author: 'Admin',
|
||||||
|
publishedAt: new Date().toISOString(),
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultGallery: GalleryItem[] = [
|
||||||
|
{
|
||||||
|
id: 'g-1',
|
||||||
|
title: 'Harmonie en Bleu',
|
||||||
|
description: 'Une œuvre abstraite explorant les nuances du bleu et la profondeur de l\'océan.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1578301978693-85fa9c0320b9?w=800',
|
||||||
|
artist: 'Marie Dupont',
|
||||||
|
year: '2023',
|
||||||
|
medium: 'Huile sur toile',
|
||||||
|
dimensions: '120 × 90 cm',
|
||||||
|
category: 'Peinture',
|
||||||
|
tags: ['abstrait', 'bleu', 'contemporain'],
|
||||||
|
order: 0,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g-2',
|
||||||
|
title: 'Portrait d\'Automne',
|
||||||
|
description: 'Un portrait saisissant capturant la mélancolie de la saison automnale.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1549490349-8643362247b5?w=800',
|
||||||
|
artist: 'Jean-Pierre Martin',
|
||||||
|
year: '2022',
|
||||||
|
medium: 'Aquarelle',
|
||||||
|
dimensions: '60 × 80 cm',
|
||||||
|
category: 'Peinture',
|
||||||
|
tags: ['portrait', 'automne', 'aquarelle'],
|
||||||
|
order: 1,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g-3',
|
||||||
|
title: 'Architecture Urbaine',
|
||||||
|
description: 'Photographie artistique explorant les lignes et formes de l\'architecture moderne.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1486325212027-8081e485255e?w=800',
|
||||||
|
artist: 'Sophie Lambert',
|
||||||
|
year: '2024',
|
||||||
|
medium: 'Photographie numérique',
|
||||||
|
dimensions: '100 × 150 cm',
|
||||||
|
category: 'Photographie',
|
||||||
|
tags: ['architecture', 'urbain', 'photographie'],
|
||||||
|
order: 2,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g-4',
|
||||||
|
title: 'La Danse des Formes',
|
||||||
|
description: 'Sculpture abstraite jouant avec l\'équilibre et le mouvement.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1561839561-b13bcfe95249?w=800',
|
||||||
|
artist: 'Pierre Moreau',
|
||||||
|
year: '2021',
|
||||||
|
medium: 'Bronze',
|
||||||
|
dimensions: '45 × 30 × 25 cm',
|
||||||
|
category: 'Sculpture',
|
||||||
|
tags: ['sculpture', 'abstrait', 'bronze'],
|
||||||
|
order: 3,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g-5',
|
||||||
|
title: 'Jardin Secret',
|
||||||
|
description: 'Une composition florale aux teintes pastel évoquant la sérénité d\'un jardin caché.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1416879595882-3373a0480b5b?w=800',
|
||||||
|
artist: 'Isabelle Chen',
|
||||||
|
year: '2023',
|
||||||
|
medium: 'Acrylique sur toile',
|
||||||
|
dimensions: '80 × 80 cm',
|
||||||
|
category: 'Peinture',
|
||||||
|
tags: ['floral', 'pastel', 'jardin'],
|
||||||
|
order: 4,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'g-6',
|
||||||
|
title: 'Lumière du Nord',
|
||||||
|
description: 'Paysage nordique baigné d\'une lumière spectrale et mystérieuse.',
|
||||||
|
imageUrl: 'https://images.unsplash.com/photo-1531366936337-7c912a4589a7?w=800',
|
||||||
|
artist: 'Erik Andersen',
|
||||||
|
year: '2024',
|
||||||
|
medium: 'Photographie',
|
||||||
|
dimensions: '120 × 80 cm',
|
||||||
|
category: 'Photographie',
|
||||||
|
tags: ['paysage', 'lumière', 'nord'],
|
||||||
|
order: 5,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const defaultNav: NavItem[] = [
|
||||||
|
{ id: 'nav-1', label: 'Accueil', url: '/', target: '_self', order: 0 },
|
||||||
|
{ id: 'nav-2', label: 'Musée', url: '/galerie', target: '_self', order: 1 },
|
||||||
|
{ id: 'nav-3', label: 'Blog', url: '/blog', target: '_self', order: 2 },
|
||||||
|
{ id: 'nav-4', label: 'À propos', url: '/a-propos', target: '_self', order: 3 },
|
||||||
|
{ id: 'nav-5', label: 'Contact', url: '/contact', target: '_self', order: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
export const useStore = create<CMSState>()(
|
||||||
|
persist(
|
||||||
|
(set, get) => ({
|
||||||
|
isAuthenticated: false,
|
||||||
|
currentUser: null,
|
||||||
|
|
||||||
|
login: (username, password) => {
|
||||||
|
if (username === 'admin' && password === 'admin123') {
|
||||||
|
set({
|
||||||
|
isAuthenticated: true,
|
||||||
|
currentUser: { username: 'admin', email: 'admin@cms.fr', role: 'admin' },
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
logout: () => set({ isAuthenticated: false, currentUser: null }),
|
||||||
|
|
||||||
|
pages: defaultPages,
|
||||||
|
|
||||||
|
addPage: (pageData) => {
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const page: Page = {
|
||||||
|
...pageData,
|
||||||
|
id: uuidv4(),
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
};
|
||||||
|
set((state) => ({ pages: [...state.pages, page] }));
|
||||||
|
return page;
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePage: (id, data) => {
|
||||||
|
set((state) => ({
|
||||||
|
pages: state.pages.map((p) =>
|
||||||
|
p.id === id ? { ...p, ...data, 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),
|
||||||
|
getPageById: (id) => get().pages.find((p) => p.id === id),
|
||||||
|
|
||||||
|
posts: defaultPosts,
|
||||||
|
|
||||||
|
addPost: (postData) => {
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const post: Post = {
|
||||||
|
...postData,
|
||||||
|
id: uuidv4(),
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
};
|
||||||
|
set((state) => ({ posts: [...state.posts, post] }));
|
||||||
|
return post;
|
||||||
|
},
|
||||||
|
|
||||||
|
updatePost: (id, data) => {
|
||||||
|
set((state) => ({
|
||||||
|
posts: state.posts.map((p) =>
|
||||||
|
p.id === id ? { ...p, ...data, updatedAt: new Date().toISOString() } : p
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
deletePost: (id) => {
|
||||||
|
set((state) => ({ posts: state.posts.filter((p) => p.id !== id) }));
|
||||||
|
},
|
||||||
|
|
||||||
|
getPostBySlug: (slug) => get().posts.find((p) => p.slug === slug),
|
||||||
|
getPostById: (id) => get().posts.find((p) => p.id === id),
|
||||||
|
|
||||||
|
gallery: defaultGallery,
|
||||||
|
|
||||||
|
addGalleryItem: (itemData) => {
|
||||||
|
const now = new Date().toISOString();
|
||||||
|
const item: GalleryItem = {
|
||||||
|
...itemData,
|
||||||
|
id: uuidv4(),
|
||||||
|
createdAt: now,
|
||||||
|
updatedAt: now,
|
||||||
|
};
|
||||||
|
set((state) => ({ gallery: [...state.gallery, item] }));
|
||||||
|
return item;
|
||||||
|
},
|
||||||
|
|
||||||
|
updateGalleryItem: (id, data) => {
|
||||||
|
set((state) => ({
|
||||||
|
gallery: state.gallery.map((g) =>
|
||||||
|
g.id === id ? { ...g, ...data, updatedAt: new Date().toISOString() } : g
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteGalleryItem: (id) => {
|
||||||
|
set((state) => ({ gallery: state.gallery.filter((g) => g.id !== id) }));
|
||||||
|
},
|
||||||
|
|
||||||
|
reorderGallery: (items) => set({ gallery: items }),
|
||||||
|
|
||||||
|
navigation: defaultNav,
|
||||||
|
|
||||||
|
addNavItem: (itemData) => {
|
||||||
|
const item: NavItem = { ...itemData, id: uuidv4() };
|
||||||
|
set((state) => ({ navigation: [...state.navigation, item] }));
|
||||||
|
},
|
||||||
|
|
||||||
|
updateNavItem: (id, data) => {
|
||||||
|
set((state) => ({
|
||||||
|
navigation: state.navigation.map((n) => (n.id === id ? { ...n, ...data } : n)),
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteNavItem: (id) => {
|
||||||
|
set((state) => ({ navigation: state.navigation.filter((n) => n.id !== id) }));
|
||||||
|
},
|
||||||
|
|
||||||
|
reorderNav: (items) => set({ navigation: items }),
|
||||||
|
|
||||||
|
settings: defaultSettings,
|
||||||
|
|
||||||
|
updateSettings: (data) => {
|
||||||
|
set((state) => ({ settings: { ...state.settings, ...data } }));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'cms-storage',
|
||||||
|
partialize: (state) => ({
|
||||||
|
pages: state.pages,
|
||||||
|
posts: state.posts,
|
||||||
|
gallery: state.gallery,
|
||||||
|
navigation: state.navigation,
|
||||||
|
settings: state.settings,
|
||||||
|
isAuthenticated: state.isAuthenticated,
|
||||||
|
currentUser: state.currentUser,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
Loading…
Reference in New Issue