diff --git a/client/src/components/FireworksApp.tsx b/client/src/components/FireworksApp.tsx index f6fd2f9..349a62a 100644 --- a/client/src/components/FireworksApp.tsx +++ b/client/src/components/FireworksApp.tsx @@ -1,13 +1,34 @@ import { useState } from 'react'; +import { useQuery } from '@tanstack/react-query'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; -import { Shield, Flame } from 'lucide-react'; +import { Shield, Flame, Plus, History } from 'lucide-react'; import NewSaleForm from './NewSaleForm'; import SalesHistory from './SalesHistory'; -import { useIndexedDB } from '@/hooks/useIndexedDB'; +import { useAuth } from '@/hooks/useAuth'; +import { useStoreContext } from '@/hooks/useStoreContext'; +import { apiRequest } from '@/lib/queryClient'; +import type { Sale } from '@shared/schema'; export default function FireworksApp() { const [activeTab, setActiveTab] = useState('nouvelle-vente'); - const { sales, isLoading, error, saveSale, deleteSale } = useIndexedDB(); + const { user } = useAuth(); + const { selectedStoreId } = useStoreContext(); + + // Query for sales count + const { data: salesData } = useQuery({ + queryKey: ['/api/sales', selectedStoreId], + queryFn: async () => { + if (!selectedStoreId) return []; + const params = new URLSearchParams(); + params.append('storeId', selectedStoreId.toString()); + const response = await apiRequest(`/api/sales?${params.toString()}`); + return response.json(); + }, + enabled: !!selectedStoreId && !!user, + refetchOnWindowFocus: false, + }); + + const sales = Array.isArray(salesData) ? salesData : []; return (
@@ -18,8 +39,11 @@ export default function FireworksApp() {

- Registre des Ventes de Pétards + RegisFlow

+ + Registre des ventes de Feux d'artifice +
@@ -34,30 +58,21 @@ export default function FireworksApp() { - - - + Nouvelle Vente - - - + Historique ({sales.length} vente{sales.length !== 1 ? 's' : ''}) - + - + diff --git a/client/src/hooks/useIndexedDB.tsx b/client/src/hooks/useIndexedDB.tsx deleted file mode 100644 index fcafdf1..0000000 --- a/client/src/hooks/useIndexedDB.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { Sale } from '@/types/sale'; -import { FireworksDB, createAutoBackup } from '@/lib/storage'; - -export function useIndexedDB() { - const [db] = useState(() => new FireworksDB()); - const [sales, setSales] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - - const loadSales = useCallback(async () => { - try { - setIsLoading(true); - const allSales = await db.getAllSales(); - setSales(allSales.sort((a, b) => new Date(b.dateVente).getTime() - new Date(a.dateVente).getTime())); - setError(null); - } catch (err) { - console.error('Error loading sales:', err); - setError('Erreur lors du chargement des ventes'); - } finally { - setIsLoading(false); - } - }, [db]); - - const saveSale = useCallback(async (saleData: Omit) => { - try { - const savedSale = await db.saveSale(saleData); - setSales(prev => [savedSale, ...prev]); - - // Create auto backup - const updatedSales = await db.getAllSales(); - await createAutoBackup(updatedSales); - - setError(null); - return savedSale; - } catch (err) { - console.error('Error saving sale:', err); - setError('Erreur lors de l\'enregistrement'); - throw err; - } - }, [db]); - - const deleteSale = useCallback(async (id: number) => { - try { - await db.deleteSale(id); - setSales(prev => prev.filter(sale => sale.id !== id)); - - // Update auto backup - const updatedSales = await db.getAllSales(); - await createAutoBackup(updatedSales); - - setError(null); - } catch (err) { - console.error('Error deleting sale:', err); - setError('Erreur lors de la suppression'); - throw err; - } - }, [db]); - - useEffect(() => { - const initDB = async () => { - try { - await db.init(); - await loadSales(); - } catch (err) { - console.error('Error initializing database:', err); - setError('Erreur d\'initialisation de la base de données'); - setIsLoading(false); - } - }; - - initDB(); - }, [db, loadSales]); - - return { - sales, - isLoading, - error, - saveSale, - deleteSale, - loadSales - }; -} diff --git a/client/src/lib/storage.ts b/client/src/lib/storage.ts index b855b61..4d94000 100644 --- a/client/src/lib/storage.ts +++ b/client/src/lib/storage.ts @@ -1,119 +1,66 @@ +// Utilitaires pour l'export et la gestion des données +// L'application utilise PostgreSQL via l'API backend + import { Sale, BackupData } from '@/types/sale'; -const DB_NAME = 'VentesPetardsDB'; -const DB_VERSION = 1; -const STORE_NAME = 'ventes'; -const BACKUP_KEY = 'petards_backup_csv'; - -export class FireworksDB { - private db: IDBDatabase | null = null; - - async init(): Promise { - return new Promise((resolve, reject) => { - const request = indexedDB.open(DB_NAME, DB_VERSION); - - request.onerror = () => reject(request.error); - request.onsuccess = () => { - this.db = request.result; - resolve(); - }; - - request.onupgradeneeded = (event) => { - const db = (event.target as IDBOpenDBRequest).result; - - if (!db.objectStoreNames.contains(STORE_NAME)) { - const store = db.createObjectStore(STORE_NAME, { - keyPath: 'id', - autoIncrement: true - }); - store.createIndex('nom', 'nom', { unique: false }); - store.createIndex('dateVente', 'dateVente', { unique: false }); - } - }; - }); - } - - async saveSale(sale: Omit): Promise { - if (!this.db) throw new Error('Database not initialized'); - - return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); - const store = transaction.objectStore(STORE_NAME); - - const saleWithTimestamp = { - ...sale, - timestamp: new Date().toISOString() - }; - - const request = store.add(saleWithTimestamp); - - request.onerror = () => reject(request.error); - request.onsuccess = () => { - const savedSale = { ...saleWithTimestamp, id: request.result as number }; - resolve(savedSale); - }; - }); - } - - async getAllSales(): Promise { - if (!this.db) throw new Error('Database not initialized'); - - return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readonly'); - const store = transaction.objectStore(STORE_NAME); - const request = store.getAll(); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(request.result); - }); - } - - async deleteSale(id: number): Promise { - if (!this.db) throw new Error('Database not initialized'); - - return new Promise((resolve, reject) => { - const transaction = this.db!.transaction([STORE_NAME], 'readwrite'); - const store = transaction.objectStore(STORE_NAME); - const request = store.delete(id); - - request.onerror = () => reject(request.error); - request.onsuccess = () => resolve(); - }); - } -} - -export async function createAutoBackup(sales: Sale[]): Promise { - const backupData: BackupData = { - timestamp: new Date().toISOString(), - sales, - recordCount: sales.length - }; - - localStorage.setItem(BACKUP_KEY, JSON.stringify(backupData)); -} - -export function getAutoBackup(): BackupData | null { - try { - const backup = localStorage.getItem(BACKUP_KEY); - return backup ? JSON.parse(backup) : null; - } catch (error) { - console.error('Error loading backup:', error); - return null; - } -} - -export function downloadBackup(): void { - const backup = getAutoBackup(); - if (!backup) return; - - const blob = new Blob([JSON.stringify(backup, null, 2)], { - type: 'application/json' - }); +// Fonction utilitaire pour télécharger des données JSON +export function downloadJSON(data: any, filename: string): void { + const dataStr = JSON.stringify(data, null, 2); + const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); + const linkElement = document.createElement('a'); + linkElement.setAttribute('href', dataUri); + linkElement.setAttribute('download', filename); + linkElement.click(); +} + +// Fonction utilitaire pour créer un CSV des ventes +export function exportSalesToCSV(sales: Sale[]): void { + const csvHeader = [ + 'Date/Heure', + 'Vendeur', + 'Date de vente', + 'Type d\'article', + 'Catégorie', + 'Quantité', + 'Gencode', + 'Nom client', + 'Prénom client', + 'Date de naissance', + 'Lieu de naissance', + 'Type identité', + 'Numéro identité', + 'Autorité délivrance', + 'Date délivrance' + ]; + + const csvData = sales.map(sale => [ + sale.timestamp ? new Date(sale.timestamp).toLocaleString('fr-FR') : '', + sale.vendeur, + sale.dateVente, + sale.typeArticle, + sale.categorie, + sale.quantite, + sale.gencode, + sale.nom, + sale.prenom, + sale.dateNaissance, + sale.lieuNaissance || '', + sale.typeIdentite, + sale.numeroIdentite, + sale.autoriteDelivrance, + sale.dateDelivrance + ]); + + const csvContent = [csvHeader, ...csvData] + .map(row => row.map(cell => `"${cell}"`).join(',')) + .join('\n'); + + const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); - link.href = URL.createObjectURL(blob); - link.download = `backup_petards_${new Date().toISOString().split('T')[0]}.json`; + const url = URL.createObjectURL(blob); + link.setAttribute('href', url); + link.setAttribute('download', `ventes_${new Date().toISOString().split('T')[0]}.csv`); link.click(); - - URL.revokeObjectURL(link.href); -} + URL.revokeObjectURL(url); +} \ No newline at end of file