diff --git a/client/src/components/NewSaleForm.tsx b/client/src/components/NewSaleForm.tsx index a851d62..4485bbe 100644 --- a/client/src/components/NewSaleForm.tsx +++ b/client/src/components/NewSaleForm.tsx @@ -35,6 +35,7 @@ interface FormData { dateDelivrance: string; photoRecto?: string; photoVerso?: string; + photoTicket?: string; } export default function NewSaleForm() { @@ -75,6 +76,7 @@ export default function NewSaleForm() { dateDelivrance: '', photoRecto: '', photoVerso: '', + photoTicket: '', } }); @@ -119,6 +121,7 @@ export default function NewSaleForm() { dateDelivrance: '', photoRecto: '', photoVerso: '', + photoTicket: '', }); }, onError: (error: any) => { @@ -235,7 +238,13 @@ export default function NewSaleForm() { const photoData = await capturePhoto(); console.log('Photo captured successfully, data length:', photoData.length); - form.setValue(currentPhotoType === 'recto' ? 'photoRecto' : 'photoVerso', photoData); + if (currentPhotoType === 'recto') { + form.setValue('photoRecto', photoData); + } else if (currentPhotoType === 'verso') { + form.setValue('photoVerso', photoData); + } else if (currentPhotoType === 'ticket') { + form.setValue('photoTicket', photoData); + } toast({ title: "Photo capturée", @@ -255,7 +264,13 @@ export default function NewSaleForm() { }; const handleRemovePhoto = (photoType: PhotoType) => { - form.setValue(photoType === 'recto' ? 'photoRecto' : 'photoVerso', ''); + if (photoType === 'recto') { + form.setValue('photoRecto', ''); + } else if (photoType === 'verso') { + form.setValue('photoVerso', ''); + } else if (photoType === 'ticket') { + form.setValue('photoTicket', ''); + } toast({ title: "Photo supprimée", description: `Photo ${photoType} supprimée`, @@ -288,7 +303,13 @@ export default function NewSaleForm() { const reader = new FileReader(); reader.onload = (e) => { const result = e.target?.result as string; - form.setValue(photoType === 'recto' ? 'photoRecto' : 'photoVerso', result); + if (photoType === 'recto') { + form.setValue('photoRecto', result); + } else if (photoType === 'verso') { + form.setValue('photoVerso', result); + } else if (photoType === 'ticket') { + form.setValue('photoTicket', result); + } toast({ title: "Photo ajoutée", description: `Photo ${photoType} ajoutée avec succès`, @@ -715,6 +736,63 @@ export default function NewSaleForm() { + + {/* Photo Ticket de caisse */} +
+
+

Photo du ticket de caisse

+
+ +
+ + {form.watch('photoTicket') ? ( +
+ Photo ticket + +
+ ) : ( +
+ +
+ handleFileUpload('ticket', e)} + className="absolute inset-0 w-full h-full opacity-0 cursor-pointer" + /> + +
+
+ )} +
+
{/* Boutons d'action */} diff --git a/client/src/components/SaleDetailsModal.tsx b/client/src/components/SaleDetailsModal.tsx index b4320ba..182d752 100644 --- a/client/src/components/SaleDetailsModal.tsx +++ b/client/src/components/SaleDetailsModal.tsx @@ -87,49 +87,77 @@ export default function SaleDetailsModal({ isOpen, sale, onClose }: SaleDetailsM {/* Photos */} -
-

Photos de la Pièce d'Identité

-
-
- Photo Recto: - {sale.photoRecto ? ( -
- Photo recto + {(sale.photoRecto || sale.photoVerso || sale.photoTicket) && ( +
+

Photos

+ + {/* Photos de la pièce d'identité */} + {(sale.photoRecto || sale.photoVerso) && ( +
+
Pièce d'identité
+
+
+ Photo Recto: + {sale.photoRecto ? ( +
+ Photo recto + +
+ ) : ( +

Aucune photo

+ )} +
+
+ Photo Verso: + {sale.photoVerso ? ( +
+ Photo verso + +
+ ) : ( +

Aucune photo

+ )} +
+
+
+ )} + + {/* Photo du ticket de caisse */} + {sale.photoTicket && ( +
+
Ticket de caisse
+
+ Photo ticket
- ) : ( -

Aucune photo

- )} -
-
- Photo Verso: - {sale.photoVerso ? ( -
- Photo verso - -
- ) : ( -

Aucune photo

- )} -
+
+ )}
-
+ )} {/* System Information */}
diff --git a/client/src/types/sale.ts b/client/src/types/sale.ts index 0a5c7ea..3160bf0 100644 --- a/client/src/types/sale.ts +++ b/client/src/types/sale.ts @@ -27,6 +27,7 @@ export interface Sale { // Photos photoRecto?: string; photoVerso?: string; + photoTicket?: string; } export interface FormData { @@ -46,9 +47,10 @@ export interface FormData { dateDelivrance: string; photoRecto?: string; photoVerso?: string; + photoTicket?: string; } -export type PhotoType = 'recto' | 'verso'; +export type PhotoType = 'recto' | 'verso' | 'ticket'; export interface BackupData { timestamp: string; diff --git a/replit.md b/replit.md index 771bdfb..89d5e0b 100644 --- a/replit.md +++ b/replit.md @@ -264,6 +264,10 @@ Preferred communication style: Simple, everyday language. - ✅ Comprehensive error handling with specific messages for different failure types - ✅ HTTPS requirement detection for secure camera access - ✅ Multiple capture methods: camera → file upload for maximum compatibility +- ✅ Added receipt/ticket photo capture as third photo type alongside identity documents +- ✅ PayloadTooLargeError resolved by increasing server limit to 10MB for photo uploads +- ✅ Complete photo management system with separate sections for identity and receipt photos +- ✅ Enhanced SaleDetailsModal with organized photo display and download functionality ### Production Deployment System (January 11, 2025) - ✅ Complete production deployment configuration with Docker Compose diff --git a/shared/schema.ts b/shared/schema.ts index f749a44..0f2947b 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -83,6 +83,7 @@ export const sales = pgTable("sales", { // Photos photoRecto: text("photo_recto"), photoVerso: text("photo_verso"), + photoTicket: text("photo_ticket"), }); // Relations