mirror of
https://github.com/R0m1k3/Regisflow.git
synced 2026-10-20 13:48:21 +02:00
Restored to '3891ae828b859a29b86a84154ffef43740610335'
Replit-Restored-To: 3891ae828b
This commit is contained in:
1 parent
2f847dd333
commit
0821b70b7d
8 files changed
+212
-694
No files matched your search
@@ -308,52 +308,44 @@ export default function Administration() {
|
||||
deleteStoreMutation.mutate(store.id);
|
||||
};
|
||||
|
||||
// SQL Backup mutations
|
||||
// Backup mutations
|
||||
const exportBackupMutation = useMutation({
|
||||
mutationFn: async () => {
|
||||
const response = await fetch('/api/admin/backup/export', {
|
||||
method: 'GET',
|
||||
credentials: 'include',
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to export SQL backup');
|
||||
}
|
||||
|
||||
return response.text(); // Get SQL content as text
|
||||
const response = await apiRequest('/api/admin/backup/export');
|
||||
return response.json();
|
||||
},
|
||||
onSuccess: (sqlContent) => {
|
||||
// Create and download SQL file
|
||||
const blob = new Blob([sqlContent], { type: 'application/sql' });
|
||||
onSuccess: (data) => {
|
||||
// Créer et télécharger le fichier JSON
|
||||
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `backup_${new Date().toISOString().split('T')[0]}_${Date.now()}.sql`;
|
||||
link.download = `backup_${new Date().toISOString().split('T')[0]}_${Date.now()}.json`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
toast({
|
||||
title: "Sauvegarde SQL exportée",
|
||||
description: "Le fichier de sauvegarde SQL a été téléchargé",
|
||||
title: "Sauvegarde exportée",
|
||||
description: "Le fichier de sauvegarde a été téléchargé",
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: () => {
|
||||
toast({
|
||||
title: "Erreur d'export SQL",
|
||||
description: error instanceof Error ? error.message : "Impossible d'exporter la sauvegarde SQL",
|
||||
title: "Erreur",
|
||||
description: "Impossible d'exporter la sauvegarde",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const importBackupMutation = useMutation({
|
||||
mutationFn: async (sqlContent: string) => {
|
||||
mutationFn: async (backupData: any) => {
|
||||
const response = await apiRequest('/api/admin/backup/import', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ sqlContent }),
|
||||
body: JSON.stringify(backupData),
|
||||
});
|
||||
return response.json();
|
||||
},
|
||||
@@ -361,15 +353,15 @@ export default function Administration() {
|
||||
// Invalider toutes les queries pour forcer un refresh
|
||||
queryClient.clear();
|
||||
toast({
|
||||
title: "Sauvegarde SQL importée",
|
||||
description: "La base de données a été restaurée avec succès",
|
||||
title: "Sauvegarde importée",
|
||||
description: "Les données ont été restaurées avec succès",
|
||||
variant: "success",
|
||||
});
|
||||
},
|
||||
onError: (error) => {
|
||||
onError: () => {
|
||||
toast({
|
||||
title: "Erreur d'import SQL",
|
||||
description: error instanceof Error ? error.message : "Impossible d'importer la sauvegarde SQL",
|
||||
title: "Erreur",
|
||||
description: "Impossible d'importer la sauvegarde",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
@@ -406,22 +398,19 @@ export default function Administration() {
|
||||
const handleImportBackup = () => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.sql';
|
||||
input.accept = '.json';
|
||||
input.onchange = (event) => {
|
||||
const file = (event.target as HTMLInputElement).files?.[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
try {
|
||||
const sqlContent = e.target?.result as string;
|
||||
if (!sqlContent.trim()) {
|
||||
throw new Error('Le fichier SQL est vide');
|
||||
}
|
||||
importBackupMutation.mutate(sqlContent);
|
||||
const backupData = JSON.parse(e.target?.result as string);
|
||||
importBackupMutation.mutate(backupData);
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "Erreur",
|
||||
description: error instanceof Error ? error.message : "Fichier de sauvegarde SQL invalide",
|
||||
description: "Fichier de sauvegarde invalide",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
@@ -1235,7 +1224,7 @@ export default function Administration() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Téléchargez une sauvegarde SQL complète de toutes les données (utilisateurs, magasins, ventes) compatible avec PostgreSQL.
|
||||
Téléchargez une sauvegarde complète de toutes les données (utilisateurs, magasins, ventes).
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleExportBackup}
|
||||
@@ -1243,7 +1232,7 @@ export default function Administration() {
|
||||
className="w-full"
|
||||
>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
{exportBackupMutation.isPending ? "Export SQL en cours..." : "Télécharger la Sauvegarde SQL"}
|
||||
{exportBackupMutation.isPending ? "Export en cours..." : "Télécharger la Sauvegarde"}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -1258,7 +1247,7 @@ export default function Administration() {
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Restaurez la base de données à partir d'un fichier de sauvegarde SQL PostgreSQL.
|
||||
Restaurez les données à partir d'un fichier de sauvegarde.
|
||||
<strong className="text-red-600"> Attention : cette action remplacera toutes les données existantes.</strong>
|
||||
</p>
|
||||
<AlertDialog>
|
||||
@@ -1269,18 +1258,18 @@ export default function Administration() {
|
||||
className="w-full"
|
||||
>
|
||||
<Upload className="h-4 w-4 mr-2" />
|
||||
{importBackupMutation.isPending ? "Import SQL en cours..." : "Restaurer depuis un Fichier SQL"}
|
||||
{importBackupMutation.isPending ? "Import en cours..." : "Restaurer depuis un Fichier"}
|
||||
</Button>
|
||||
</AlertDialogTrigger>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Confirmer la restauration SQL</AlertDialogTitle>
|
||||
<AlertDialogTitle>Confirmer la restauration</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Êtes-vous sûr de vouloir restaurer la base de données depuis un fichier de sauvegarde SQL ?
|
||||
Êtes-vous sûr de vouloir restaurer les données depuis un fichier de sauvegarde ?
|
||||
<br /><br />
|
||||
<strong className="text-red-600">
|
||||
⚠️ ATTENTION : Cette action exécutera le script SQL qui remplacera complètement
|
||||
la structure et les données de la base de données PostgreSQL.
|
||||
⚠️ ATTENTION : Cette action supprimera définitivement toutes les données actuelles
|
||||
(utilisateurs, magasins, ventes) et les remplacera par celles du fichier de sauvegarde.
|
||||
</strong>
|
||||
<br /><br />
|
||||
Cette action est irréversible. Assurez-vous d'avoir une sauvegarde des données actuelles
|
||||
@@ -1293,7 +1282,7 @@ export default function Administration() {
|
||||
onClick={handleImportBackup}
|
||||
className="bg-red-600 hover:bg-red-700"
|
||||
>
|
||||
Confirmer la Restauration SQL
|
||||
Confirmer la Restauration
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
@@ -1310,19 +1299,16 @@ export default function Administration() {
|
||||
<CardContent>
|
||||
<div className="space-y-3 text-sm">
|
||||
<div>
|
||||
<strong>Sauvegarde automatique :</strong> Le système crée automatiquement une sauvegarde SQL toutes les 12 heures (00:00 et 12:00) et conserve les 10 plus récentes.
|
||||
<strong>Sauvegarde automatique :</strong> Le système crée automatiquement une sauvegarde toutes les 12 heures (00:00 et 12:00) et conserve les 10 plus récentes.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Format des fichiers :</strong> Les sauvegardes sont au format SQL PostgreSQL (.sql) et contiennent la structure complète et les données de la base.
|
||||
<strong>Format des fichiers :</strong> Les sauvegardes sont au format JSON et contiennent toutes les données de l'application.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Contenu inclus :</strong> Schéma de base de données, utilisateurs, magasins, ventes, et toutes les relations entre ces données.
|
||||
<strong>Contenu inclus :</strong> Utilisateurs, magasins, ventes, et toutes les relations entre ces données.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Avantages SQL :</strong> Sauvegarde native PostgreSQL compatible avec pg_dump/pg_restore, structure et données préservées.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Restauration :</strong> Remplace complètement la base de données existante par le contenu du fichier SQL importé.
|
||||
<strong>Sécurité :</strong> Les mots de passe sont hachés et ne peuvent pas être récupérés depuis une sauvegarde.
|
||||
</div>
|
||||
<div>
|
||||
<strong>Recommandation :</strong> Les sauvegardes automatiques couvrent la plupart des besoins, mais vous pouvez créer des sauvegardes manuelles avant des modifications importantes.
|
||||
|
||||
@@ -64,7 +64,7 @@ export default function NewSaleForm() {
|
||||
const form = useForm<FormData>({
|
||||
defaultValues: {
|
||||
vendeur: '',
|
||||
products: [{ typeArticle: '', categorie: '', quantite: '1', gencode: '' }],
|
||||
products: [{ typeArticle: '', categorie: '', quantite: '', gencode: '' }],
|
||||
nom: '',
|
||||
prenom: '',
|
||||
dateNaissance: '',
|
||||
@@ -85,7 +85,7 @@ export default function NewSaleForm() {
|
||||
|
||||
const addProduct = () => {
|
||||
const currentProducts = form.getValues('products');
|
||||
form.setValue('products', [...currentProducts, { typeArticle: '', categorie: '', quantite: '1', gencode: '' }]);
|
||||
form.setValue('products', [...currentProducts, { typeArticle: '', categorie: '', quantite: '', gencode: '' }]);
|
||||
};
|
||||
|
||||
const removeProduct = (index: number) => {
|
||||
@@ -370,7 +370,7 @@ export default function NewSaleForm() {
|
||||
<div className="section-vendor">
|
||||
<div className="section-header">
|
||||
<User className="h-5 w-5" />
|
||||
<span>Informations vendeur</span>
|
||||
<span>Informations Vendeur</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
<FormField
|
||||
@@ -378,7 +378,7 @@ export default function NewSaleForm() {
|
||||
name="vendeur"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Nom du vendeur <span className="text-destructive">*</span></FormLabel>
|
||||
<FormLabel>Vendeur <span className="text-destructive">*</span></FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder="Nom du vendeur" className="modern-input" {...field} />
|
||||
</FormControl>
|
||||
@@ -389,14 +389,11 @@ export default function NewSaleForm() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Section Produits */}
|
||||
{/* Section Produit */}
|
||||
<div className="section-product">
|
||||
<div className="section-header">
|
||||
<Package className="h-5 w-5" />
|
||||
<span>Produits</span>
|
||||
{products.length > 1 && (
|
||||
<span className="text-sm text-gray-600 ml-2">({products.length} produits)</span>
|
||||
)}
|
||||
<span>Informations Produit</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<FormField
|
||||
@@ -457,7 +454,7 @@ export default function NewSaleForm() {
|
||||
<FormItem>
|
||||
<FormLabel>Quantité <span className="text-destructive">*</span></FormLabel>
|
||||
<FormControl>
|
||||
<Input type="number" min="1" placeholder="1" className="modern-input" {...field} />
|
||||
<Input type="number" min="1" placeholder="Entrez la quantité" className="modern-input" {...field} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
@@ -490,7 +487,7 @@ export default function NewSaleForm() {
|
||||
<div className="section-client">
|
||||
<div className="section-header">
|
||||
<Users className="h-5 w-5" />
|
||||
<span>Informations client</span>
|
||||
<span>Informations Client</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<FormField
|
||||
@@ -576,7 +573,7 @@ export default function NewSaleForm() {
|
||||
<div className="section-identity">
|
||||
<div className="section-header">
|
||||
<BadgeCheck className="h-5 w-5" />
|
||||
<span>Pièce d'identité</span>
|
||||
<span>Pièce d'Identité</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<FormField
|
||||
@@ -647,8 +644,7 @@ export default function NewSaleForm() {
|
||||
{/* Section Photos */}
|
||||
<div className="mt-6 space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-gray-700">Photos de la pièce d'identité (optionnel)</h4>
|
||||
<p className="text-xs text-gray-500 mt-1">Les photos d'identité ne sont pas obligatoires</p>
|
||||
<h4 className="text-sm font-medium text-gray-700">Photos de la pièce d'identité</h4>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
@@ -759,8 +755,7 @@ export default function NewSaleForm() {
|
||||
{/* Photo Ticket de caisse */}
|
||||
<div className="mt-6 space-y-4">
|
||||
<div>
|
||||
<h4 className="text-sm font-medium text-gray-700">Photo du ticket de caisse (optionnel)</h4>
|
||||
<p className="text-xs text-gray-500 mt-1">La photo du ticket n'est pas obligatoire</p>
|
||||
<h4 className="text-sm font-medium text-gray-700">Photo du ticket de caisse</h4>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
|
||||
Reference in new issue
Block a user