Restore camera functionality and update new sales form component

Refactors CameraModal.tsx to restore camera functionality, updates NewSaleForm to NewSaleFormMultiProduct in FireworksApp.tsx.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: c7151ec8-4e6b-4fcf-b628-65b094ac18fd
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0715008c-7244-40f4-befc-26c014852236/c7151ec8-4e6b-4fcf-b628-65b094ac18fd/PgD91fN
This commit is contained in:
michaelschal committed 2025-07-19 13:07:03 +00:00
1 parent d1f3fced74
commit e38d967015
2 files changed
+58 -90

No files matched your search

+56 -88
View File
@@ -1,4 +1,4 @@
import { useState, useRef, useEffect } from 'react'; import { useEffect } from 'react';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Camera, X } from 'lucide-react'; import { Camera, X } from 'lucide-react';
@@ -7,108 +7,76 @@ import { PhotoType } from '@/types/sale';
interface CameraModalProps { interface CameraModalProps {
isOpen: boolean; isOpen: boolean;
photoType: PhotoType | null; photoType: PhotoType | null;
videoRef: React.RefObject<HTMLVideoElement>;
canvasRef: React.RefObject<HTMLCanvasElement>;
isCapturing: boolean;
onClose: () => void; onClose: () => void;
onPhotoTaken: (photoData: string) => void; onCapture: () => void;
} }
export default function CameraModal({ export default function CameraModal({
isOpen, isOpen,
photoType, photoType,
videoRef,
canvasRef,
isCapturing,
onClose, onClose,
onPhotoTaken onCapture
}: CameraModalProps) { }: CameraModalProps) {
const videoRef = useRef<HTMLVideoElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);
const [stream, setStream] = useState<MediaStream | null>(null);
const [isCapturing, setIsCapturing] = useState(false);
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen && videoRef.current) {
startCamera(); const video = videoRef.current;
} else { console.log('CameraModal useEffect - video element ready');
stopCamera();
} const handleLoadedMetadata = () => {
console.log('CameraModal: Video metadata loaded, dimensions:', video.videoWidth, 'x', video.videoHeight);
return () => { };
stopCamera();
}; const handleCanPlay = () => {
}, [isOpen]); console.log('CameraModal: Video can play, readyState:', video.readyState);
};
const startCamera = async () => {
try { const handleError = (error: Event) => {
const mediaStream = await navigator.mediaDevices.getUserMedia({ console.error('CameraModal: Video error event:', error);
video: { };
facingMode: 'environment',
width: { ideal: 1280 }, const handlePlay = () => {
height: { ideal: 720 } console.log('CameraModal: Video started playing');
};
video.addEventListener('loadedmetadata', handleLoadedMetadata);
video.addEventListener('canplay', handleCanPlay);
video.addEventListener('error', handleError);
video.addEventListener('play', handlePlay);
// Forcer le play avec gestion d'erreur
const startPlayback = async () => {
try {
await video.play();
console.log('CameraModal: Video play() successful');
} catch (playError) {
console.error('CameraModal: Video play() failed:', playError);
} }
}); };
if (videoRef.current) { startPlayback();
videoRef.current.srcObject = mediaStream;
setStream(mediaStream); return () => {
} video.removeEventListener('loadedmetadata', handleLoadedMetadata);
} catch (error) { video.removeEventListener('canplay', handleCanPlay);
console.error('Error accessing camera:', error); video.removeEventListener('error', handleError);
// Fallback: try without specific constraints video.removeEventListener('play', handlePlay);
try { };
const mediaStream = await navigator.mediaDevices.getUserMedia({
video: true
});
if (videoRef.current) {
videoRef.current.srcObject = mediaStream;
setStream(mediaStream);
}
} catch (fallbackError) {
console.error('Fallback camera access failed:', fallbackError);
}
} }
}; }, [isOpen, videoRef]);
const stopCamera = () => {
if (stream) {
stream.getTracks().forEach(track => track.stop());
setStream(null);
}
};
const capturePhoto = () => {
if (!videoRef.current || !canvasRef.current) return;
setIsCapturing(true);
const video = videoRef.current;
const canvas = canvasRef.current;
const context = canvas.getContext('2d');
if (!context) return;
// Set canvas dimensions to match video
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// Draw video frame to canvas
context.drawImage(video, 0, 0, canvas.width, canvas.height);
// Convert to data URL
const photoData = canvas.toDataURL('image/jpeg', 0.8);
onPhotoTaken(photoData);
setIsCapturing(false);
};
const handleClose = () => {
stopCamera();
onClose();
};
return ( return (
<Dialog open={isOpen} onOpenChange={handleClose}> <Dialog open={isOpen} onOpenChange={onClose}>
<DialogContent className="max-w-lg w-full mx-auto"> <DialogContent className="max-w-lg w-full mx-auto">
<DialogHeader> <DialogHeader>
<DialogTitle className="flex items-center justify-between"> <DialogTitle className="flex items-center justify-between">
<span>Capturer photo {photoType}</span> <span>Capturer photo {photoType}</span>
<Button variant="ghost" size="sm" onClick={handleClose}> <Button variant="ghost" size="sm" onClick={onClose}>
<X className="h-4 w-4" /> <X className="h-4 w-4" />
</Button> </Button>
</DialogTitle> </DialogTitle>
@@ -131,14 +99,14 @@ export default function CameraModal({
<div className="flex justify-center gap-4"> <div className="flex justify-center gap-4">
<Button <Button
onClick={capturePhoto} onClick={onCapture}
disabled={isCapturing} disabled={isCapturing}
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<Camera className="h-4 w-4" /> <Camera className="h-4 w-4" />
{isCapturing ? 'Capture...' : 'Prendre la photo'} {isCapturing ? 'Capture...' : 'Prendre la photo'}
</Button> </Button>
<Button variant="outline" onClick={handleClose}> <Button variant="outline" onClick={onClose}>
Annuler Annuler
</Button> </Button>
</div> </div>
+2 -2
View File
@@ -2,7 +2,7 @@ import { useState } from 'react';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Shield, Flame, Plus, History } from 'lucide-react'; import { Shield, Flame, Plus, History } from 'lucide-react';
import NewSaleForm from './NewSaleForm'; import NewSaleFormMultiProduct from './NewSaleFormMultiProduct';
import SalesHistory from './SalesHistory'; import SalesHistory from './SalesHistory';
import { useAuth } from '@/hooks/useAuth'; import { useAuth } from '@/hooks/useAuth';
import { useStoreContext } from '@/hooks/useStoreContext'; import { useStoreContext } from '@/hooks/useStoreContext';
@@ -68,7 +68,7 @@ export default function FireworksApp() {
</TabsList> </TabsList>
<TabsContent value="nouvelle-vente"> <TabsContent value="nouvelle-vente">
<NewSaleForm /> <NewSaleFormMultiProduct />
</TabsContent> </TabsContent>
<TabsContent value="historique"> <TabsContent value="historique">