Track payment method and generate sales history reports as PDF

Adds payment method tracking to sales and exports sales data to PDF using jsPDF and jspdf-autotable.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 91318273-c764-4fd4-be04-bdc12c38af32
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/bd1b8419-ee4f-46fd-822d-f546a9643613.jpg
This commit is contained in:
michaelschal committed 2025-07-09 21:25:17 +00:00
1 parent 6f065a4903
commit 0a84dec87a
8 files changed
+394 -8

No files matched your search

+29 -1
View File
@@ -11,7 +11,7 @@ import { useToast } from '@/hooks/use-toast';
import { useAuth } from '@/hooks/useAuth'; import { useAuth } from '@/hooks/useAuth';
import { useStoreContext } from '@/hooks/useStoreContext'; import { useStoreContext } from '@/hooks/useStoreContext';
import { apiRequest } from '@/lib/queryClient'; import { apiRequest } from '@/lib/queryClient';
import { validateRequiredFields, validateEAN13, ARTICLE_CATEGORY_MAPPING, IDENTITY_TYPES } from '@/lib/validation'; import { validateRequiredFields, validateEAN13, ARTICLE_CATEGORY_MAPPING, IDENTITY_TYPES, PAYMENT_METHODS } from '@/lib/validation';
import { Package, User, Users, Save, X } from 'lucide-react'; import { Package, User, Users, Save, X } from 'lucide-react';
interface FormData { interface FormData {
@@ -25,6 +25,7 @@ interface FormData {
prenom: string; prenom: string;
dateNaissance: string; dateNaissance: string;
lieuNaissance: string; lieuNaissance: string;
modePaiement: string;
typeIdentite: string; typeIdentite: string;
numeroIdentite: string; numeroIdentite: string;
autoriteDelivrance: string; autoriteDelivrance: string;
@@ -50,6 +51,7 @@ export default function NewSaleForm() {
prenom: '', prenom: '',
dateNaissance: '', dateNaissance: '',
lieuNaissance: '', lieuNaissance: '',
modePaiement: 'Espèce',
typeIdentite: '', typeIdentite: '',
numeroIdentite: '', numeroIdentite: '',
autoriteDelivrance: '', autoriteDelivrance: '',
@@ -92,6 +94,7 @@ export default function NewSaleForm() {
prenom: '', prenom: '',
dateNaissance: '', dateNaissance: '',
lieuNaissance: '', lieuNaissance: '',
modePaiement: 'Espèce',
typeIdentite: '', typeIdentite: '',
numeroIdentite: '', numeroIdentite: '',
autoriteDelivrance: '', autoriteDelivrance: '',
@@ -374,6 +377,30 @@ export default function NewSaleForm() {
</FormItem> </FormItem>
)} )}
/> />
<FormField
control={form.control}
name="modePaiement"
render={({ field }) => (
<FormItem>
<FormLabel>Mode de paiement <span className="text-destructive">*</span></FormLabel>
<Select value={field.value} onValueChange={field.onChange}>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Sélectionner le mode de paiement" />
</SelectTrigger>
</FormControl>
<SelectContent>
{PAYMENT_METHODS.map((method) => (
<SelectItem key={method.value} value={method.value}>
{method.label}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div> </div>
</div> </div>
@@ -473,6 +500,7 @@ export default function NewSaleForm() {
prenom: '', prenom: '',
dateNaissance: '', dateNaissance: '',
lieuNaissance: '', lieuNaissance: '',
modePaiement: 'Espèce',
typeIdentite: '', typeIdentite: '',
numeroIdentite: '', numeroIdentite: '',
autoriteDelivrance: '', autoriteDelivrance: '',
@@ -58,6 +58,7 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM
{sale.dateNaissance ? formatDate(sale.dateNaissance) : 'Non spécifiée'} {sale.dateNaissance ? formatDate(sale.dateNaissance) : 'Non spécifiée'}
</div> </div>
<div><span className="font-medium">Lieu de naissance:</span> {sale.lieuNaissance || 'Non spécifié'}</div> <div><span className="font-medium">Lieu de naissance:</span> {sale.lieuNaissance || 'Non spécifié'}</div>
<div><span className="font-medium">Mode de paiement:</span> {sale.modePaiement || 'Espèce'}</div>
</div> </div>
</div> </div>
+119 -7
View File
@@ -11,7 +11,7 @@ import { useStoreContext } from '@/hooks/useStoreContext';
import { useAuth } from '@/hooks/useAuth'; import { useAuth } from '@/hooks/useAuth';
import { apiRequest } from '@/lib/queryClient'; import { apiRequest } from '@/lib/queryClient';
import type { Sale } from '@shared/schema'; import type { Sale } from '@shared/schema';
import { History, Download, Trash2, Eye, Filter } from 'lucide-react'; import { History, Download, Trash2, Eye, Filter, FileText } from 'lucide-react';
interface SalesHistoryProps { interface SalesHistoryProps {
canDelete?: boolean; canDelete?: boolean;
@@ -75,6 +75,102 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
deleteSaleMutation.mutate(sale.id); deleteSaleMutation.mutate(sale.id);
}; };
const exportToPDF = async () => {
if (sales.length === 0) {
toast({
title: "Aucune donnée",
description: "Aucune vente à exporter",
variant: "destructive",
});
return;
}
const { default: jsPDF } = await import('jspdf');
await import('jspdf-autotable');
const doc = new jsPDF() as any;
// Configuration du document
doc.setFont('helvetica');
// Titre du document
doc.setFontSize(16);
doc.text('Historique des Ventes de Feux d\'Artifice', 20, 20);
// Informations générales
doc.setFontSize(10);
doc.text(`Date d'export: ${new Date().toLocaleDateString('fr-FR')}`, 20, 30);
doc.text(`Nombre de ventes: ${sales.length}`, 20, 35);
if (startDate || endDate) {
let periodText = 'Période: ';
if (startDate) periodText += `du ${new Date(startDate).toLocaleDateString('fr-FR')} `;
if (endDate) periodText += `au ${new Date(endDate).toLocaleDateString('fr-FR')}`;
doc.text(periodText, 20, 40);
}
// Données du tableau
const tableData = sales.map(sale => [
new Date(sale.timestamp!).toLocaleDateString('fr-FR'),
sale.vendeur,
sale.typeArticle,
sale.categorie,
sale.quantite.toString(),
sale.nom + ' ' + sale.prenom,
sale.modePaiement || 'Espèce',
sale.typeIdentite,
sale.numeroIdentite
]);
// Configuration du tableau
doc.autoTable({
head: [['Date', 'Vendeur', 'Article', 'Cat.', 'Qté', 'Client', 'Paiement', 'ID Type', 'N° ID']],
body: tableData,
startY: startDate || endDate ? 50 : 45,
styles: {
fontSize: 8,
cellPadding: 2,
},
headStyles: {
fillColor: [59, 130, 246],
textColor: 255,
fontSize: 9,
},
alternateRowStyles: {
fillColor: [248, 250, 252],
},
columnStyles: {
0: { cellWidth: 18 }, // Date
1: { cellWidth: 25 }, // Vendeur
2: { cellWidth: 35 }, // Article
3: { cellWidth: 12 }, // Catégorie
4: { cellWidth: 12 }, // Quantité
5: { cellWidth: 30 }, // Client
6: { cellWidth: 20 }, // Paiement
7: { cellWidth: 15 }, // ID Type
8: { cellWidth: 25 }, // N° ID
},
margin: { left: 10, right: 10 },
});
// Pied de page
const pageCount = doc.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
doc.setPage(i);
doc.setFontSize(8);
doc.text(`Page ${i} sur ${pageCount}`, doc.internal.pageSize.width - 30, doc.internal.pageSize.height - 10);
doc.text('RegisFlow - Registre des ventes de feux d\'artifice', 20, doc.internal.pageSize.height - 10);
}
// Téléchargement
doc.save(`historique_ventes_${new Date().toISOString().split('T')[0]}.pdf`);
toast({
title: "Export PDF réussi",
description: `${sales.length} ventes exportées en PDF`,
});
};
const exportToCSV = () => { const exportToCSV = () => {
if (sales.length === 0) { if (sales.length === 0) {
toast({ toast({
@@ -97,6 +193,7 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
'Prénom client', 'Prénom client',
'Date de naissance', 'Date de naissance',
'Lieu de naissance', 'Lieu de naissance',
'Mode de paiement',
'Type identité', 'Type identité',
'Numéro identité', 'Numéro identité',
'Autorité délivrance', 'Autorité délivrance',
@@ -115,6 +212,7 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
sale.prenom, sale.prenom,
sale.dateNaissance, sale.dateNaissance,
sale.lieuNaissance || '', sale.lieuNaissance || '',
sale.modePaiement || 'Espèce',
sale.typeIdentite, sale.typeIdentite,
sale.numeroIdentite, sale.numeroIdentite,
sale.autoriteDelivrance, sale.autoriteDelivrance,
@@ -178,11 +276,18 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
/> />
</div> </div>
</div> </div>
<Button onClick={exportToCSV} variant="outline" className="touch-friendly-button w-full sm:w-auto"> <div className="flex gap-2 w-full sm:w-auto">
<Download className="h-4 w-4 mr-2" /> <Button onClick={exportToCSV} variant="outline" className="touch-friendly-button flex-1 sm:flex-none">
<span className="hidden sm:inline">Export CSV</span> <Download className="h-4 w-4 mr-2" />
<span className="sm:hidden">CSV</span> <span className="hidden sm:inline">Export CSV</span>
</Button> <span className="sm:hidden">CSV</span>
</Button>
<Button onClick={exportToPDF} variant="outline" className="touch-friendly-button flex-1 sm:flex-none">
<FileText className="h-4 w-4 mr-2" />
<span className="hidden sm:inline">Export PDF</span>
<span className="sm:hidden">PDF</span>
</Button>
</div>
</div> </div>
{/* Sales Table */} {/* Sales Table */}
@@ -204,6 +309,7 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
<TableHead className="responsive-caption">Client</TableHead> <TableHead className="responsive-caption">Client</TableHead>
<TableHead className="responsive-caption">Article</TableHead> <TableHead className="responsive-caption">Article</TableHead>
<TableHead className="responsive-caption">Quantité</TableHead> <TableHead className="responsive-caption">Quantité</TableHead>
<TableHead className="responsive-caption hidden lg:table-cell">Paiement</TableHead>
<TableHead className="responsive-caption hidden sm:table-cell">Gencode</TableHead> <TableHead className="responsive-caption hidden sm:table-cell">Gencode</TableHead>
<TableHead className="responsive-caption">Actions</TableHead> <TableHead className="responsive-caption">Actions</TableHead>
</TableRow> </TableRow>
@@ -234,7 +340,12 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
</div> </div>
</TableCell> </TableCell>
<TableCell>{sale.quantite}</TableCell> <TableCell>{sale.quantite}</TableCell>
<TableCell>{sale.gencode}</TableCell> <TableCell className="hidden lg:table-cell">
<span className="inline-flex px-2 py-1 text-xs rounded-full bg-blue-100 text-blue-800">
{sale.modePaiement || 'Espèce'}
</span>
</TableCell>
<TableCell className="hidden sm:table-cell">{sale.gencode}</TableCell>
<TableCell> <TableCell>
<div className="flex gap-2"> <div className="flex gap-2">
<Dialog> <Dialog>
@@ -274,6 +385,7 @@ export default function SalesHistory({ canDelete = false }: SalesHistoryProps) {
{selectedSale.lieuNaissance && ( {selectedSale.lieuNaissance && (
<p><strong>Lieu de naissance :</strong> {selectedSale.lieuNaissance}</p> <p><strong>Lieu de naissance :</strong> {selectedSale.lieuNaissance}</p>
)} )}
<p><strong>Mode de paiement :</strong> {selectedSale.modePaiement || 'Espèce'}</p>
</div> </div>
<div> <div>
<h4 className="font-medium mb-2">Pièce d'identité</h4> <h4 className="font-medium mb-2">Pièce d'identité</h4>
+9
View File
@@ -23,6 +23,7 @@ export function validateRequiredFields(formData: any): string[] {
{ key: 'nom', label: 'Nom' }, { key: 'nom', label: 'Nom' },
{ key: 'prenom', label: 'Prénom' }, { key: 'prenom', label: 'Prénom' },
{ key: 'dateNaissance', label: 'Date de naissance' }, { key: 'dateNaissance', label: 'Date de naissance' },
{ key: 'modePaiement', label: 'Mode de paiement' },
{ key: 'typeIdentite', label: 'Type de pièce d\'identité' }, { key: 'typeIdentite', label: 'Type de pièce d\'identité' },
{ key: 'numeroIdentite', label: 'Numéro de pièce d\'identité' }, { key: 'numeroIdentite', label: 'Numéro de pièce d\'identité' },
{ key: 'autoriteDelivrance', label: 'Autorité de délivrance' }, { key: 'autoriteDelivrance', label: 'Autorité de délivrance' },
@@ -60,3 +61,11 @@ export const IDENTITY_TYPES = [
{ value: "Passeport", label: "Passeport" }, { value: "Passeport", label: "Passeport" },
{ value: "Permis de conduire", label: "Permis de conduire" } { value: "Permis de conduire", label: "Permis de conduire" }
] as const; ] as const;
export const PAYMENT_METHODS = [
{ value: "Carte Bancaire", label: "Carte Bancaire" },
{ value: "Espèce", label: "Espèce" },
{ value: "Chèque", label: "Chèque" },
{ value: "Carte Cadeau", label: "Carte Cadeau" },
{ value: "Autre", label: "Autre" }
] as const;
+2
View File
@@ -17,6 +17,7 @@ export interface Sale {
prenom: string; prenom: string;
dateNaissance: string; dateNaissance: string;
lieuNaissance?: string; lieuNaissance?: string;
modePaiement: string;
// Identité // Identité
typeIdentite: string; typeIdentite: string;
@@ -40,6 +41,7 @@ export interface FormData {
prenom: string; prenom: string;
dateNaissance: string; dateNaissance: string;
lieuNaissance: string; lieuNaissance: string;
modePaiement: string;
typeIdentite: string; typeIdentite: string;
numeroIdentite: string; numeroIdentite: string;
autoriteDelivrance: string; autoriteDelivrance: string;
+230
View File
@@ -41,6 +41,7 @@
"@radix-ui/react-tooltip": "^1.2.0", "@radix-ui/react-tooltip": "^1.2.0",
"@tanstack/react-query": "^5.60.5", "@tanstack/react-query": "^5.60.5",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/jspdf": "^1.3.3",
"@types/memoizee": "^0.4.12", "@types/memoizee": "^0.4.12",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
@@ -56,6 +57,8 @@
"express-session": "^1.18.1", "express-session": "^1.18.1",
"framer-motion": "^11.13.1", "framer-motion": "^11.13.1",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"lucide-react": "^0.453.0", "lucide-react": "^0.453.0",
"memoizee": "^0.4.17", "memoizee": "^0.4.17",
"memorystore": "^1.6.7", "memorystore": "^1.6.7",
@@ -3478,6 +3481,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/jspdf": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@types/jspdf/-/jspdf-1.3.3.tgz",
"integrity": "sha512-DqwyAKpVuv+7DniCp2Deq1xGvfdnKSNgl9Agun2w6dFvR5UKamiv4VfYUgcypd8S9ojUyARFIlZqBrYrBMQlew==",
"license": "MIT"
},
"node_modules/@types/memoizee": { "node_modules/@types/memoizee": {
"version": "0.4.12", "version": "0.4.12",
"resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.12.tgz", "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.12.tgz",
@@ -3564,6 +3573,13 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/raf": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
"integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/range-parser": { "node_modules/@types/range-parser": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
@@ -3615,6 +3631,13 @@
"@types/send": "*" "@types/send": "*"
} }
}, },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
"license": "MIT",
"optional": true
},
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.13", "version": "8.5.13",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz",
@@ -3725,6 +3748,18 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/atob": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"license": "(MIT OR Apache-2.0)",
"bin": {
"atob": "bin/atob.js"
},
"engines": {
"node": ">= 4.5.0"
}
},
"node_modules/autoprefixer": { "node_modules/autoprefixer": {
"version": "10.4.20", "version": "10.4.20",
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz",
@@ -3769,6 +3804,16 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/base64-arraybuffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
"integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">= 0.6.0"
}
},
"node_modules/bcryptjs": { "node_modules/bcryptjs": {
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
@@ -3883,6 +3928,18 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
} }
}, },
"node_modules/btoa": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz",
"integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==",
"license": "(MIT OR Apache-2.0)",
"bin": {
"btoa": "bin/btoa.js"
},
"engines": {
"node": ">= 0.4.0"
}
},
"node_modules/buffer-from": { "node_modules/buffer-from": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3962,6 +4019,33 @@
], ],
"license": "CC-BY-4.0" "license": "CC-BY-4.0"
}, },
"node_modules/canvg": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/canvg/-/canvg-3.0.11.tgz",
"integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==",
"license": "MIT",
"optional": true,
"dependencies": {
"@babel/runtime": "^7.12.5",
"@types/raf": "^3.4.0",
"core-js": "^3.8.3",
"raf": "^3.4.1",
"regenerator-runtime": "^0.13.7",
"rgbcolor": "^1.0.1",
"stackblur-canvas": "^2.0.0",
"svg-pathdata": "^6.0.3"
},
"engines": {
"node": ">=10.0.0"
}
},
"node_modules/canvg/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT",
"optional": true
},
"node_modules/chokidar": { "node_modules/chokidar": {
"version": "3.6.0", "version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -4114,6 +4198,18 @@
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/core-js": {
"version": "3.44.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.44.0.tgz",
"integrity": "sha512-aFCtd4l6GvAXwVEh3XbbVqJGHDJt0OZRa+5ePGx3LLwi12WfexqQxcsohb2wgsa/92xtl19Hd66G/L+TaAxDMw==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/core-js"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -4128,6 +4224,16 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/css-line-break": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz",
"integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==",
"license": "MIT",
"optional": true,
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/cssesc": { "node_modules/cssesc": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -4383,6 +4489,16 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/dompurify": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
"integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
"license": "(MPL-2.0 OR Apache-2.0)",
"optional": true,
"optionalDependencies": {
"@types/trusted-types": "^2.0.7"
}
},
"node_modules/drizzle-kit": { "node_modules/drizzle-kit": {
"version": "0.30.4", "version": "0.30.4",
"resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.30.4.tgz", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.30.4.tgz",
@@ -5383,6 +5499,12 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
"license": "MIT"
},
"node_modules/fill-range": { "node_modules/fill-range": {
"version": "7.1.1", "version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -5684,6 +5806,20 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/html2canvas": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz",
"integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==",
"license": "MIT",
"optional": true,
"dependencies": {
"css-line-break": "^2.1.0",
"text-segmentation": "^1.0.3"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -5888,6 +6024,33 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/jspdf": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.1.tgz",
"integrity": "sha512-qaGIxqxetdoNnFQQXxTKUD9/Z7AloLaw94fFsOiJMxbfYdBbrBuhWmbzI8TVjrw7s3jBY1PFHofBKMV/wZPapg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.26.7",
"atob": "^2.1.2",
"btoa": "^1.2.1",
"fflate": "^0.8.1"
},
"optionalDependencies": {
"canvg": "^3.0.11",
"core-js": "^3.6.0",
"dompurify": "^3.2.4",
"html2canvas": "^1.0.0-rc.5"
}
},
"node_modules/jspdf-autotable": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/jspdf-autotable/-/jspdf-autotable-5.0.2.tgz",
"integrity": "sha512-YNKeB7qmx3pxOLcNeoqAv3qTS7KuvVwkFe5AduCawpop3NOkBUtqDToxNc225MlNecxT4kP2Zy3z/y/yvGdXUQ==",
"license": "MIT",
"peerDependencies": {
"jspdf": "^2 || ^3"
}
},
"node_modules/lightningcss": { "node_modules/lightningcss": {
"version": "1.29.2", "version": "1.29.2",
"resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz",
@@ -6669,6 +6832,13 @@
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
}, },
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"license": "MIT",
"optional": true
},
"node_modules/pg": { "node_modules/pg": {
"version": "8.13.1", "version": "8.13.1",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz", "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.1.tgz",
@@ -7135,6 +7305,16 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
"integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
"license": "MIT",
"optional": true,
"dependencies": {
"performance-now": "^2.1.0"
}
},
"node_modules/random-bytes": { "node_modules/random-bytes": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
@@ -7453,6 +7633,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/rgbcolor": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/rgbcolor/-/rgbcolor-1.0.1.tgz",
"integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==",
"license": "MIT OR SEE LICENSE IN FEEL-FREE.md",
"optional": true,
"engines": {
"node": ">= 0.8.15"
}
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.24.4", "version": "4.24.4",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz",
@@ -7735,6 +7925,16 @@
"node": ">= 10.x" "node": ">= 10.x"
} }
}, },
"node_modules/stackblur-canvas": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz",
"integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=0.1.14"
}
},
"node_modules/statuses": { "node_modules/statuses": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
@@ -7874,6 +8074,16 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/svg-pathdata": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz",
"integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==",
"license": "MIT",
"optional": true,
"engines": {
"node": ">=12.0.0"
}
},
"node_modules/tailwind-merge": { "node_modules/tailwind-merge": {
"version": "2.6.0", "version": "2.6.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz",
@@ -7936,6 +8146,16 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/text-segmentation": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz",
"integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==",
"license": "MIT",
"optional": true,
"dependencies": {
"utrie": "^1.0.2"
}
},
"node_modules/thenify": { "node_modules/thenify": {
"version": "3.3.1", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
@@ -8641,6 +8861,16 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/utrie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz",
"integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==",
"license": "MIT",
"optional": true,
"dependencies": {
"base64-arraybuffer": "^1.0.2"
}
},
"node_modules/vary": { "node_modules/vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+3
View File
@@ -43,6 +43,7 @@
"@radix-ui/react-tooltip": "^1.2.0", "@radix-ui/react-tooltip": "^1.2.0",
"@tanstack/react-query": "^5.60.5", "@tanstack/react-query": "^5.60.5",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/jspdf": "^1.3.3",
"@types/memoizee": "^0.4.12", "@types/memoizee": "^0.4.12",
"@types/node-cron": "^3.0.11", "@types/node-cron": "^3.0.11",
"bcryptjs": "^3.0.2", "bcryptjs": "^3.0.2",
@@ -58,6 +59,8 @@
"express-session": "^1.18.1", "express-session": "^1.18.1",
"framer-motion": "^11.13.1", "framer-motion": "^11.13.1",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"jspdf": "^3.0.1",
"jspdf-autotable": "^5.0.2",
"lucide-react": "^0.453.0", "lucide-react": "^0.453.0",
"memoizee": "^0.4.17", "memoizee": "^0.4.17",
"memorystore": "^1.6.7", "memorystore": "^1.6.7",
+1
View File
@@ -73,6 +73,7 @@ export const sales = pgTable("sales", {
prenom: varchar("prenom", { length: 255 }).notNull(), prenom: varchar("prenom", { length: 255 }).notNull(),
dateNaissance: varchar("date_naissance", { length: 10 }).notNull(), dateNaissance: varchar("date_naissance", { length: 10 }).notNull(),
lieuNaissance: varchar("lieu_naissance", { length: 255 }), lieuNaissance: varchar("lieu_naissance", { length: 255 }),
modePaiement: varchar("mode_paiement", { length: 50 }).notNull().default("Espèce"),
// Identité // Identité
typeIdentite: varchar("type_identite", { length: 50 }).notNull(), typeIdentite: varchar("type_identite", { length: 50 }).notNull(),