diff --git a/apps/backoffice/src/app/features/analytics/pages/AnalyticsPage.tsx b/apps/backoffice/src/app/features/analytics/pages/AnalyticsPage.tsx index ff3d66a02..070d83e42 100644 --- a/apps/backoffice/src/app/features/analytics/pages/AnalyticsPage.tsx +++ b/apps/backoffice/src/app/features/analytics/pages/AnalyticsPage.tsx @@ -1,255 +1,21 @@ -import { - Badge, - Button, - Card, - Divider, - Group, - Paper, - Progress, - SimpleGrid, - Stack, - Table, - Text, - ThemeIcon, - Title, - rem, -} from '@mantine/core'; -import { - IconBook2, - IconCertificate, - IconChartBar, - IconDownload, - IconHeart, - IconId, - IconShieldCheck, - IconTrendingUp, - IconUsers, -} from '@tabler/icons-react'; +import { Container } from '@mantine/core'; +import { FeatureUnavailable } from '@ema-platform/ui'; -// --------------------------------------------------------------------------- -// Mock statistics -// --------------------------------------------------------------------------- -const KPI = [ - { label: 'Total Seafarers', value: 1284, delta: '+12 this month', color: 'blue', icon: IconUsers }, - { label: 'Seaman Books Issued', value: 1102, delta: '85.8% of total', color: 'teal', icon: IconBook2 }, - { label: 'BTC Issued', value: 1098, delta: '85.5% of total', color: 'teal', icon: IconCertificate }, - { label: 'BSID Issued', value: 874, delta: '68.1% of total', color: 'violet', icon: IconId }, - { label: 'CoC / CoP Issued', value: 342, delta: '26.6% of total', color: 'orange', icon: IconShieldCheck }, - { label: 'Medical Expiring (90d)',value: 41, delta: 'Action required', color: 'red', icon: IconHeart }, -]; - -const MONTHLY_APPS = [ - { month: 'Jan', seamanBook: 18, coc: 4 }, - { month: 'Feb', seamanBook: 22, coc: 6 }, - { month: 'Mar', seamanBook: 28, coc: 9 }, - { month: 'Apr', seamanBook: 31, coc: 12 }, - { month: 'May', seamanBook: 26, coc: 8 }, - { month: 'Jun', seamanBook: 35, coc: 14 }, -]; - -const CERT_DISTRIBUTION = [ - { label: 'Seaman Book', count: 1102, pct: 86, color: 'blue' }, - { label: 'Basic Training Cert', count: 1098, pct: 85, color: 'teal' }, - { label: 'BSID', count: 874, pct: 68, color: 'violet' }, - { label: 'CoC / CoP', count: 342, pct: 27, color: 'orange' }, -]; - -const REVENUE = [ - { cert: 'Seaman Book', apps: 1102, feePerApp: 700, revenue: 771400 }, - { cert: 'BTC', apps: 1098, feePerApp: 400, revenue: 439200 }, - { cert: 'BSID', apps: 874, feePerApp: 250, revenue: 218500 }, - { cert: 'CoC / CoP', apps: 342, feePerApp: 1400, revenue: 478800 }, -]; -const TOTAL_REVENUE = REVENUE.reduce((s, r) => s + r.revenue, 0); - -const EXPIRY_WATCH = [ - { cert: 'Seaman Book', expiring30: 8, expiring90: 23, total: 1102 }, - { cert: 'BTC', expiring30: 6, expiring90: 19, total: 1098 }, - { cert: 'Medical Cert', expiring30: 12, expiring90: 41, total: 1284 }, - { cert: 'CoC / CoP', expiring30: 4, expiring90: 17, total: 342 }, -]; - -// --------------------------------------------------------------------------- -// Components -// --------------------------------------------------------------------------- -function KpiCard({ label, value, delta, color, icon: Icon }: typeof KPI[0]) { - return ( - - - - {value.toLocaleString()} - - {label} - {delta} - - ); -} - -// Simple bar chart using divs -function BarChart() { - const max = Math.max(...MONTHLY_APPS.map((m) => m.seamanBook + m.coc)); - return ( - - {MONTHLY_APPS.map((m) => ( - - {m.month} -
-
-
-
- {m.seamanBook + m.coc} - - ))} - -
Seaman Book -
CoC / CoP - - - ); -} - -// --------------------------------------------------------------------------- -// Main page -// --------------------------------------------------------------------------- +/** + * Placeholder until this feature has a backend. + * + * This page previously rendered invented figures/records that were + * indistinguishable from real ones. + */ export function AnalyticsPage() { return ( - - -
- Analytics & Reports - Overview of seafarer registrations, certificate issuance, and revenue — mock data for demonstration -
- -
- - {/* KPIs */} - - {KPI.map((k) => )} - - - - {/* Monthly applications bar chart */} - - - - - Monthly Applications (2025) - - - - - - {/* Certificate distribution */} - - - - Certificate Distribution - - - {CERT_DISTRIBUTION.map((c) => ( -
- - - {c.label} - - - {c.count.toLocaleString()} - {c.pct}% - - - -
- ))} -
-
-
- - {/* Revenue breakdown */} - - - Revenue Breakdown by Certificate Type - Total: ETB {TOTAL_REVENUE.toLocaleString()} - - - - - {['Certificate Type', 'Applications', 'Fee per App', 'Total Revenue', '% of Revenue'].map((h) => ( - {h} - ))} - - - - {REVENUE.map((r) => ( - - {r.cert} - {r.apps.toLocaleString()} - ETB {r.feePerApp.toLocaleString()} - ETB {r.revenue.toLocaleString()} - - - - {((r.revenue / TOTAL_REVENUE) * 100).toFixed(1)}% - - - - ))} - - - - Total - {REVENUE.reduce((s, r) => s + r.apps, 0).toLocaleString()} - - ETB {TOTAL_REVENUE.toLocaleString()} - - - -
-
- - {/* Expiry watchlist */} - - - - Expiry Watchlist - - - - - {['Certificate', 'Total Issued', 'Expiring in 30 days', 'Expiring in 90 days', '% at Risk'].map((h) => ( - {h} - ))} - - - - {EXPIRY_WATCH.map((e) => ( - - {e.cert} - {e.total.toLocaleString()} - - 5 ? 'red' : 'orange'} variant="light" size="sm">{e.expiring30} - - - 20 ? 'orange' : 'yellow'} variant="light" size="sm">{e.expiring90} - - - - 0.1 ? 'red' : 'orange'} - size="sm" - radius="xl" - style={{ flex: 1, minWidth: 60 }} - /> - {((e.expiring90 / e.total) * 100).toFixed(1)}% - - - - ))} - -
-
-
+ + + ); } + +export default AnalyticsPage; diff --git a/apps/backoffice/src/app/features/applications/pages/ApplicationReviewPage.tsx b/apps/backoffice/src/app/features/applications/pages/ApplicationReviewPage.tsx index 1ec7115d0..344cbd856 100644 --- a/apps/backoffice/src/app/features/applications/pages/ApplicationReviewPage.tsx +++ b/apps/backoffice/src/app/features/applications/pages/ApplicationReviewPage.tsx @@ -1,446 +1,21 @@ -import { useState } from 'react'; -import { - ActionIcon, - Alert, - Badge, - Box, - Button, - Card, - Divider, - Group, - Modal, - Paper, - SimpleGrid, - Stack, - Tabs, - Text, - Textarea, - ThemeIcon, - Title, - rem, -} from '@mantine/core'; -import { - IconAlertTriangle, - IconArrowLeft, - IconBook2, - IconCertificate, - IconCheck, - IconCircleCheck, - IconCreditCard, - IconExternalLink, - IconEye, - IconFileDescription, - IconHeart, - IconInfoCircle, - IconShieldCheck, - IconUser, - IconX, -} from '@tabler/icons-react'; -import { useNavigate } from 'react-router-dom'; -import { notify } from '@ema-platform/ui'; +import { Container } from '@mantine/core'; +import { FeatureUnavailable } from '@ema-platform/ui'; -// --------------------------------------------------------------------------- -// Mock data -// --------------------------------------------------------------------------- -const FEES = [ - { group: 'Seaman Book', label: 'Application Fee', amount: 500 }, - { group: 'Seaman Book', label: 'Document Verification Fee', amount: 200 }, - { group: 'Basic Training Certificate (BTC)', label: 'Application Fee', amount: 300 }, - { group: 'Basic Training Certificate (BTC)', label: 'Document Verification Fee',amount: 100 }, - { group: 'BSID', label: 'Application Fee', amount: 100 }, -]; -const TOTAL = FEES.reduce((s, f) => s + f.amount, 0); - -const MOCK_APPLICATION = { - id: 'SB-APP-2025-001', - submittedAt: '2025-05-10', - status: 'pending_review' as const, - applicant: { - fullName: 'Abebe Girma Tadesse', - dob: '1990-04-15', - nationality: 'Ethiopian', - idNumber: 'ID-ETH-2024-001', - phone: '+251 91 234 5678', - email: 'abebe.girma@email.com', - address: 'Bole, Addis Ababa, Ethiopia', - height: '178 cm', - bloodType: 'O+', - hairColor: 'Black', - eyeColor: 'Dark Brown', - seafarerNumber: 'SEAF-2024-00142', - }, - bst: [ - { key: 'pst', short: 'PST', label: 'Personal Survival Techniques', certNumber: 'PST-2024-001', issuer: 'Bahirdar Maritime Training School', issueDate: '2024-01-15', expiryDate: '2029-01-15', fileName: 'pst_cert.pdf', fileType: 'pdf' }, - { key: 'fpff', short: 'FPFF', label: 'Fire Prevention & Fire Fighting', certNumber: 'FPFF-2024-002', issuer: 'Bahirdar Maritime Training School', issueDate: '2024-01-16', expiryDate: '2029-01-16', fileName: 'fpff_cert.pdf', fileType: 'pdf' }, - { key: 'efa', short: 'EFA', label: 'Elementary First Aid', certNumber: 'EFA-2024-003', issuer: 'EMA Training Centre', issueDate: '2024-02-01', expiryDate: '', fileName: 'efa_cert.jpg', fileType: 'image' }, - { key: 'pssr', short: 'PSSR', label: 'Personal Safety & Social Responsibility', certNumber: 'PSSR-2024-004', issuer: 'EMA Training Centre', issueDate: '2024-02-02', expiryDate: '', fileName: 'pssr_cert.jpg', fileType: 'image' }, - { key: 'shpt', short: 'SHPT', label: 'Sexual Harassment Prevention Training', certNumber: 'SHPT-2024-005', issuer: 'EMA Training Centre', issueDate: '2024-02-03', expiryDate: '', fileName: 'shpt_cert.jpg', fileType: 'image' }, - ], - medical: { - certNumber: 'MC-2024-009', - issuer: 'EMA Medical Centre — Addis Ababa', - issueDate: '2024-03-20', - expiryDate: '2026-03-20', - fileName: 'medical_cert.pdf', - fileType: 'pdf', - }, - payment: { - method: 'CBE Bank Transfer', - ref: 'CBE-TXN-20240510-001', - date: '2025-05-10', - status: 'confirmed' as 'confirmed' | 'pending' | 'rejected', - receiptFileName: 'payment_receipt.pdf', - receiptFileType: 'pdf', - }, -}; - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- -const STATUS_COLOR: Record = { - pending_review: 'yellow', approved: 'teal', correction_required: 'orange', rejected: 'red', -}; -const STATUS_LABEL: Record = { - pending_review: 'Pending Review', approved: 'Approved', correction_required: 'Correction Required', rejected: 'Rejected', -}; -const PAYMENT_COLOR = { confirmed: 'teal', pending: 'yellow', rejected: 'red' } as const; - -const DEMO_PDF = 'data:application/pdf;base64,JVBERi0xLjQKJeLjz9MKMSAwIG9iago8PC9UeXBlL0NhdGFsb2cvUGFnZXMgMiAwIFI+PgplbmRvYmoKMiAwIG9iago8PC9UeXBlL1BhZ2VzL0tpZHNbMyAwIFJdL0NvdW50IDE+PgplbmRvYmoKMyAwIG9iago8PC9UeXBlL1BhZ2UvUGFyZW50IDIgMCBSL01lZGlhQm94WzAgMCA2MTIgNzkyXT4+CmVuZG9iagp4cmVmCjAgNAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA1OCAwMDAwMCBuIAowMDAwMDAwMTE1IDAwMDAwIG4gCnRyYWlsZXIKPDwvU2l6ZSA0L1Jvb3QgMSAwIFI+PgpzdGFydHhyZWYKMjIwCiUlRU9G'; - -function getDocUrl(fileName: string) { - return fileName.endsWith('.pdf') ? DEMO_PDF : `https://placehold.co/600x400/e9ecef/6c757d?text=${encodeURIComponent(fileName)}`; -} - -// --------------------------------------------------------------------------- -// Inline document viewer -// --------------------------------------------------------------------------- -function DocViewer({ fileName, fileType, label }: { fileName: string; fileType: string; label: string }) { - const [open, setOpen] = useState(false); - const url = getDocUrl(fileName); - return ( - <> - - - - - -
- {label} - {fileName} -
- {fileType.toUpperCase()} -
- - {fileType === 'pdf' - ?