From 3d97fb2e29b2bc29cb2d679367ad9e6ca4020ab9 Mon Sep 17 00:00:00 2001 From: mohanmd Date: Wed, 24 Jun 2026 00:42:06 +0530 Subject: [PATCH] dashboard api for employer --- .../Api/EmployerWorkerController.php | 10 +- .../Controllers/Employer/WorkerController.php | 10 +- resources/js/Pages/Employer/Shortlist.jsx | 18 +- resources/js/Pages/Employer/Workers/Index.jsx | 12 +- resources/js/Pages/Employer/Workers/Show.jsx | 179 ++++++++++++++++-- .../js/components/Employer/FilterDrawer.jsx | 30 +-- 6 files changed, 202 insertions(+), 57 deletions(-) diff --git a/app/Http/Controllers/Api/EmployerWorkerController.php b/app/Http/Controllers/Api/EmployerWorkerController.php index 398b3a0..705859f 100644 --- a/app/Http/Controllers/Api/EmployerWorkerController.php +++ b/app/Http/Controllers/Api/EmployerWorkerController.php @@ -92,7 +92,10 @@ private function formatWorker(Worker $w) 'documents' => $w->documents->map(function ($doc) { $ocrData = $doc->ocr_data; if ($doc->type === 'visa' && is_array($ocrData)) { - unset($ocrData['sponsor']); + unset($ocrData['file_number']); + unset($ocrData['name']); + unset($ocrData['passport_number']); + unset($ocrData['accompanied_by']); unset($ocrData['valid_until']); } return [ @@ -1000,7 +1003,10 @@ public function getWorkerDetail(Request $request, $id) 'documents' => $w->documents->map(function ($doc) { $ocrData = $doc->ocr_data; if ($doc->type === 'visa' && is_array($ocrData)) { - unset($ocrData['sponsor']); + unset($ocrData['file_number']); + unset($ocrData['name']); + unset($ocrData['passport_number']); + unset($ocrData['accompanied_by']); unset($ocrData['valid_until']); } return [ diff --git a/app/Http/Controllers/Employer/WorkerController.php b/app/Http/Controllers/Employer/WorkerController.php index e2c0371..8fe0a9f 100644 --- a/app/Http/Controllers/Employer/WorkerController.php +++ b/app/Http/Controllers/Employer/WorkerController.php @@ -390,6 +390,14 @@ public function show($id) 'in_country' => (bool) $w->in_country, 'preferred_location' => $w->preferred_location, 'documents' => $w->documents->map(function ($doc) { + $ocrData = $doc->ocr_data; + if ($doc->type === 'visa' && is_array($ocrData)) { + unset($ocrData['file_number']); + unset($ocrData['name']); + unset($ocrData['passport_number']); + unset($ocrData['accompanied_by']); + unset($ocrData['valid_until']); + } return [ 'id' => $doc->id, 'type' => $doc->type, @@ -398,7 +406,7 @@ public function show($id) 'expiry_date' => $doc->expiry_date, 'ocr_accuracy' => $doc->ocr_accuracy, 'file_path' => $doc->file_path ? url($doc->file_path) : null, - 'ocr_data' => $doc->ocr_data, + 'ocr_data' => $ocrData, ]; })->toArray(), ]; diff --git a/resources/js/Pages/Employer/Shortlist.jsx b/resources/js/Pages/Employer/Shortlist.jsx index 80ca5d0..8f3e113 100644 --- a/resources/js/Pages/Employer/Shortlist.jsx +++ b/resources/js/Pages/Employer/Shortlist.jsx @@ -123,23 +123,7 @@ export default function Shortlist({ shortlistedWorkers }) { {/* Passport Status Verification Badge & Visa Status */}
- {worker.visa_expiry_date ? ( -
- - Visa Exp: {worker.visa_expiry_date} -
- ) : ( -
- - Visa Expiry Pending -
- )} + {/* Removed visa expiry warning badge for cleaner UI */} {worker.visa_status && (worker.visa_status.toLowerCase().includes('tourist') || worker.visa_status.toLowerCase().includes('visit')) ? (
diff --git a/resources/js/Pages/Employer/Workers/Index.jsx b/resources/js/Pages/Employer/Workers/Index.jsx index 0b6edb1..e9faae7 100644 --- a/resources/js/Pages/Employer/Workers/Index.jsx +++ b/resources/js/Pages/Employer/Workers/Index.jsx @@ -704,17 +704,7 @@ export default function Index({ initialWorkers = [], initialShortlistedIds = [], {worker.visa_status}
)} - {worker.document_expiry_status && ( -
- 📅 {worker.document_expiry_status} -
- )} + {/* Removed visa expiry status badge for cleaner UI */}
diff --git a/resources/js/Pages/Employer/Workers/Show.jsx b/resources/js/Pages/Employer/Workers/Show.jsx index 01feb3b..69b3476 100644 --- a/resources/js/Pages/Employer/Workers/Show.jsx +++ b/resources/js/Pages/Employer/Workers/Show.jsx @@ -54,7 +54,7 @@ const getExpiryStatus = (expiryDate) => { if (diffDays < 0) { return { - text: `Expired \${Math.abs(diffDays)} days ago`, + text: 'Expired', color: 'text-rose-700 bg-rose-50 border-rose-200 font-black' }; } else if (diffDays === 0) { @@ -64,7 +64,7 @@ const getExpiryStatus = (expiryDate) => { }; } else { return { - text: `Valid until \${expiryDate} (Expires in \${diffDays} days)`, + text: `Valid until ${expiryDate}`, color: 'text-emerald-700 bg-emerald-50 border-emerald-150 font-black' }; } @@ -83,6 +83,7 @@ const safeRender = (value) => { export default function Show({ worker }) { const { t } = useTranslation(); const passportDoc = worker.documents?.find(d => d.type === 'passport'); + const emiratesIdDoc = worker.documents?.find(d => d.type === 'emirates_id' || d.type === 'eid'); const visaDoc = worker.documents?.find(d => d.type === 'visa'); const [showReportModal, setShowReportModal] = useState(false); @@ -434,8 +435,8 @@ export default function Show({ worker }) { {(() => { const expiryStatus = getExpiryStatus(passportDoc.expiry_date); return ( - - \${expiryStatus.text} + + {expiryStatus.text} ); })()} @@ -474,6 +475,14 @@ export default function Show({ worker }) {
{t('place_of_birth', 'Place of Birth')}
{safeRender(passportDoc.ocr_data.place_of_birth)}
+
+
{t('nationality', 'Nationality')}
+
{safeRender(passportDoc.ocr_data.nationality || worker.nationality)}
+
+
+
{t('issuing_country', 'Issuing Country')}
+
{safeRender(passportDoc.ocr_data.issuing_country)}
+
{t('issue_date', 'Date of Issue')}
{safeRender(passportDoc.ocr_data.date_of_issue || passportDoc.issue_date)}
@@ -483,8 +492,12 @@ export default function Show({ worker }) {
{safeRender(passportDoc.ocr_data.date_of_expiry || passportDoc.expiry_date)}
-
{t('issuing_country', 'Issuing Country')}
-
{safeRender(passportDoc.ocr_data.issuing_country)}
+
{t('authority', 'Authority')}
+
{safeRender(passportDoc.ocr_data.authority)}
+
+
+
{t('document_type', 'Document Type')}
+
{safeRender(passportDoc.ocr_data.document_type || passportDoc.type || 'passport')}
) : ( @@ -537,6 +550,106 @@ export default function Show({ worker }) { )} + {/* Document Card: Emirates ID */} + {emiratesIdDoc && ( +
+
+
+ {t('emirates_id_details', 'Emirates ID Details')} + {(() => { + const expiryStatus = getExpiryStatus(emiratesIdDoc.expiry_date); + return ( + + {expiryStatus.text} + + ); + })()} +
+ {emiratesIdDoc.ocr_accuracy && ( + + {emiratesIdDoc.ocr_accuracy}% Match + + )} +
+ +
+ {emiratesIdDoc.ocr_data ? ( +
+
+
{t('emirates_id_number', 'Emirates ID Number')}
+
{safeRender(emiratesIdDoc.ocr_data.emirates_id_number || emiratesIdDoc.number)}
+
+
+
{t('full_name', 'Full Name')}
+
{safeRender(emiratesIdDoc.ocr_data.name || emiratesIdDoc.ocr_data.full_name)}
+
+
+
{t('nationality', 'Nationality')}
+
{safeRender(emiratesIdDoc.ocr_data.nationality)}
+
+
+
{t('date_of_birth', 'Date of Birth')}
+
{safeRender(emiratesIdDoc.ocr_data.date_of_birth || emiratesIdDoc.ocr_data.dob)}
+
+
+
{t('gender', 'Gender')}
+
{safeRender(emiratesIdDoc.ocr_data.gender || emiratesIdDoc.ocr_data.sex)}
+
+
+
{t('occupation', 'Occupation')}
+
{safeRender(emiratesIdDoc.ocr_data.occupation || emiratesIdDoc.ocr_data.profession)}
+
+
+
{t('employer', 'Employer')}
+
{safeRender(emiratesIdDoc.ocr_data.employer)}
+
+
+
{t('issue_date', 'Issue Date')}
+
{safeRender(emiratesIdDoc.ocr_data.issue_date || emiratesIdDoc.issue_date)}
+
+
+
{t('expiry_date', 'Expiry Date')}
+
{safeRender(emiratesIdDoc.ocr_data.expiry_date || emiratesIdDoc.expiry_date)}
+
+
+ ) : ( +
+
+
{t('document_id', 'Document ID')}
+
{emiratesIdDoc.number}
+
+
+
{t('issue_date', 'Issue')}
+
{emiratesIdDoc.issue_date || 'N/A'}
+
+
+
{t('expiry_date', 'Expiry')}
+
{emiratesIdDoc.expiry_date || 'N/A'}
+
+
+ )} + +
+ + + UAE PDPL Compliant Scan Storage (Permanently Purged) + + {emiratesIdDoc.file_path && ( + + + View Emirates ID Scan + + )} +
+
+
+ )} + {/* Document Card: Visa */} {visaDoc ? (
@@ -544,10 +657,10 @@ export default function Show({ worker }) {
{t('visa_details', 'Entry Visa Details')} {(() => { - const expiryStatus = getExpiryStatus(passportDoc.expiry_date); + const expiryStatus = getExpiryStatus(visaDoc.expiry_date || worker.visa_expiry_date); return ( - - \${expiryStatus.text} + + {expiryStatus.text} ); })()} @@ -576,13 +689,29 @@ export default function Show({ worker }) { return (
+
+
{t('entry_permit_no', 'Entry Permit No')}
+
{safeRender(visaDoc.ocr_data.entry_permit_no)}
+
+
+
{t('uid_no', 'UID No')}
+
{safeRender(visaDoc.ocr_data.uid_no)}
+
+
+
{t('id_number', 'ID Number')}
+
{safeRender(visaDoc.ocr_data.id_number || visaDoc.ocr_data.emirates_id_number)}
+
+
+
{t('visa_type', 'Visa Type')}
+
{safeRender(visaDoc.ocr_data.visa_type || 'ENTRY PERMIT')}
+
{t('profession', 'Profession')}
{safeRender(visaDoc.ocr_data.profession || worker.visa_status)}
{sponsorName && (
-
{t('sponsor', 'Sponsor')}
+
{t('sponsor_name', 'Sponsor Name')}
{safeRender(sponsorName)}
)} @@ -592,20 +721,44 @@ export default function Show({ worker }) {
{safeRender(sponsorMobile)}
)} +
+
{t('nationality', 'Nationality')}
+
{safeRender(visaDoc.ocr_data.nationality || worker.nationality)}
+
+
+
{t('country', 'Country')}
+
{safeRender(visaDoc.ocr_data.country || 'United Arab Emirates')}
+
+
+
{t('date_of_birth', 'Date of Birth')}
+
{safeRender(visaDoc.ocr_data.date_of_birth || visaDoc.ocr_data.dob)}
+
+
+
{t('place_of_birth', 'Place of Birth')}
+
{safeRender(visaDoc.ocr_data.place_of_birth)}
+
{t('place_of_issue', 'Place of Issue')}
{safeRender(visaDoc.ocr_data.place_of_issue)}
-
{t('issue_date', 'Date of Issue')}
+
{t('issue_date', 'Issue Date')}
{safeRender(visaDoc.ocr_data.issue_date || visaDoc.issue_date)}
-
{t('expiry_date', 'Date of Expiry')}
+
{t('expiry_date', 'Expiry Date')}
{safeRender(visaDoc.ocr_data.expiry_date || visaDoc.expiry_date)}
+
+
{t('document_type', 'Document Type')}
+
{safeRender(visaDoc.ocr_data.document_type || visaDoc.type || 'uae_visa')}
+
+
+
{t('ocr_accuracy', 'OCR Accuracy')}
+
{(visaDoc.ocr_accuracy || visaDoc.ocr_data?.ocr_accuracy || 99)}%
+
- ); + ); })() : (
diff --git a/resources/js/components/Employer/FilterDrawer.jsx b/resources/js/components/Employer/FilterDrawer.jsx index 4428b1b..26e7fab 100644 --- a/resources/js/components/Employer/FilterDrawer.jsx +++ b/resources/js/components/Employer/FilterDrawer.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import * as React from 'react'; import { X, RotateCcw, SlidersHorizontal } from 'lucide-react'; /** @@ -14,7 +14,7 @@ import { X, RotateCcw, SlidersHorizontal } from 'lucide-react'; */ export default function FilterDrawer({ open, onClose, onReset, activeCount = 0, title = 'Filters', children }) { // Lock body scroll when drawer is open - useEffect(() => { + React.useEffect(() => { if (open) { document.body.style.overflow = 'hidden'; } else { @@ -24,8 +24,8 @@ export default function FilterDrawer({ open, onClose, onReset, activeCount = 0, }, [open]); // Close on Escape key - useEffect(() => { - const handler = (e) => { if (e.key === 'Escape') onClose(); }; + React.useEffect(() => { + const handler = (e) => { if (e.key === 'Escape' && typeof onClose === 'function') onClose(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [onClose]); @@ -164,14 +164,15 @@ export function FilterInput({ id, value, onChange, placeholder }) { * FilterChips — a row of selectable chip buttons for single-select options. * options: [{ value, label }] */ -export function FilterChips({ options, selected, onChange }) { +export function FilterChips({ options = [], selected, onChange }) { + const opts = options || []; return (
- {options.map(opt => ( + {opts.map(opt => (