@mostajs/ticketing 2.0.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/api/detect.route.d.ts +19 -0
  2. package/dist/api/detect.route.d.ts.map +1 -0
  3. package/dist/api/detect.route.js +31 -0
  4. package/dist/api/detect.route.js.map +1 -0
  5. package/dist/api/recognize.route.d.ts +32 -0
  6. package/dist/api/recognize.route.d.ts.map +1 -0
  7. package/dist/api/recognize.route.js +86 -0
  8. package/dist/api/recognize.route.js.map +1 -0
  9. package/dist/components/FaceDetector.d.ts +28 -0
  10. package/dist/components/FaceDetector.d.ts.map +1 -0
  11. package/dist/components/FaceDetector.js +184 -0
  12. package/dist/components/FaceDetector.js.map +1 -0
  13. package/dist/components/ScanResultCard.d.ts +10 -0
  14. package/dist/components/ScanResultCard.d.ts.map +1 -0
  15. package/dist/components/ScanResultCard.js +63 -0
  16. package/dist/components/ScanResultCard.js.map +1 -0
  17. package/dist/components/ScannerView.d.ts +9 -0
  18. package/dist/components/ScannerView.d.ts.map +1 -0
  19. package/dist/components/ScannerView.js +62 -0
  20. package/dist/components/ScannerView.js.map +1 -0
  21. package/dist/hooks/useCamera.d.ts +18 -0
  22. package/dist/hooks/useCamera.d.ts.map +1 -0
  23. package/dist/hooks/useCamera.js +74 -0
  24. package/dist/hooks/useCamera.js.map +1 -0
  25. package/dist/hooks/useFaceDetection.d.ts +22 -0
  26. package/dist/hooks/useFaceDetection.d.ts.map +1 -0
  27. package/dist/hooks/useFaceDetection.js +62 -0
  28. package/dist/hooks/useFaceDetection.js.map +1 -0
  29. package/dist/hooks/useScan.d.ts +9 -0
  30. package/dist/hooks/useScan.d.ts.map +1 -0
  31. package/dist/hooks/useScan.js +101 -0
  32. package/dist/hooks/useScan.js.map +1 -0
  33. package/dist/index.d.ts +13 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +14 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/lib/audio.d.ts +10 -0
  38. package/dist/lib/audio.d.ts.map +1 -0
  39. package/dist/lib/audio.js +29 -0
  40. package/dist/lib/audio.js.map +1 -0
  41. package/dist/lib/face-api.d.ts +24 -0
  42. package/dist/lib/face-api.d.ts.map +1 -0
  43. package/dist/lib/face-api.js +66 -0
  44. package/dist/lib/face-api.js.map +1 -0
  45. package/dist/lib/face-matcher.d.ts +19 -0
  46. package/dist/lib/face-matcher.d.ts.map +1 -0
  47. package/dist/lib/face-matcher.js +53 -0
  48. package/dist/lib/face-matcher.js.map +1 -0
  49. package/dist/lib/face-utils.d.ts +21 -0
  50. package/dist/lib/face-utils.d.ts.map +1 -0
  51. package/dist/lib/face-utils.js +40 -0
  52. package/dist/lib/face-utils.js.map +1 -0
  53. package/dist/pages/ScanPage.d.ts +2 -0
  54. package/dist/pages/ScanPage.d.ts.map +1 -0
  55. package/dist/pages/ScanPage.js +32 -0
  56. package/dist/pages/ScanPage.js.map +1 -0
  57. package/dist/pages/TicketsPage.d.ts +2 -0
  58. package/dist/pages/TicketsPage.d.ts.map +1 -0
  59. package/dist/pages/TicketsPage.js +16 -0
  60. package/dist/pages/TicketsPage.js.map +1 -0
  61. package/dist/register.d.ts.map +1 -1
  62. package/dist/register.js +13 -3
  63. package/dist/register.js.map +1 -1
  64. package/dist/server.d.ts +4 -0
  65. package/dist/server.d.ts.map +1 -1
  66. package/dist/server.js +5 -2
  67. package/dist/server.js.map +1 -1
  68. package/dist/types/index.d.ts +115 -0
  69. package/dist/types/index.d.ts.map +1 -1
  70. package/dist/types/index.js +7 -2
  71. package/dist/types/index.js.map +1 -1
  72. package/i18n/fr/scan.json +25 -0
  73. package/package.json +90 -4
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Configuration for the detect handler.
3
+ */
4
+ export interface DetectHandlerConfig {
5
+ /** Check auth/permission — return null if OK, or a Response to deny */
6
+ checkAuth?: (req: Request) => Promise<Response | null>;
7
+ /** Check if face recognition is enabled (default: always true) */
8
+ isEnabled?: () => Promise<boolean>;
9
+ }
10
+ /**
11
+ * Factory for POST /api/face/detect
12
+ *
13
+ * Placeholder endpoint — face detection runs client-side via face-api.js.
14
+ * This route exists for permission gating and future server-side detection.
15
+ */
16
+ export declare function createDetectHandler(config?: DetectHandlerConfig): {
17
+ POST: (req: Request) => Promise<Response>;
18
+ };
19
+ //# sourceMappingURL=detect.route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.route.d.ts","sourceRoot":"","sources":["../../api/detect.route.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,uEAAuE;IACvE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACtD,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,GAAE,mBAAwB;gBACzC,OAAO;EAyBjC"}
@@ -0,0 +1,31 @@
1
+ // @mostajs/ticketing — Detect API route factory (from @mostajs/face)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ /**
4
+ * Factory for POST /api/face/detect
5
+ *
6
+ * Placeholder endpoint — face detection runs client-side via face-api.js.
7
+ * This route exists for permission gating and future server-side detection.
8
+ */
9
+ export function createDetectHandler(config = {}) {
10
+ async function POST(req) {
11
+ if (config.isEnabled) {
12
+ const enabled = await config.isEnabled();
13
+ if (!enabled) {
14
+ return Response.json({ error: { code: 'FEATURE_DISABLED', message: 'Face recognition is disabled' } }, { status: 403 });
15
+ }
16
+ }
17
+ if (config.checkAuth) {
18
+ const denied = await config.checkAuth(req);
19
+ if (denied)
20
+ return denied;
21
+ }
22
+ return Response.json({
23
+ data: {
24
+ message: 'Face detection runs client-side. Use the FaceDetector component or useFaceDetection hook.',
25
+ hint: 'For recognition, send the descriptor to POST /api/face/recognize',
26
+ },
27
+ });
28
+ }
29
+ return { POST };
30
+ }
31
+ //# sourceMappingURL=detect.route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.route.js","sourceRoot":"","sources":["../../api/detect.route.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,wCAAwC;AAYxC;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,SAA8B,EAAE;IAClE,KAAK,UAAU,IAAI,CAAC,GAAY;QAC9B,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAA;YACxC,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,OAAO,QAAQ,CAAC,IAAI,CAClB,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,8BAA8B,EAAE,EAAE,EAChF,EAAE,MAAM,EAAE,GAAG,EAAE,CAChB,CAAA;YACH,CAAC;QACH,CAAC;QAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;YACrB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;YAC1C,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAA;QAC3B,CAAC;QAED,OAAO,QAAQ,CAAC,IAAI,CAAC;YACnB,IAAI,EAAE;gBACJ,OAAO,EAAE,2FAA2F;gBACpG,IAAI,EAAE,kEAAkE;aACzE;SACF,CAAC,CAAA;IACJ,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,CAAA;AACjB,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Candidate shape: must have an id, faceDescriptor, and any extra fields.
3
+ */
4
+ export interface FaceCandidate {
5
+ id: string;
6
+ faceDescriptor: number[];
7
+ [key: string]: unknown;
8
+ }
9
+ /**
10
+ * Configuration for the recognize handler.
11
+ */
12
+ export interface RecognizeHandlerConfig {
13
+ /** Fetch all active candidates with a faceDescriptor from the database */
14
+ getCandidates: () => Promise<FaceCandidate[]>;
15
+ /** Check auth/permission — return null if OK, or a Response to deny */
16
+ checkAuth?: (req: Request) => Promise<Response | null>;
17
+ /** Get the matching distance threshold (default: 0.6) */
18
+ getThreshold?: () => Promise<number>;
19
+ /** Check if face recognition is enabled (default: always true) */
20
+ isEnabled?: () => Promise<boolean>;
21
+ /** Fields to return from the matched candidate (default: all except faceDescriptor) */
22
+ publicFields?: string[];
23
+ }
24
+ /**
25
+ * Factory for POST /api/face/recognize
26
+ *
27
+ * Receives a 128-float face descriptor, searches candidates, returns best match.
28
+ */
29
+ export declare function createRecognizeHandler(config: RecognizeHandlerConfig): {
30
+ POST: (req: Request) => Promise<Response>;
31
+ };
32
+ //# sourceMappingURL=recognize.route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recognize.route.d.ts","sourceRoot":"","sources":["../../api/recognize.route.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,cAAc,EAAE,MAAM,EAAE,CAAA;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,0EAA0E;IAC1E,aAAa,EAAE,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;IAC7C,uEAAuE;IACvE,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACtD,yDAAyD;IACzD,YAAY,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IACpC,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAClC,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB;gBAC1C,OAAO;EA8FjC"}
@@ -0,0 +1,86 @@
1
+ // @mostajs/ticketing — Recognize API route factory (from @mostajs/face)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ import { findMatch } from '../lib/face-matcher';
4
+ /**
5
+ * Factory for POST /api/face/recognize
6
+ *
7
+ * Receives a 128-float face descriptor, searches candidates, returns best match.
8
+ */
9
+ export function createRecognizeHandler(config) {
10
+ async function POST(req) {
11
+ // Check if enabled
12
+ if (config.isEnabled) {
13
+ const enabled = await config.isEnabled();
14
+ if (!enabled) {
15
+ return Response.json({ error: { code: 'FEATURE_DISABLED', message: 'Face recognition is disabled' } }, { status: 403 });
16
+ }
17
+ }
18
+ // Check auth
19
+ if (config.checkAuth) {
20
+ const denied = await config.checkAuth(req);
21
+ if (denied)
22
+ return denied;
23
+ }
24
+ // Parse body
25
+ let body;
26
+ try {
27
+ body = await req.json();
28
+ }
29
+ catch {
30
+ return Response.json({ error: { code: 'VALIDATION_ERROR', message: 'Invalid JSON' } }, { status: 400 });
31
+ }
32
+ // Validate descriptor
33
+ const desc = body.faceDescriptor;
34
+ if (!Array.isArray(desc) ||
35
+ desc.length !== 128 ||
36
+ !desc.every((v) => typeof v === 'number')) {
37
+ return Response.json({ error: { code: 'VALIDATION_ERROR', message: 'faceDescriptor must be an array of 128 numbers' } }, { status: 400 });
38
+ }
39
+ // Get candidates
40
+ const candidates = await config.getCandidates();
41
+ if (candidates.length === 0) {
42
+ return Response.json({
43
+ data: { match: false, message: 'No candidates with face data' },
44
+ });
45
+ }
46
+ // Find match
47
+ const threshold = config.getThreshold ? await config.getThreshold() : 0.6;
48
+ const result = findMatch(desc, candidates, threshold);
49
+ if (result) {
50
+ // Strip faceDescriptor from response
51
+ const { faceDescriptor: _fd, ...publicData } = result.match;
52
+ const filtered = config.publicFields
53
+ ? Object.fromEntries(Object.entries(publicData).filter(([k]) => config.publicFields.includes(k) || k === 'id'))
54
+ : publicData;
55
+ return Response.json({
56
+ data: {
57
+ match: true,
58
+ distance: Math.round(result.distance * 1000) / 1000,
59
+ candidate: filtered,
60
+ },
61
+ });
62
+ }
63
+ // No match — still report best distance for debugging
64
+ let bestDistance = null;
65
+ for (const c of candidates) {
66
+ if (!c.faceDescriptor || c.faceDescriptor.length !== 128)
67
+ continue;
68
+ let sum = 0;
69
+ for (let i = 0; i < 128; i++) {
70
+ const diff = desc[i] - c.faceDescriptor[i];
71
+ sum += diff * diff;
72
+ }
73
+ const d = Math.sqrt(sum);
74
+ if (bestDistance === null || d < bestDistance)
75
+ bestDistance = d;
76
+ }
77
+ return Response.json({
78
+ data: {
79
+ match: false,
80
+ distance: bestDistance !== null ? Math.round(bestDistance * 1000) / 1000 : null,
81
+ },
82
+ });
83
+ }
84
+ return { POST };
85
+ }
86
+ //# sourceMappingURL=recognize.route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recognize.route.js","sourceRoot":"","sources":["../../api/recognize.route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ export interface FaceDetectorProps {
2
+ /** Existing photo (base64) */
3
+ photo: string;
4
+ /** Callback when a photo is captured */
5
+ onCapture: (data: {
6
+ photo: string;
7
+ faceDescriptor: number[] | null;
8
+ }) => void;
9
+ /** Callback when photo is cleared */
10
+ onClear: () => void;
11
+ /** Verification mode: compare against existing descriptor */
12
+ verifyDescriptor?: number[];
13
+ /** Callback with verification result */
14
+ onVerifyResult?: (result: {
15
+ match: boolean;
16
+ distance: number;
17
+ } | null) => void;
18
+ /** Enable face detection (default: true) */
19
+ enabled?: boolean;
20
+ /** Match threshold (default: 0.6) */
21
+ threshold?: number;
22
+ /** Require face detected before capture (default: true) */
23
+ requireForCapture?: boolean;
24
+ /** Error callback instead of console.error */
25
+ onError?: (message: string) => void;
26
+ }
27
+ export default function FaceDetector({ photo, onCapture, onClear, verifyDescriptor, onVerifyResult, enabled, threshold, requireForCapture, onError, }: FaceDetectorProps): import("react/jsx-runtime").JSX.Element;
28
+ //# sourceMappingURL=FaceDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FaceDetector.d.ts","sourceRoot":"","sources":["../../components/FaceDetector.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,iBAAiB;IAChC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,wCAAwC;IACxC,SAAS,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;KAAE,KAAK,IAAI,CAAA;IAC7E,qCAAqC;IACrC,OAAO,EAAE,MAAM,IAAI,CAAA;IACnB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,wCAAwC;IACxC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAA;IAC9E,4CAA4C;IAC5C,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2DAA2D;IAC3D,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,8CAA8C;IAC9C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACpC;AAED,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,EACnC,KAAK,EACL,SAAS,EACT,OAAO,EACP,gBAAgB,EAChB,cAAc,EACd,OAAc,EACd,SAAe,EACf,iBAAwB,EACxB,OAAO,GACR,EAAE,iBAAiB,2CAiTnB"}
@@ -0,0 +1,184 @@
1
+ // @mostajs/ticketing — FaceDetector component (from @mostajs/face)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { useState, useRef, useCallback, useEffect } from 'react';
6
+ import { loadModels, detectFace, extractDescriptor } from '../lib/face-api';
7
+ import { compareFaces } from '../lib/face-matcher';
8
+ import { drawDetection } from '../lib/face-utils';
9
+ export default function FaceDetector({ photo, onCapture, onClear, verifyDescriptor, onVerifyResult, enabled = true, threshold = 0.6, requireForCapture = true, onError, }) {
10
+ const videoRef = useRef(null);
11
+ const canvasRef = useRef(null);
12
+ const overlayRef = useRef(null);
13
+ const animFrameRef = useRef(0);
14
+ const [streaming, setStreaming] = useState(false);
15
+ const [modelsReady, setModelsReady] = useState(false);
16
+ const [loadingModels, setLoadingModels] = useState(false);
17
+ const [faceDetected, setFaceDetected] = useState(false);
18
+ const [verifyResult, setVerifyResult] = useState(null);
19
+ const reportError = useCallback((msg) => {
20
+ if (onError)
21
+ onError(msg);
22
+ else
23
+ console.error('[FaceDetector]', msg);
24
+ }, [onError]);
25
+ // Load face-api models on mount
26
+ useEffect(() => {
27
+ if (!enabled)
28
+ return;
29
+ let cancelled = false;
30
+ async function init() {
31
+ setLoadingModels(true);
32
+ try {
33
+ await loadModels();
34
+ if (!cancelled)
35
+ setModelsReady(true);
36
+ }
37
+ catch (err) {
38
+ console.error('Face-api model loading error:', err);
39
+ if (!cancelled)
40
+ reportError('Impossible de charger les modeles de detection faciale');
41
+ }
42
+ finally {
43
+ if (!cancelled)
44
+ setLoadingModels(false);
45
+ }
46
+ }
47
+ init();
48
+ return () => { cancelled = true; };
49
+ }, [enabled, reportError]);
50
+ // Real-time detection loop
51
+ const detectLoop = useCallback(async () => {
52
+ if (!videoRef.current || !overlayRef.current)
53
+ return;
54
+ if (videoRef.current.paused || videoRef.current.ended)
55
+ return;
56
+ const video = videoRef.current;
57
+ const overlay = overlayRef.current;
58
+ const detection = await detectFace(video);
59
+ setFaceDetected(!!detection);
60
+ if (!videoRef.current || !overlayRef.current)
61
+ return;
62
+ drawDetection(overlay, detection, video.videoWidth, video.videoHeight);
63
+ animFrameRef.current = requestAnimationFrame(detectLoop);
64
+ }, []);
65
+ const startCamera = useCallback(async () => {
66
+ setVerifyResult(null);
67
+ try {
68
+ const stream = await navigator.mediaDevices.getUserMedia({
69
+ video: { width: 320, height: 240, facingMode: 'user' },
70
+ });
71
+ if (!videoRef.current) {
72
+ stream.getTracks().forEach((track) => track.stop());
73
+ return;
74
+ }
75
+ videoRef.current.srcObject = stream;
76
+ await videoRef.current.play().catch(() => { });
77
+ setStreaming(true);
78
+ if (enabled && modelsReady) {
79
+ videoRef.current.onloadeddata = () => {
80
+ animFrameRef.current = requestAnimationFrame(detectLoop);
81
+ };
82
+ }
83
+ }
84
+ catch {
85
+ reportError("Impossible d'acceder a la camera");
86
+ }
87
+ }, [enabled, modelsReady, detectLoop, reportError]);
88
+ const stopCamera = useCallback(() => {
89
+ cancelAnimationFrame(animFrameRef.current);
90
+ if (videoRef.current?.srcObject) {
91
+ const tracks = videoRef.current.srcObject.getTracks();
92
+ tracks.forEach((track) => track.stop());
93
+ videoRef.current.srcObject = null;
94
+ }
95
+ setStreaming(false);
96
+ setFaceDetected(false);
97
+ }, []);
98
+ // Cleanup on unmount
99
+ useEffect(() => {
100
+ return () => {
101
+ cancelAnimationFrame(animFrameRef.current);
102
+ const video = videoRef.current;
103
+ if (video) {
104
+ video.pause();
105
+ const stream = video.srcObject;
106
+ if (stream) {
107
+ stream.getTracks().forEach((track) => track.stop());
108
+ }
109
+ video.srcObject = null;
110
+ video.removeAttribute('src');
111
+ video.load();
112
+ }
113
+ };
114
+ }, []);
115
+ const capturePhoto = useCallback(async () => {
116
+ if (!videoRef.current || !canvasRef.current)
117
+ return;
118
+ const canvas = canvasRef.current;
119
+ const video = videoRef.current;
120
+ canvas.width = video.videoWidth;
121
+ canvas.height = video.videoHeight;
122
+ const ctx = canvas.getContext('2d');
123
+ if (!ctx)
124
+ return;
125
+ ctx.drawImage(video, 0, 0);
126
+ const dataUrl = canvas.toDataURL('image/jpeg', 0.8);
127
+ let descriptor = null;
128
+ if (enabled && modelsReady) {
129
+ try {
130
+ const raw = await extractDescriptor(canvas);
131
+ if (raw)
132
+ descriptor = Array.from(raw);
133
+ }
134
+ catch (err) {
135
+ console.error('Descriptor extraction error:', err);
136
+ }
137
+ }
138
+ stopCamera();
139
+ onCapture({ photo: dataUrl, faceDescriptor: descriptor });
140
+ }, [enabled, modelsReady, stopCamera, onCapture]);
141
+ const verifyFace = useCallback(async () => {
142
+ if (!videoRef.current || !verifyDescriptor)
143
+ return;
144
+ try {
145
+ const raw = await extractDescriptor(videoRef.current);
146
+ if (!raw) {
147
+ setVerifyResult(null);
148
+ onVerifyResult?.(null);
149
+ reportError('Aucun visage detecte');
150
+ return;
151
+ }
152
+ const distance = compareFaces(raw, verifyDescriptor);
153
+ const match = distance < threshold;
154
+ const result = { match, distance };
155
+ setVerifyResult(result);
156
+ onVerifyResult?.(result);
157
+ }
158
+ catch (err) {
159
+ console.error('Face verification error:', err);
160
+ reportError('Erreur lors de la verification');
161
+ }
162
+ }, [verifyDescriptor, onVerifyResult, threshold, reportError]);
163
+ const captureDisabled = enabled && requireForCapture && !faceDetected;
164
+ // Basic webcam mode (face detection disabled)
165
+ if (!enabled) {
166
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [photo ? (_jsxs("div", { style: { position: 'relative' }, children: [_jsx("img", { src: photo, alt: "Photo", style: { width: '100%', borderRadius: '0.5rem' } }), _jsx("button", { type: "button", onClick: onClear, style: { position: 'absolute', top: 8, right: 8, background: '#ef4444', color: 'white', border: 'none', borderRadius: '50%', width: 24, height: 24, cursor: 'pointer', fontSize: 14, lineHeight: 1 }, children: "x" })] })) : (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsx("video", { ref: videoRef, style: { width: '100%', borderRadius: '0.5rem', display: streaming ? 'block' : 'none' }, autoPlay: true, playsInline: true, muted: true }), streaming ? (_jsxs("div", { style: { display: 'flex', gap: '0.5rem' }, children: [_jsx("button", { type: "button", onClick: capturePhoto, style: { flex: 1, padding: '0.5rem', cursor: 'pointer' }, children: "Capturer" }), _jsx("button", { type: "button", onClick: stopCamera, style: { padding: '0.5rem', cursor: 'pointer' }, children: "x" })] })) : (_jsx("button", { type: "button", onClick: startCamera, style: { width: '100%', padding: '0.5rem', cursor: 'pointer' }, children: "Prendre photo" }))] })), _jsx("canvas", { ref: canvasRef, style: { display: 'none' } })] }));
167
+ }
168
+ // Full face detection mode
169
+ return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '1rem' }, children: [photo ? (_jsxs("div", { style: { position: 'relative' }, children: [_jsx("img", { src: photo, alt: "Photo", style: { width: '100%', borderRadius: '0.5rem' } }), _jsx("button", { type: "button", onClick: onClear, style: { position: 'absolute', top: 8, right: 8, background: '#ef4444', color: 'white', border: 'none', borderRadius: '50%', width: 24, height: 24, cursor: 'pointer', fontSize: 14, lineHeight: 1 }, children: "x" })] })) : (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: '0.5rem' }, children: [_jsxs("div", { style: { position: 'relative', display: streaming ? 'block' : 'none' }, children: [_jsx("video", { ref: videoRef, style: { width: '100%', borderRadius: '0.5rem' }, autoPlay: true, playsInline: true, muted: true }), _jsx("canvas", { ref: overlayRef, style: { position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', pointerEvents: 'none' } }), _jsx("div", { style: {
170
+ position: 'absolute', bottom: 8, left: 8,
171
+ padding: '2px 8px', borderRadius: 4, fontSize: 12, fontWeight: 500,
172
+ background: faceDetected ? 'rgba(34,197,94,0.9)' : 'rgba(239,68,68,0.9)',
173
+ color: 'white',
174
+ }, children: faceDetected ? 'Visage detecte' : 'Aucun visage' })] }), streaming ? (_jsxs("div", { style: { display: 'flex', gap: '0.5rem' }, children: [verifyDescriptor ? (_jsx("button", { type: "button", onClick: verifyFace, disabled: !faceDetected, style: { flex: 1, padding: '0.5rem', cursor: faceDetected ? 'pointer' : 'not-allowed', opacity: faceDetected ? 1 : 0.5 }, children: "Verifier visage" })) : (_jsx("button", { type: "button", onClick: capturePhoto, disabled: captureDisabled, style: { flex: 1, padding: '0.5rem', cursor: captureDisabled ? 'not-allowed' : 'pointer', opacity: captureDisabled ? 0.5 : 1 }, children: captureDisabled ? 'Cadrez votre visage...' : 'Capturer' })), _jsx("button", { type: "button", onClick: stopCamera, style: { padding: '0.5rem', cursor: 'pointer' }, children: "x" })] })) : (_jsx("div", { children: loadingModels ? (_jsx("button", { type: "button", disabled: true, style: { width: '100%', padding: '0.5rem', opacity: 0.5 }, children: "Chargement detection faciale..." })) : (_jsx("button", { type: "button", onClick: startCamera, style: { width: '100%', padding: '0.5rem', cursor: 'pointer' }, children: verifyDescriptor ? 'Verifier visage' : 'Prendre photo' })) }))] })), verifyResult && (_jsx("div", { style: {
175
+ display: 'flex', alignItems: 'center', gap: '0.5rem',
176
+ padding: '0.75rem', borderRadius: '0.5rem', fontSize: 14, fontWeight: 500,
177
+ background: verifyResult.match ? '#f0fdf4' : '#fef2f2',
178
+ color: verifyResult.match ? '#15803d' : '#b91c1c',
179
+ border: `1px solid ${verifyResult.match ? '#bbf7d0' : '#fecaca'}`,
180
+ }, children: verifyResult.match
181
+ ? `Visage verifie (confiance: ${Math.round((1 - verifyResult.distance) * 100)}%)`
182
+ : `Visage non reconnu (distance: ${verifyResult.distance.toFixed(2)})` })), _jsx("canvas", { ref: canvasRef, style: { display: 'none' } })] }));
183
+ }
184
+ //# sourceMappingURL=FaceDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FaceDetector.js","sourceRoot":"","sources":["../../components/FaceDetector.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { ScanResultCardProps } from '../types/index';
2
+ /**
3
+ * Displays the result of a ticket scan (granted, denied, or reentry).
4
+ */
5
+ export default function ScanResultCard({ data, t, renderExtra, }: ScanResultCardProps): import("react/jsx-runtime").JSX.Element;
6
+ /** Empty state placeholder when no scan result yet */
7
+ export declare function ScanEmptyState({ message }: {
8
+ message?: string;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ //# sourceMappingURL=ScanResultCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScanResultCard.d.ts","sourceRoot":"","sources":["../../components/ScanResultCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,mBAAmB,EAAkB,MAAM,gBAAgB,CAAA;AAazE;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EACrC,IAAI,EACJ,CAAgB,EAChB,WAAW,GACZ,EAAE,mBAAmB,2CA+GrB;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,2CAe/D"}
@@ -0,0 +1,63 @@
1
+ // @mostajs/ticketing — ScanResultCard component (from @mostajs/scan)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { ScanLine } from 'lucide-react';
6
+ const colors = {
7
+ granted: { bg: '#f0fdf4', text: '#15803d', border: '#22c55e', badge: '#166534' },
8
+ reentry: { bg: '#eff6ff', text: '#1d4ed8', border: '#3b82f6', badge: '#1e40af' },
9
+ denied: { bg: '#fef2f2', text: '#dc2626', border: '#ef4444', badge: '#991b1b' },
10
+ };
11
+ function getColors(data) {
12
+ if (data.result === 'granted')
13
+ return data.isReentry ? colors.reentry : colors.granted;
14
+ return colors.denied;
15
+ }
16
+ /**
17
+ * Displays the result of a ticket scan (granted, denied, or reentry).
18
+ */
19
+ export default function ScanResultCard({ data, t = (key) => key, renderExtra, }) {
20
+ const c = getColors(data);
21
+ return (_jsx("div", { style: {
22
+ border: `2px solid ${c.border}`,
23
+ borderRadius: 8,
24
+ overflow: 'hidden',
25
+ }, children: _jsxs("div", { style: { padding: 24 }, children: [_jsxs("div", { style: {
26
+ textAlign: 'center',
27
+ padding: 24,
28
+ borderRadius: 8,
29
+ backgroundColor: c.bg,
30
+ }, children: [_jsx("div", { style: { fontSize: 40, fontWeight: 700, marginBottom: 8, color: c.text }, children: data.result === 'granted' ? (data.isReentry ? '\u{1f504}' : '\u{2705}') : '\u{274c}' }), _jsx("div", { style: { fontSize: 24, fontWeight: 700, color: c.text }, children: data.isReentry
31
+ ? t('scan.result.reentry')
32
+ : t(`scan.result.${data.result}`) }), data.isReentry && (_jsx("div", { style: { fontSize: 14, marginTop: 4, color: c.text }, children: t('scan.result.reentryHint') })), data.reason && (_jsx("div", { style: { fontSize: 14, marginTop: 8, color: colors.denied.text }, children: data.reason.startsWith('ticket_') || data.reason.startsWith('quota_') ||
33
+ data.reason.startsWith('access_') || data.reason.startsWith('client_') ||
34
+ data.reason === 'invalid_ticket'
35
+ ? t(`scan.denyReasons.${data.reason}`)
36
+ : data.reason }))] }), data.client && (_jsxs("div", { style: { marginTop: 16 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [data.client.photo ? (_jsx("img", { src: data.client.photo, alt: "", style: {
37
+ width: 48, height: 48, borderRadius: '50%', objectFit: 'cover',
38
+ } })) : (_jsx("div", { style: {
39
+ width: 48, height: 48, borderRadius: '50%',
40
+ backgroundColor: '#e5e7eb',
41
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
42
+ fontSize: 18, fontWeight: 700, color: '#6b7280',
43
+ }, children: data.client.name.charAt(0) })), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700 }, children: data.client.name }), _jsx("div", { style: { fontSize: 14, color: '#6b7280' }, children: data.client.clientNumber })] })] }), data.ticket && (_jsxs("div", { style: { fontSize: 14, marginTop: 12 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 4 }, children: [_jsx("span", { style: { color: '#6b7280' }, children: t('scan.info.activity') }), _jsx("span", { style: { fontWeight: 500 }, children: data.ticket.activityName })] }), data.access && data.access.remainingQuota != null && (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 4 }, children: [_jsx("span", { style: { color: '#6b7280' }, children: t('scan.info.quotaRemaining') }), _jsx("span", { style: { fontWeight: 700, fontSize: 18 }, children: data.access.remainingQuota })] })), data.ticket.ticketType === 'cadeau' && (_jsx("span", { style: {
44
+ display: 'inline-block',
45
+ padding: '2px 8px',
46
+ borderRadius: 4,
47
+ backgroundColor: '#fef3c7',
48
+ color: '#92400e',
49
+ fontSize: 12,
50
+ fontWeight: 500,
51
+ }, children: t('tickets.types.cadeau') }))] })), renderExtra?.(data)] }))] }) }));
52
+ }
53
+ /** Empty state placeholder when no scan result yet */
54
+ export function ScanEmptyState({ message }) {
55
+ return (_jsxs("div", { style: {
56
+ border: '1px solid #e5e7eb',
57
+ borderRadius: 8,
58
+ padding: '48px 24px',
59
+ textAlign: 'center',
60
+ color: '#9ca3af',
61
+ }, children: [_jsx(ScanLine, { style: { width: 48, height: 48, margin: '0 auto 16px' } }), _jsx("p", { children: message || 'Scan a QR ticket to verify access' })] }));
62
+ }
63
+ //# sourceMappingURL=ScanResultCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScanResultCard.js","sourceRoot":"","sources":["../../components/ScanResultCard.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ScannerViewProps } from '../types/index';
2
+ /**
3
+ * Self-contained QR scanner view with start/stop controls.
4
+ *
5
+ * Renders a camera view + controls. When a QR code is detected,
6
+ * it calls the configured API endpoint and fires `onResult`.
7
+ */
8
+ export default function ScannerView({ apiEndpoint, onResult, onError, startLabel, stopLabel, soundFrequencies, }: ScannerViewProps): import("react/jsx-runtime").JSX.Element;
9
+ //# sourceMappingURL=ScannerView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScannerView.d.ts","sourceRoot":"","sources":["../../components/ScannerView.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAEtD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAyB,EACzB,QAAQ,EACR,OAAO,EACP,UAA4B,EAC5B,SAAkB,EAClB,gBAAgB,GACjB,EAAE,gBAAgB,2CAyFlB"}
@@ -0,0 +1,62 @@
1
+ // @mostajs/ticketing — ScannerView component (from @mostajs/scan)
2
+ // Author: Dr Hamid MADANI drmdh@msn.com
3
+ 'use client';
4
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
5
+ import { ScanLine, Camera, RefreshCw } from 'lucide-react';
6
+ import { useScan } from '../hooks/useScan';
7
+ /**
8
+ * Self-contained QR scanner view with start/stop controls.
9
+ *
10
+ * Renders a camera view + controls. When a QR code is detected,
11
+ * it calls the configured API endpoint and fires `onResult`.
12
+ */
13
+ export default function ScannerView({ apiEndpoint = '/api/scan', onResult, onError, startLabel = 'Start Scanner', stopLabel = 'Stop', soundFrequencies, }) {
14
+ const { scanning, result, startScanner, stopScanner } = useScan({
15
+ apiEndpoint,
16
+ onResult,
17
+ onError,
18
+ soundFrequencies,
19
+ });
20
+ return (_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(ScanLine, { style: { width: 20, height: 20 } }), _jsx("span", { className: "font-semibold", children: "Scanner" })] }), _jsx("div", { id: "qr-reader", style: {
21
+ width: '100%',
22
+ minHeight: 300,
23
+ backgroundColor: '#111827',
24
+ borderRadius: 8,
25
+ overflow: 'hidden',
26
+ } }), _jsxs("div", { style: { marginTop: 16, display: 'flex', gap: 8 }, children: [!scanning ? (_jsxs("button", { onClick: startScanner, style: {
27
+ flex: 1,
28
+ display: 'flex',
29
+ alignItems: 'center',
30
+ justifyContent: 'center',
31
+ gap: 8,
32
+ padding: '10px 16px',
33
+ backgroundColor: '#0284c7',
34
+ color: 'white',
35
+ border: 'none',
36
+ borderRadius: 6,
37
+ fontSize: 14,
38
+ fontWeight: 500,
39
+ cursor: 'pointer',
40
+ }, children: [_jsx(Camera, { style: { width: 16, height: 16 } }), startLabel] })) : (_jsx("button", { onClick: stopScanner, style: {
41
+ flex: 1,
42
+ padding: '10px 16px',
43
+ backgroundColor: 'white',
44
+ color: '#374151',
45
+ border: '1px solid #d1d5db',
46
+ borderRadius: 6,
47
+ fontSize: 14,
48
+ fontWeight: 500,
49
+ cursor: 'pointer',
50
+ }, children: stopLabel })), result && (_jsx("button", { onClick: startScanner, style: {
51
+ display: 'flex',
52
+ alignItems: 'center',
53
+ justifyContent: 'center',
54
+ padding: '10px 12px',
55
+ backgroundColor: 'white',
56
+ color: '#374151',
57
+ border: '1px solid #d1d5db',
58
+ borderRadius: 6,
59
+ cursor: 'pointer',
60
+ }, children: _jsx(RefreshCw, { style: { width: 16, height: 16 } }) }))] })] }));
61
+ }
62
+ //# sourceMappingURL=ScannerView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScannerView.js","sourceRoot":"","sources":["../../components/ScannerView.tsx"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,wCAAwC;AACxC,YAAY,CAAA;;AAEZ,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAG1C;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,EAClC,WAAW,GAAG,WAAW,EACzB,QAAQ,EACR,OAAO,EACP,UAAU,GAAG,eAAe,EAC5B,SAAS,GAAG,MAAM,EAClB,gBAAgB,GACC;IACjB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;QAC9D,WAAW;QACX,QAAQ;QACR,OAAO;QACP,gBAAgB;KACjB,CAAC,CAAA;IAEF,OAAO,CACL,0BACE,eAAK,SAAS,EAAC,8BAA8B,aAC3C,KAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAI,EAC9C,eAAM,SAAS,EAAC,eAAe,wBAAe,IAC1C,EAEN,cACE,EAAE,EAAC,WAAW,EACd,KAAK,EAAE;oBACL,KAAK,EAAE,MAAM;oBACb,SAAS,EAAE,GAAG;oBACd,eAAe,EAAE,SAAS;oBAC1B,YAAY,EAAE,CAAC;oBACf,QAAQ,EAAE,QAAQ;iBACnB,GACD,EAEF,eAAK,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,aACnD,CAAC,QAAQ,CAAC,CAAC,CAAC,CACX,kBACE,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE;4BACL,IAAI,EAAE,CAAC;4BACP,OAAO,EAAE,MAAM;4BACf,UAAU,EAAE,QAAQ;4BACpB,cAAc,EAAE,QAAQ;4BACxB,GAAG,EAAE,CAAC;4BACN,OAAO,EAAE,WAAW;4BACpB,eAAe,EAAE,SAAS;4BAC1B,KAAK,EAAE,OAAO;4BACd,MAAM,EAAE,MAAM;4BACd,YAAY,EAAE,CAAC;4BACf,QAAQ,EAAE,EAAE;4BACZ,UAAU,EAAE,GAAG;4BACf,MAAM,EAAE,SAAS;yBAClB,aAED,KAAC,MAAM,IAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAI,EAC3C,UAAU,IACJ,CACV,CAAC,CAAC,CAAC,CACF,iBACE,OAAO,EAAE,WAAW,EACpB,KAAK,EAAE;4BACL,IAAI,EAAE,CAAC;4BACP,OAAO,EAAE,WAAW;4BACpB,eAAe,EAAE,OAAO;4BACxB,KAAK,EAAE,SAAS;4BAChB,MAAM,EAAE,mBAAmB;4BAC3B,YAAY,EAAE,CAAC;4BACf,QAAQ,EAAE,EAAE;4BACZ,UAAU,EAAE,GAAG;4BACf,MAAM,EAAE,SAAS;yBAClB,YAEA,SAAS,GACH,CACV,EAEA,MAAM,IAAI,CACT,iBACE,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE;4BACL,OAAO,EAAE,MAAM;4BACf,UAAU,EAAE,QAAQ;4BACpB,cAAc,EAAE,QAAQ;4BACxB,OAAO,EAAE,WAAW;4BACpB,eAAe,EAAE,OAAO;4BACxB,KAAK,EAAE,SAAS;4BAChB,MAAM,EAAE,mBAAmB;4BAC3B,YAAY,EAAE,CAAC;4BACf,MAAM,EAAE,SAAS;yBAClB,YAED,KAAC,SAAS,IAAC,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAI,GACxC,CACV,IACG,IACF,CACP,CAAA;AACH,CAAC"}