)
diff --git a/frontend/src/components/WebCam/WebCamComponent.tsx b/frontend/src/components/WebCam/WebCamComponent.tsx
new file mode 100644
index 000000000..784eab3bd
--- /dev/null
+++ b/frontend/src/components/WebCam/WebCamComponent.tsx
@@ -0,0 +1,176 @@
+import { useState, useRef, useCallback } from 'react';
+import { useMutationFeedback } from '../../hooks/useMutationFeedback.tsx';
+import Webcam from 'react-webcam';
+import { X, RotateCcw, Search } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogHeader,
+ DialogTitle,
+} from '@/components/ui/dialog';
+import { useDispatch } from 'react-redux';
+import { startSearch, setResults, clearSearch } from '@/features/searchSlice';
+import type { Image } from '@/types/Media';
+import { usePictoMutation } from '@/hooks/useQueryExtension';
+import { fetchSearchedFacesBase64 } from '@/api/api-functions';
+import { showInfoDialog } from '@/features/infoDialogSlice';
+
+const videoConstraints = {
+ facingMode: 'user',
+};
+
+interface WebcamComponentProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+function WebcamComponent({ isOpen, onClose }: WebcamComponentProps) {
+ const [showCamera, setShowCamera] = useState(true);
+ const [capturedImageUrl, setCapturedImageUrl] = useState
(null);
+ const webcamRef = useRef(null);
+ const dispatch = useDispatch();
+
+ const getSearchImagesBase64 = usePictoMutation({
+ mutationFn: async (base64_data: string) =>
+ fetchSearchedFacesBase64({ base64_data }),
+ });
+
+ useMutationFeedback(getSearchImagesBase64, {
+ showLoading: true,
+ loadingMessage: 'Searching for images...',
+ successTitle: 'Search Complete',
+ successMessage: 'Images matching your search have been found.',
+ errorTitle: 'Search Error',
+ errorMessage: 'Failed to search images. Please try again.',
+ onSuccess: () => {
+ const result = getSearchImagesBase64.data?.data as Image[];
+ if (result && result.length > 0) {
+ dispatch(setResults(result));
+ } else {
+ dispatch(
+ showInfoDialog({
+ title: 'No Match Found',
+ message:
+ 'We couldn’t find any matching faces in your gallery for this photo.',
+ variant: 'info',
+ }),
+ );
+ dispatch(setResults([]));
+ dispatch(clearSearch());
+ }
+ getSearchImagesBase64.reset();
+ },
+ });
+
+ const capture = useCallback(() => {
+ if (webcamRef.current) {
+ const imageSrc = webcamRef.current.getScreenshot();
+ setCapturedImageUrl(imageSrc);
+ setShowCamera(false);
+ }
+ }, [webcamRef]);
+
+ const handleRetake = () => {
+ setCapturedImageUrl(null);
+ setShowCamera(true);
+ };
+
+ const handleSearchCapturedImage = () => {
+ onClose();
+ if (capturedImageUrl) {
+ dispatch(startSearch(capturedImageUrl));
+ getSearchImagesBase64.mutate(capturedImageUrl);
+ } else {
+ dispatch(
+ showInfoDialog({
+ title: 'Capture Failed',
+ message: 'An unexpected error occurred during capture.',
+ variant: 'error',
+ }),
+ );
+ handleClose();
+ }
+ };
+
+ const handleClose = () => {
+ setShowCamera(true);
+ setCapturedImageUrl(null);
+ onClose();
+ };
+
+ return (
+
+ );
+}
+
+export default WebcamComponent;
diff --git a/package.json b/package.json
index c298b862e..61a8a383a 100644
--- a/package.json
+++ b/package.json
@@ -17,4 +17,4 @@
"devDependencies": {
"husky": "^9.1.7"
}
-}
\ No newline at end of file
+}