mirror of
http://88.130.71.182:3000/BlitTech/badoHair_fe.git
synced 2026-06-12 23:23:22 +00:00
only front-end init
This commit is contained in:
102
contexts/AdminContext.tsx
Normal file
102
contexts/AdminContext.tsx
Normal file
@@ -0,0 +1,102 @@
|
||||
"use client";
|
||||
|
||||
import React, { useContext, useState, ReactNode, createContext } from "react";
|
||||
import { Product, products } from "@/data/products";
|
||||
|
||||
export interface Reservation {
|
||||
id: string;
|
||||
clientName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
service: string;
|
||||
date: string;
|
||||
time: string;
|
||||
status: "pending" | "confirmed" | "cancelled";
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
const initialReservations: Reservation[] = [
|
||||
{ id: "r1", clientName: "Marie Dupont", email: "marie@example.com", phone: "+33 6 12 34 56 78", service: "Pose complète", date: "2026-04-22", time: "10:00", status: "confirmed", createdAt: "2026-04-15" },
|
||||
{ id: "r2", clientName: "Sophie Laurent", email: "sophie@example.com", phone: "+33 6 98 76 54 32", service: "Conseil personnalisé", date: "2026-04-23", time: "14:30", status: "pending", createdAt: "2026-04-16" },
|
||||
{ id: "r3", clientName: "Amira Benali", email: "amira@example.com", phone: "+33 7 11 22 33 44", service: "Retouche", date: "2026-04-25", time: "11:00", status: "confirmed", createdAt: "2026-04-17" },
|
||||
{ id: "r4", clientName: "Léa Martin", email: "lea@example.com", phone: "+33 6 55 44 33 22", service: "Pose complète", date: "2026-04-28", time: "15:00", status: "pending", createdAt: "2026-04-17" },
|
||||
];
|
||||
|
||||
interface AdminContextType {
|
||||
isAdmin: boolean;
|
||||
login: (password: string) => boolean;
|
||||
logout: () => void;
|
||||
products: Product[];
|
||||
addProduct: (product: Omit<Product, "id">) => void;
|
||||
updateProduct: (id: string, product: Partial<Product>) => void;
|
||||
deleteProduct: (id: string) => void;
|
||||
reservations: Reservation[];
|
||||
updateReservationStatus: (id: string, status: Reservation["status"]) => void;
|
||||
deleteReservation: (id: string) => void;
|
||||
}
|
||||
|
||||
const AdminContext = createContext<AdminContextType | undefined>(undefined);
|
||||
|
||||
const ADMIN_PASSWORD = "admin123";
|
||||
|
||||
export const AdminProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [isAdmin, setIsAdmin] = useState(false);
|
||||
const [productList, setProducts] = useState<Product[]>(products);
|
||||
const [reservations, setReservations] = useState<Reservation[]>(initialReservations);
|
||||
|
||||
const login = (password: string) => {
|
||||
if (password === ADMIN_PASSWORD) {
|
||||
setIsAdmin(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const logout = () => setIsAdmin(false);
|
||||
|
||||
const addProduct = (product: Omit<Product, "id">) => {
|
||||
const newProduct: Product = { ...product, id: `p-${Date.now()}` };
|
||||
setProducts((prev) => [newProduct, ...prev]);
|
||||
};
|
||||
|
||||
const updateProduct = (id: string, updates: Partial<Product>) => {
|
||||
setProducts((prev) => prev.map((p) => (p.id === id ? { ...p, ...updates } : p)));
|
||||
};
|
||||
|
||||
const deleteProduct = (id: string) => {
|
||||
setProducts((prev) => prev.filter((p) => p.id !== id));
|
||||
};
|
||||
|
||||
const updateReservationStatus = (id: string, status: Reservation["status"]) => {
|
||||
setReservations((prev) => prev.map((r) => (r.id === id ? { ...r, status } : r)));
|
||||
};
|
||||
|
||||
const deleteReservation = (id: string) => {
|
||||
setReservations((prev) => prev.filter((r) => r.id !== id));
|
||||
};
|
||||
|
||||
return (
|
||||
<AdminContext.Provider
|
||||
value={{
|
||||
isAdmin,
|
||||
login,
|
||||
logout,
|
||||
products: productList,
|
||||
addProduct,
|
||||
updateProduct,
|
||||
deleteProduct,
|
||||
reservations,
|
||||
updateReservationStatus,
|
||||
deleteReservation,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AdminContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useAdmin = () => {
|
||||
const ctx = useContext(AdminContext);
|
||||
if (!ctx) throw new Error("useAdmin must be used within AdminProvider");
|
||||
return ctx;
|
||||
};
|
||||
74
contexts/CartContext.tsx
Normal file
74
contexts/CartContext.tsx
Normal file
@@ -0,0 +1,74 @@
|
||||
"use client";
|
||||
|
||||
import React, { createContext, useContext, useState, ReactNode } from "react";
|
||||
import { Product } from "@/data/products";
|
||||
|
||||
export interface CartItem {
|
||||
product: Product;
|
||||
quantity: number;
|
||||
selectedColor: string;
|
||||
selectedLength: string;
|
||||
}
|
||||
|
||||
interface CartContextType {
|
||||
items: CartItem[];
|
||||
addItem: (product: Product, color: string, length: string) => void;
|
||||
removeItem: (productId: string) => void;
|
||||
updateQuantity: (productId: string, quantity: number) => void;
|
||||
clearCart: () => void;
|
||||
totalItems: number;
|
||||
totalPrice: number;
|
||||
isCartOpen: boolean;
|
||||
setIsCartOpen: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const CartContext = createContext<CartContextType | undefined>(undefined);
|
||||
|
||||
export const CartProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [items, setItems] = useState<CartItem[]>([]);
|
||||
const [isCartOpen, setIsCartOpen] = useState(false);
|
||||
|
||||
const addItem = (product: Product, color: string, length: string) => {
|
||||
setItems((prev) => {
|
||||
const existing = prev.find((i) => i.product.id === product.id && i.selectedColor === color && i.selectedLength === length);
|
||||
if (existing) {
|
||||
return prev.map((i) =>
|
||||
i.product.id === product.id && i.selectedColor === color && i.selectedLength === length
|
||||
? { ...i, quantity: i.quantity + 1 }
|
||||
: i
|
||||
);
|
||||
}
|
||||
return [...prev, { product, quantity: 1, selectedColor: color, selectedLength: length }];
|
||||
});
|
||||
setIsCartOpen(true);
|
||||
};
|
||||
|
||||
const removeItem = (productId: string) => {
|
||||
setItems((prev) => prev.filter((i) => i.product.id !== productId));
|
||||
};
|
||||
|
||||
const updateQuantity = (productId: string, quantity: number) => {
|
||||
if (quantity <= 0) {
|
||||
removeItem(productId);
|
||||
return;
|
||||
}
|
||||
setItems((prev) => prev.map((i) => (i.product.id === productId ? { ...i, quantity } : i)));
|
||||
};
|
||||
|
||||
const clearCart = () => setItems([]);
|
||||
|
||||
const totalItems = items.reduce((sum, i) => sum + i.quantity, 0);
|
||||
const totalPrice = items.reduce((sum, i) => sum + i.product.price * i.quantity, 0);
|
||||
|
||||
return (
|
||||
<CartContext.Provider value={{ items, addItem, removeItem, updateQuantity, clearCart, totalItems, totalPrice, isCartOpen, setIsCartOpen }}>
|
||||
{children}
|
||||
</CartContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useCart = () => {
|
||||
const context = useContext(CartContext);
|
||||
if (!context) throw new Error("useCart must be used within CartProvider");
|
||||
return context;
|
||||
};
|
||||
80
contexts/LanguageContext.tsx
Normal file
80
contexts/LanguageContext.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
"use client";
|
||||
|
||||
import { useContext, useState, ReactNode, createContext } from "react";
|
||||
|
||||
export type Language = "fr" | "de" | "en" | "ar" | "tr";
|
||||
|
||||
interface LanguageContextType {
|
||||
language: Language;
|
||||
setLanguage: (lang: Language) => void;
|
||||
t: (key: string) => string;
|
||||
isRTL: boolean;
|
||||
}
|
||||
|
||||
const translations: Record<string, Record<Language, string>> = {
|
||||
"nav.shop": { fr: "Boutique", de: "Shop", en: "Shop", ar: "المتجر", tr: "Mağaza" },
|
||||
"nav.booking": { fr: "Réservation", de: "Terminbuchung", en: "Book Appointment", ar: "حجز موعد", tr: "Randevu" },
|
||||
"nav.about": { fr: "À propos", de: "Über uns", en: "About", ar: "من نحن", tr: "Hakkımızda" },
|
||||
"nav.contact": { fr: "Contact", de: "Kontakt", en: "Contact", ar: "اتصل بنا", tr: "İletişim" },
|
||||
"nav.account": { fr: "Mon compte", de: "Mein Konto", en: "My Account", ar: "حسابي", tr: "Hesabım" },
|
||||
"hero.title": { fr: "Sublimez votre beauté naturelle", de: "Unterstreichen Sie Ihre natürliche Schönheit", en: "Enhance your natural beauty", ar: "عززي جمالك الطبيعي", tr: "Doğal güzelliğinizi ortaya çıkarın" },
|
||||
"hero.subtitle": { fr: "Extensions de cheveux 100% naturels, qualité premium", de: "100% natürliche Haarverlängerungen, Premium-Qualität", en: "100% natural hair extensions, premium quality", ar: "وصلات شعر طبيعية 100%، جودة فاخرة", tr: "100% doğal saç eklentileri, premium kalite" },
|
||||
"hero.cta": { fr: "Découvrir la collection", de: "Kollektion entdecken", en: "Discover the collection", ar: "اكتشفي المجموعة", tr: "Koleksiyonu keşfedin" },
|
||||
"categories.title": { fr: "Nos Collections", de: "Unsere Kollektionen", en: "Our Collections", ar: "مجموعاتنا", tr: "Koleksiyonlarımız" },
|
||||
"bestsellers.title": { fr: "Les Plus Vendus", de: "Bestseller", en: "Bestsellers", ar: "الأكثر مبيعاً", tr: "En Çok Satanlar" },
|
||||
"booking.title": { fr: "Réservez votre rendez-vous", de: "Termin buchen", en: "Book your appointment", ar: "احجزي موعدك", tr: "Randevunuzu alın" },
|
||||
"booking.subtitle": { fr: "Consultation gratuite et personnalisée", de: "Kostenlose und persönliche Beratung", en: "Free personalized consultation", ar: "استشارة مجانية وشخصية", tr: "Ücretsiz kişisel danışmanlık" },
|
||||
"booking.cta": { fr: "Prendre rendez-vous", de: "Termin vereinbaren", en: "Book now", ar: "احجزي الآن", tr: "Randevu al" },
|
||||
"reviews.title": { fr: "Ce que disent nos clientes", de: "Was unsere Kundinnen sagen", en: "What our clients say", ar: "ماذا تقول عميلاتنا", tr: "Müşterilerimiz ne diyor" },
|
||||
"cart.title": { fr: "Mon Panier", de: "Warenkorb", en: "My Cart", ar: "سلة التسوق", tr: "Sepetim" },
|
||||
"cart.empty": { fr: "Votre panier est vide", de: "Ihr Warenkorb ist leer", en: "Your cart is empty", ar: "سلة التسوق فارغة", tr: "Sepetiniz boş" },
|
||||
"cart.total": { fr: "Total", de: "Gesamt", en: "Total", ar: "المجموع", tr: "Toplam" },
|
||||
"cart.checkout": { fr: "Commander", de: "Bestellen", en: "Checkout", ar: "إتمام الشراء", tr: "Sipariş ver" },
|
||||
"cart.add": { fr: "Ajouter au panier", de: "In den Warenkorb", en: "Add to cart", ar: "أضيفي إلى السلة", tr: "Sepete ekle" },
|
||||
"product.similar": { fr: "Produits similaires", de: "Ähnliche Produkte", en: "Similar products", ar: "منتجات مشابهة", tr: "Benzer ürünler" },
|
||||
"product.color": { fr: "Couleur", de: "Farbe", en: "Color", ar: "اللون", tr: "Renk" },
|
||||
"product.length": { fr: "Longueur", de: "Länge", en: "Length", ar: "الطول", tr: "Uzunluk" },
|
||||
"product.features": { fr: "Caractéristiques", de: "Eigenschaften", en: "Features", ar: "المميزات", tr: "Özellikler" },
|
||||
"shop.title": { fr: "Notre Boutique", de: "Unser Shop", en: "Our Shop", ar: "متجرنا", tr: "Mağazamız" },
|
||||
"shop.filter.all": { fr: "Tous", de: "Alle", en: "All", ar: "الكل", tr: "Tümü" },
|
||||
"auth.login": { fr: "Connexion", de: "Anmelden", en: "Login", ar: "تسجيل الدخول", tr: "Giriş" },
|
||||
"auth.register": { fr: "Inscription", de: "Registrieren", en: "Sign Up", ar: "إنشاء حساب", tr: "Kayıt ol" },
|
||||
"auth.email": { fr: "Adresse email", de: "E-Mail-Adresse", en: "Email address", ar: "البريد الإلكتروني", tr: "E-posta adresi" },
|
||||
"auth.password": { fr: "Mot de passe", de: "Passwort", en: "Password", ar: "كلمة المرور", tr: "Şifre" },
|
||||
"auth.name": { fr: "Nom complet", de: "Vollständiger Name", en: "Full name", ar: "الاسم الكامل", tr: "Tam ad" },
|
||||
"about.title": { fr: "Notre Histoire", de: "Unsere Geschichte", en: "Our Story", ar: "قصتنا", tr: "Hikayemiz" },
|
||||
"contact.title": { fr: "Contactez-nous", de: "Kontaktieren Sie uns", en: "Contact us", ar: "اتصلي بنا", tr: "Bize ulaşın" },
|
||||
"contact.send": { fr: "Envoyer", de: "Senden", en: "Send", ar: "إرسال", tr: "Gönder" },
|
||||
"contact.message": { fr: "Votre message", de: "Ihre Nachricht", en: "Your message", ar: "رسالتك", tr: "Mesajınız" },
|
||||
"footer.rights": { fr: "Tous droits réservés", de: "Alle Rechte vorbehalten", en: "All rights reserved", ar: "جميع الحقوق محفوظة", tr: "Tüm hakları saklıdır" },
|
||||
"booking.select_service": { fr: "Choisir un service", de: "Service wählen", en: "Select service", ar: "اختاري الخدمة", tr: "Hizmet seçin" },
|
||||
"booking.select_date": { fr: "Choisir une date", de: "Datum wählen", en: "Select date", ar: "اختاري التاريخ", tr: "Tarih seçin" },
|
||||
"booking.select_time": { fr: "Choisir un créneau", de: "Zeitfenster wählen", en: "Select time", ar: "اختاري الوقت", tr: "Saat seçin" },
|
||||
"booking.confirm": { fr: "Confirmer la réservation", de: "Buchung bestätigen", en: "Confirm booking", ar: "تأكيد الحجز", tr: "Rezervasyonu onayla" },
|
||||
"booking.phone": { fr: "Téléphone", de: "Telefon", en: "Phone", ar: "الهاتف", tr: "Telefon" },
|
||||
"booking.free": { fr: "Gratuit", de: "Kostenlos", en: "Free", ar: "مجاني", tr: "Ücretsiz" },
|
||||
};
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||
|
||||
export const LanguageProvider = ({ children }: { children: ReactNode }) => {
|
||||
const [language, setLanguage] = useState<Language>("fr");
|
||||
|
||||
const t = (key: string): string => {
|
||||
return translations[key]?.[language] || key;
|
||||
};
|
||||
|
||||
const isRTL = language === "ar";
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ language, setLanguage, t, isRTL }}>
|
||||
<div dir={isRTL ? "rtl" : "ltr"}>{children}</div>
|
||||
</LanguageContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useLanguage = () => {
|
||||
const context = useContext(LanguageContext);
|
||||
if (!context) throw new Error("useLanguage must be used within LanguageProvider");
|
||||
return context;
|
||||
};
|
||||
Reference in New Issue
Block a user