From 8639adf4bef1823c440173416e8e7d889f7dba17 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Fri, 11 Jul 2025 15:38:37 +0000 Subject: [PATCH] Allow capture of identity document photos directly within the new sale form Implements camera functionality in NewSaleForm.tsx to capture recto/verso ID photos, storing them as photoRecto/photoVerso fields. Replit-Commit-Author: Agent Replit-Commit-Session-Id: dec54137-d5a2-4406-8ad4-5f2f9fb110bf Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/988a9bb7-9903-443d-8c9e-7773c87e8266.jpg --- client/src/components/NewSaleForm.tsx | 155 +++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 3 deletions(-) diff --git a/client/src/components/NewSaleForm.tsx b/client/src/components/NewSaleForm.tsx index 9a46d49..a88148f 100644 --- a/client/src/components/NewSaleForm.tsx +++ b/client/src/components/NewSaleForm.tsx @@ -9,9 +9,12 @@ import { Alert, AlertDescription } from '@/components/ui/alert'; import { useToast } from '@/hooks/use-toast'; import { useAuth } from '@/hooks/useAuth'; import { useStoreContext } from '@/hooks/useStoreContext'; +import { useCamera } from '@/hooks/useCamera'; import { apiRequest } from '@/lib/queryClient'; import { validateRequiredFields, validateEAN13, ARTICLE_CATEGORY_MAPPING, IDENTITY_TYPES, PAYMENT_METHODS } from '@/lib/validation'; -import { Package, User, Users, Save, Calendar, BadgeCheck, Info } from 'lucide-react'; +import { Package, User, Users, Save, Calendar, BadgeCheck, Info, Camera } from 'lucide-react'; +import CameraModal from '@/components/CameraModal'; +import type { PhotoType } from '@/types/sale'; interface FormData { vendeur: string; @@ -29,6 +32,8 @@ interface FormData { numeroIdentite: string; autoriteDelivrance: string; dateDelivrance: string; + photoRecto?: string; + photoVerso?: string; } export default function NewSaleForm() { @@ -37,6 +42,18 @@ export default function NewSaleForm() { const { user } = useAuth(); const { selectedStoreId } = useStoreContext(); const [isSubmitting, setIsSubmitting] = useState(false); + + // Camera functionality + const { + isOpen: isCameraOpen, + currentPhotoType, + isCapturing, + videoRef, + canvasRef, + startCamera, + capturePhoto, + stopCamera + } = useCamera(); const form = useForm({ defaultValues: { @@ -55,6 +72,8 @@ export default function NewSaleForm() { numeroIdentite: '', autoriteDelivrance: '', dateDelivrance: '', + photoRecto: '', + photoVerso: '', } }); @@ -97,6 +116,8 @@ export default function NewSaleForm() { numeroIdentite: '', autoriteDelivrance: '', dateDelivrance: '', + photoRecto: '', + photoVerso: '', }); }, onError: (error: any) => { @@ -145,6 +166,8 @@ export default function NewSaleForm() { numeroIdentite: data.numeroIdentite, autoriteDelivrance: data.autoriteDelivrance, dateDelivrance: data.dateDelivrance, + photoRecto: data.photoRecto, + photoVerso: data.photoVerso, }; await createSaleMutation.mutateAsync(saleData); @@ -155,6 +178,51 @@ export default function NewSaleForm() { } }; + // Photo capture functions + const handlePhotoCapture = async (photoType: PhotoType) => { + try { + await startCamera(photoType); + } catch (error) { + toast({ + title: "Erreur caméra", + description: error instanceof Error ? error.message : "Impossible d'accéder à la caméra", + variant: "destructive", + }); + } + }; + + const handleTakePhoto = async () => { + if (!currentPhotoType) return; + + try { + const photoData = await capturePhoto(); + form.setValue(currentPhotoType === 'recto' ? 'photoRecto' : 'photoVerso', photoData); + + toast({ + title: "Photo capturée", + description: `Photo ${currentPhotoType} enregistrée avec succès`, + variant: "success", + }); + + stopCamera(); + } catch (error) { + toast({ + title: "Erreur de capture", + description: error instanceof Error ? error.message : "Erreur lors de la capture", + variant: "destructive", + }); + } + }; + + const handleRemovePhoto = (photoType: PhotoType) => { + form.setValue(photoType === 'recto' ? 'photoRecto' : 'photoVerso', ''); + toast({ + title: "Photo supprimée", + description: `Photo ${photoType} supprimée`, + variant: "success", + }); + }; + return (
@@ -434,10 +502,80 @@ export default function NewSaleForm() { )} />
+ + {/* Section Photos */} +
+

Photos de la pièce d'identité

+
+ {/* Photo Recto */} +
+ + {form.watch('photoRecto') ? ( +
+ Photo recto + +
+ ) : ( + + )} +
+ + {/* Photo Verso */} +
+ + {form.watch('photoVerso') ? ( +
+ Photo verso + +
+ ) : ( + + )} +
+
+
- - {/* Boutons d'action */}
+ + {/* Camera Modal */} + ); } \ No newline at end of file