Table component is added and visitor management screen is added

This commit is contained in:
2026-07-23 12:50:44 +05:30
parent 89e93366e1
commit 1993d91319
7 changed files with 1204 additions and 857 deletions

View File

@@ -1,397 +1,317 @@
import { useState } from 'react';
import {
Pencil,
Menu,
ShieldCheck,
FileText,
QrCode,
Search,
Plus,
AlertTriangle,
Clock,
ShieldCheck,
Target,
Download,
LayoutGrid,
} from "lucide-react";
Power,
import Table from "@/utils/dataTable";
import InfoBanner from "@/utils/infoBanner";
import Button from "@/utils/button";
import NavigationTabs from "@/utils/tabs";
import { equipmentTabs } from "@/constants/constant";
import { useState } from "react";
Check,
const headers = [
"Code",
"Equipment",
"Group",
"Block · Floor",
"Service Provider",
"Criticality",
"PPM Status",
"Actions",
];
Sparkles,
const Badge = ({
text,
color,
}: {
text: string;
color: string;
}) => (
<span
className={`inline-flex items-center rounded-full px-3 py-1 text-[10px] font-bold uppercase tracking-wider ${color}`}
>
<span className="mr-1.5 h-1.5 w-1.5 rounded-full bg-current"></span>
{text}
</span>
);
} from 'lucide-react';
const ActionButtons = () => (
<div className="flex justify-center gap-1.5">
<button className="rounded-md border border-slate-700/60 p-1.5 text-slate-400 hover:text-white hover:bg-slate-800 transition">
<FileText size={14} />
</button>
import { mockData } from '@/constants/constant';
<button className="rounded-md border border-slate-700/60 p-1.5 text-slate-400 hover:text-white hover:bg-slate-800 transition">
<Pencil size={14} />
</button>
<button className="rounded-md border border-slate-700/60 p-1.5 text-slate-400 hover:text-white hover:bg-slate-800 transition">
<LayoutGrid size={14} />
</button>
</div>
);
export default function EquipmentPortal() {
const [activeTab, setActiveTab] = useState<string>("Site-Wise Equipment");
export default function LocationTable(){
const [selectedIds, setSelectedIds] = useState<string[]>(['1']); // Row 1 selected by default (teal indicator bar)
const toggleSelectAll = () => {
if (selectedIds.length === mockData.length) {
setSelectedIds([]);
} else {
setSelectedIds(mockData.map((row) => row.id));
}
};
const toggleSelectRow = (id: string) => {
setSelectedIds((prev) =>
prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
);
};
const rows = [
[
<span className="font-semibold text-teal-400">APP-1100</span>,
<span className="font-bold text-slate-100">Cooling Tower</span>,
"Electrical",
"Block 1 · 2",
"Blue Star",
"Important",
<Badge text="Working" color="bg-amber-950/60 text-amber-400 border border-amber-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1103</span>,
<span className="font-bold text-slate-100">Air Handling Unit</span>,
"Plumbing",
"Tower A · 1",
"Blue Star",
<Badge text="Critical" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<Badge text="Completed" color="bg-emerald-950/60 text-emerald-400 border border-emerald-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1106</span>,
<span className="font-bold text-slate-100">VRV Outdoor</span>,
"Electrical",
"Tower A · G",
"Voltas",
<Badge text="Critical" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<Badge text="Completed" color="bg-emerald-950/60 text-emerald-400 border border-emerald-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1109</span>,
<span className="font-bold text-slate-100">VRV Outdoor</span>,
"HVAC",
"Tower A · 1",
"Voltas",
"Normal",
<Badge text="Overdue" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1112</span>,
<span className="font-bold text-slate-100">Air Handling Unit</span>,
"HVAC",
"Tower A · G",
"In-house",
<Badge text="Critical" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<Badge text="Completed" color="bg-emerald-950/60 text-emerald-400 border border-emerald-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1115</span>,
<span className="font-bold text-slate-100">Air Handling Unit</span>,
"Electrical",
"Tower A · 2",
"Voltas",
<Badge text="Critical" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<Badge text="Completed" color="bg-emerald-950/60 text-emerald-400 border border-emerald-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1118</span>,
<span className="font-bold text-slate-100">Chiller</span>,
"Electrical",
"Block 1 · 1",
"In-house",
"Important",
<Badge text="Overdue" color="bg-rose-950/60 text-rose-400 border border-rose-800/30" />,
<ActionButtons />,
],
[
<span className="font-semibold text-teal-400">APP-1121</span>,
<span className="font-bold text-slate-100">Cooling Tower</span>,
"Plumbing",
"Tower A · 1",
"Blue Star",
"Normal",
<Badge text="Completed" color="bg-emerald-950/60 text-emerald-400 border border-emerald-800/30" />,
<ActionButtons />,
],
];
return (
<div className="w-full bg-[#071321] text-slate-300 rounded-xl overflow-hidden border border-slate-800/80 shadow-2xl relative font-sans">
<div className="overflow-x-auto">
<table className="w-full text-left text-xs border-collapse">
{/* TABLE HEADER */}
<thead>
<tr className="border-b border-slate-800/90 text-[11px] font-bold tracking-wider text-slate-500 uppercase font-mono">
<th className="py-4 px-4 w-12 text-center">
<input
type="checkbox"
checked={selectedIds.length === mockData.length}
onChange={toggleSelectAll}
className="rounded border-slate-700 bg-slate-900/80 text-teal-400 focus:ring-0 focus:ring-offset-0 cursor-pointer accent-teal-400 h-3.5 w-3.5"
/>
</th>
<th className="py-4 px-3 font-mono">BLOCK</th>
<th className="py-4 px-3 font-mono">FLOOR</th>
<th className="py-4 px-3 font-mono">ZONE</th>
<th className="py-4 px-3 font-mono">DEPARTMENT</th>
<th className="py-4 px-3 font-mono">LOCATION</th>
<th className="py-4 px-3 font-mono">WINDOW</th>
<th className="py-4 px-3 font-mono">TAT</th>
<th className="py-4 px-3 font-mono">SCHED.</th>
<th className="py-4 px-3 font-mono">APPROVAL</th>
<th className="py-4 px-3 font-mono">STATUS</th>
<th className="py-4 px-4 font-mono text-right pr-6">ACTIONS</th>
</tr>
</thead>
{/* TABLE BODY */}
<tbody className="divide-y divide-slate-800/50">
{mockData.map((row) => {
const isSelected = selectedIds.includes(row.id);
return (
<tr
key={row.id}
className={`group relative transition-colors ${
isSelected
? 'bg-[#0e2238]/60 hover:bg-[#122942]/80'
: 'hover:bg-slate-800/30'
}`}
>
{/* Left Selection Indicator Bar (for Row 1 / Selected Rows) */}
<td className="py-3 px-4 text-center relative">
{isSelected && (
<span className="absolute left-0 top-0 bottom-0 w-[3px] bg-teal-400 rounded-r" />
)}
<input
type="checkbox"
checked={isSelected}
onChange={() => toggleSelectRow(row.id)}
className="rounded border-slate-700 bg-slate-900/80 text-teal-400 focus:ring-0 focus:ring-offset-0 cursor-pointer accent-teal-400 h-3.5 w-3.5"
/>
</td>
{/* Block */}
<td className="py-3 px-3 font-semibold text-slate-100 whitespace-nowrap">
{row.block}
</td>
{/* Floor */}
<td className="py-3 px-3 font-medium text-slate-200">
{row.floor}
</td>
{/* Zone */}
<td className="py-3 px-3 font-medium text-slate-300">
{row.zone}
</td>
{/* Department */}
<td className="py-3 px-3 font-semibold text-slate-200 whitespace-nowrap">
{row.department}
</td>
{/* Location */}
<td className="py-3 px-3 font-extrabold text-white whitespace-nowrap">
{row.location}
</td>
{/* Window */}
<td className="py-3 px-3 font-mono text-slate-400 text-[11px] whitespace-nowrap">
{row.window}
</td>
{/* TAT */}
<td className="py-3 px-3 font-mono text-slate-200 font-medium">
{row.tat}
</td>
{/* Sched */}
<td className="py-3 px-3 font-mono text-slate-200 font-medium">
{row.sched}
</td>
{/* Approval */}
<td className="py-3 px-3 whitespace-nowrap">
{row.approval ? (
<span className="inline-flex items-center gap-1.5 text-emerald-400 font-semibold">
<Check size={14} className="stroke-[3]" />
<span>Yes</span>
</span>
) : (
<span className="text-slate-500 font-mono"></span>
)}
</td>
{/* Status Badge */}
<td className="py-3 px-3 whitespace-nowrap">
{row.status === 'ACTIVE' ? (
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-bold font-mono tracking-wider bg-emerald-950/80 border border-emerald-800/40 text-emerald-400">
<span className="h-1.5 w-1.5 rounded-full bg-emerald-400"></span>
ACTIVE
</span>
) : (
<span className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[10px] font-bold font-mono tracking-wider bg-slate-800/80 border border-slate-700/50 text-slate-400">
<span className="h-1.5 w-1.5 rounded-full bg-slate-400"></span>
INACTIVE
</span>
)}
</td>
{/* Action Icon Buttons */}
<td className="py-3 px-4 text-right whitespace-nowrap pr-6">
<div className="inline-flex items-center gap-1.5">
<button
title="Edit"
className="p-1.5 rounded-md bg-[#0b1b2d] hover:bg-slate-700/60 border border-slate-800 text-slate-400 hover:text-slate-200 transition-colors"
>
<Pencil size={13} />
</button>
<button
title="Options"
className="p-1.5 rounded-md bg-[#0b1b2d] hover:bg-slate-700/60 border border-slate-800 text-slate-400 hover:text-slate-200 transition-colors"
>
<Menu size={13} />
</button>
<button
title="Security"
className="p-1.5 rounded-md bg-[#0b1b2d] hover:bg-slate-700/60 border border-slate-800 text-slate-400 hover:text-slate-200 transition-colors"
>
<ShieldCheck size={13} />
</button>
<button
title="QR Code"
className="p-1.5 rounded-md bg-[#0b1b2d] hover:bg-slate-700/60 border border-slate-800 text-slate-400 hover:text-slate-200 transition-colors"
>
<QrCode size={13} />
</button>
<button
title="Toggle Power"
className="p-1.5 rounded-md bg-[#0b1b2d] hover:bg-slate-700/60 border border-slate-800 text-slate-400 hover:text-slate-200 transition-colors"
>
<Power size={13} />
</button>
</div>
</td>
</tr>
);
})}
</tbody>
</table>
<div className="my-15 text-slate-100 font-sans ">
<div className="flex items-center gap-2 text-[10px]
font-bold tracking-widest text-slate-500 uppercase font-mono">
<span className="text-teal-400">Apple One HQ Tower</span>
<span></span>
<span>Operations</span>
<span></span>
<span>Equipment</span>
</div>
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
Equipment & maintenance
</h1>
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl mb-10 leading-relaxed">
Asset register, preventive (PPM) and
contract (AMC) maintenance, scoped to the active tenant.
</p>
{/* Navigation Tabs */}
<NavigationTabs
tabs={equipmentTabs}
activeTab={activeTab}
onTabChange={setActiveTab}
className="mb-6"
/>
{/* --- STAT CARDS (4 CARDS MATCHING SCREENSHOT) --- */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{/* Total equipment */}
<div className="bg-[#0b1727] border border-teal-500/50 rounded-xl p-4 flex flex-col justify-between relative shadow-lg">
<div className="flex items-center justify-between">
<div className="p-2.5 rounded-lg bg-teal-950/60 border border-teal-800/40 text-teal-400">
<Target size={18} />
</div>
<span className="text-xs font-mono text-slate-400">4 groups</span>
</div>
<div className="mt-4">
<div className="text-3xl font-bold text-white tracking-tight">8</div>
<div className="text-xs text-slate-400 mt-1 font-medium">Total equipment</div>
</div>
</div>
{/* Critical assets */}
<div className="bg-[#0b1727] border border-slate-800/80 rounded-xl p-4 flex flex-col justify-between relative">
<div className="flex items-center justify-between">
<div className="p-2.5 rounded-lg bg-rose-950/50 border border-rose-800/40 text-rose-400">
<AlertTriangle size={18} />
</div>
<span className="text-xs font-mono text-rose-400 font-semibold">priority</span>
</div>
<div className="mt-4">
<div className="text-3xl font-bold text-white tracking-tight">4</div>
<div className="text-xs text-slate-400 mt-1 font-medium">Critical assets</div>
</div>
</div>
{/* PPM attention */}
<div className="bg-[#0b1727] border border-slate-800/80 rounded-xl p-4 flex flex-col justify-between relative">
<div className="flex items-center justify-between">
<div className="p-2.5 rounded-lg bg-amber-950/50 border border-amber-800/40 text-amber-400">
<Clock size={18} />
</div>
<span className="text-xs font-mono text-amber-400 font-semibold">due / overdue</span>
</div>
<div className="mt-4">
<div className="text-3xl font-bold text-white tracking-tight">3</div>
<div className="text-xs text-slate-400 mt-1 font-medium">PPM attention</div>
</div>
</div>
{/* On plan */}
<div className="bg-[#0b1727] border border-slate-800/80 rounded-xl p-4 flex flex-col justify-between relative">
<div className="flex items-center justify-between">
<div className="p-2.5 rounded-lg bg-emerald-950/50 border border-emerald-800/40 text-emerald-400">
<ShieldCheck size={18} />
</div>
<span className="text-xs font-mono text-emerald-400 font-semibold">healthy</span>
</div>
<div className="mt-4">
<div className="text-3xl font-bold text-white tracking-tight">5</div>
<div className="text-xs text-slate-400 mt-1 font-medium">On plan</div>
</div>
</div>
</div>
{/* --- ROW 2: SEARCH, FILTERS & ACTION BUTTONS --- */}
<div className="flex flex-col md:flex-row items-center justify-between gap-3 mb-6">
{/* Search & Group Dropdown */}
<div className="flex flex-1 flex-wrap items-center gap-3 w-full md:w-auto">
{/* Search Bar */}
<div className="relative flex-1 min-w-[260px] max-w-md">
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<input
type="text"
placeholder="Search code, name, block, provider..."
className="w-full bg-[#0b1727] border border-slate-800 rounded-lg pl-9 pr-4 py-2 text-xs text-slate-200 placeholder-slate-500 focus:outline-none focus:border-teal-500/50 transition-colors"
/>
</div>
{/* All groups dropdown */}
<select className="bg-[#0b1727] border-2 border-teal-500/80 rounded-xl px-4 py-1.5 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none transition-all">
<option value="all">All groups</option>
<option value="hvac">HVAC</option>
<option value="plumbing">Plumbing</option>
<option value="electrical">Electrical</option>
<option value="fire">Fire</option>
</select>
</div>
{/* Action Buttons Right Side */}
<div className="flex items-center gap-2 w-full md:w-auto justify-end flex-wrap">
{/* All QR */}
<Button className="flex items-center gap-1.5 bg-[#0b1727] hover:bg-slate-800/80 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
<QrCode size={14} />
<span>All QR</span>
</Button>
{/* Download */}
<Button className="flex items-center gap-1.5 bg-[#0b1727] hover:bg-slate-800/80 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
<Download size={14} />
<span>Download</span>
</Button>
{/* Bulk upload */}
<Button className="flex items-center gap-1.5 bg-[#0b1727] hover:bg-slate-800/80 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
<LayoutGrid size={14} />
<span>Bulk upload</span>
</Button>
{/* Add equipment */}
<Button className="flex items-center gap-1.5 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-4 py-2 text-xs transition-colors">
<Plus size={16} className="stroke-[3]" />
<span>Add equipment</span>
</Button>
</div>
</div>
{/* Data Table */}
<div className="mt-4">
<Table headers={headers} rows={rows} />
</div>
{/* Info Banner */}
<div className="my-8">
<InfoBanner
boldText="Tip:"
normalText="click any asset to open its record — Manufacture, PPM, AMC, Request, QR, Attachment, and History Card. Use Bulk upload to import hundreds of assets from Excel/CSV, auto-linked to site, block, floor & group."
/>
</div>
</div>
);
}
}

View File

@@ -1,31 +1,312 @@
import { useState } from 'react';
import { useState } from 'react';
import {
Layers,
ShieldCheck,
CheckSquare,
Clock,
Search,
QrCode,
QrCode,
Power,
List,
Pencil,
Plus,
} from 'lucide-react';
import Button from "@/utils/button";
import StatCard from "@/utils/dashStatCard";
import LocationTable from '@/utils/dataTable';
import Table from "@/utils/dataTable";
import NavigationTabs from '@/utils/tabs';
import { helpdeskTtabs } from '@/constants/constant';
const StatusBadge = ({
text,
color,
}: {
text: string;
color: string;
}) => (
<span
className={`inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wider whitespace-nowrap ${color}`}
>
<span className="h-1.5 w-1.5 rounded-full bg-current" />
{text}
</span>
);
const ActionButtons = ({ active }: { active: boolean }) => (
<div className="flex justify-center items-center gap-1">
<Button className="flex h-7 w-7 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<Pencil size={13} />
</Button>
<Button className="flex h-7 w-7 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<List size={13} />
</Button>
<Button className="flex h-7 w-7 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<ShieldCheck size={13} />
</Button>
<Button className="flex h-7 w-7 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<QrCode size={13} />
</Button>
<Button
className={`flex h-7 w-7 items-center justify-center rounded-md border border-slate-700/60 hover:bg-slate-800 transition ${
active ? "text-red-400" : "text-emerald-400"
}`}
>
<Power size={13} />
</Button>
</div>
);
const headers = [
"",
"Block",
"Floor",
"Zone",
"Department",
"Location",
"Window",
"TAT",
"Sched.",
"Approval",
"Status",
"Actions",
];
const checkbox = (
<input
type="checkbox"
className="h-4 w-4 appearance-none rounded border border-slate-600 bg-slate-700 checked:bg-slate-500 checked:border-slate-500 cursor-pointer"
/>
);
const rows = [
[
checkbox,
"Block 2",
"1",
"Z2",
"HVAC",
<span className="font-semibold text-slate-100 whitespace-nowrap">Parking P1</span>,
<span className="font-mono text-xs whitespace-nowrap">01:5618:00</span>,
"60m",
"4",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Inactive"
color="bg-slate-700/50 text-slate-400"
/>,
<ActionButtons active={false} />,
],
[
checkbox,
"Block 1",
"2",
"Z1",
"Inspection work",
<span className="font-semibold text-slate-100 whitespace-nowrap">Cafeteria</span>,
<span className="font-mono text-xs whitespace-nowrap">01:5623:59</span>,
"360m",
"6",
<span className="text-slate-500"></span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 2",
"G",
"N",
"Inspection work",
<span className="font-semibold text-slate-100 whitespace-nowrap">Main lobby</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0023:59</span>,
"360m",
"4",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 1",
"2",
"Z2",
"Electrical",
<span className="font-semibold text-slate-100 whitespace-nowrap">Reception</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0018:00</span>,
"360m",
"4",
<span className="text-slate-500"></span>,
<StatusBadge
text="Inactive"
color="bg-slate-700/50 text-slate-400"
/>,
<ActionButtons active={false} />,
],
[
checkbox,
"Block 2",
"3",
"Z1",
"Housekeeping",
<span className="font-semibold text-slate-100 whitespace-nowrap">Cafeteria</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0018:00</span>,
"20m",
"8",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 1",
"3",
"Z1",
"Electrical",
<span className="font-semibold text-slate-100 whitespace-nowrap">Food court</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0017:30</span>,
"20m",
"8",
<span className="text-slate-500"></span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 2",
"1",
"Z2",
"HVAC",
<span className="font-semibold text-slate-100 whitespace-nowrap">Parking P1</span>,
<span className="font-mono text-xs whitespace-nowrap">01:5618:00</span>,
"60m",
"4",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Inactive"
color="bg-slate-700/50 text-slate-400"
/>,
<ActionButtons active={false} />,
],
[
checkbox,
"Block 1",
"2",
"Z1",
"Inspection work",
<span className="font-semibold text-slate-100 whitespace-nowrap">Cafeteria</span>,
<span className="font-mono text-xs whitespace-nowrap">01:5623:59</span>,
"360m",
"6",
<span className="text-slate-500"></span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 2",
"G",
"N",
"Inspection work",
<span className="font-semibold text-slate-100 whitespace-nowrap">Main lobby</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0023:59</span>,
"360m",
"4",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 1",
"2",
"Z2",
"Electrical",
<span className="font-semibold text-slate-100 whitespace-nowrap">Reception</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0018:00</span>,
"360m",
"4",
<span className="text-slate-500"></span>,
<StatusBadge
text="Inactive"
color="bg-slate-700/50 text-slate-400"
/>,
<ActionButtons active={false} />,
],
[
checkbox,
"Block 2",
"3",
"Z1",
"Housekeeping",
<span className="font-semibold text-slate-100 whitespace-nowrap">Cafeteria</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0018:00</span>,
"20m",
"8",
<span className="text-emerald-400 font-semibold whitespace-nowrap"> Yes</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
[
checkbox,
"Block 1",
"3",
"Z1",
"Electrical",
<span className="font-semibold text-slate-100 whitespace-nowrap">Food court</span>,
<span className="font-mono text-xs whitespace-nowrap">08:0017:30</span>,
"20m",
"8",
<span className="text-slate-500"></span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons active />,
],
];
export default function HelpDeskPortal() {
const [activeTab, setActiveTab] = useState<string>('Location');
return (
<div className="my-15 text-slate-100 max-w-7xl mx-auto font-sans">
<div className="my-15 px-4 text-slate-100 font-sans">
{/* --- HEADER SECTION --- */}
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-3 mb-4">
<div>
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
<span className="text-teal-400">Apple One HQ Tower</span>
@@ -34,123 +315,118 @@ export default function HelpDeskPortal() {
<span></span>
<span>Help Desk</span>
</div>
<h1 className="text-2xl font-extrabold text-white tracking-tight mt-2">
<h1 className="text-2xl font-extrabold text-white tracking-tight mt-1.5">
Help Desk & checklists
</h1>
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
<p className="text-sm font-medium text-slate-400 mt-1 max-w-xl leading-relaxed">
Scheduled location and equipment inspections, missed-checklist recovery, and HSQE incident management.
</p>
</div>
</div>
<div>
{/* Reusable Navigation Tabs */}
<NavigationTabs
tabs={helpdeskTtabs}
activeTab={activeTab}
onTabChange={setActiveTab}
className="mb-6"
/>
{/* Rest of your page content */}
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{/* Total Checklists */}
<StatCard
variant="icon"
title="Total checklists"
value="10"
icon={Layers}
badgeText="Location set"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-slate-800/60 border-slate-700/50 text-slate-300"
/>
{/* Active */}
<StatCard
variant="icon"
title="Active"
value="7"
icon={ShieldCheck}
badgeText="of 10"
badgeClassName="text-teal-400 font-mono font-medium"
iconContainerClassName="bg-emerald-950/60 border-emerald-800/40 text-emerald-400"
/>
<div>
{/* Reusable Navigation Tabs */}
<NavigationTabs
tabs={helpdeskTtabs}
activeTab={activeTab}
onTabChange={setActiveTab}
className="mb-4"
/>
</div>
{/* Approval Set */}
<StatCard
variant="icon"
title="Approval set"
value="5"
icon={CheckSquare}
badgeText="workflow"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-500"
/>
{/* Avg TAT */}
<StatCard
variant="icon"
title="Avg TAT"
value="40m"
icon={Clock}
badgeText="target"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-blue-950/50 border-blue-800/30 text-blue-400"
/>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
{/* Total Checklists */}
<StatCard
variant="icon"
title="Total checklists"
value="10"
icon={Layers}
badgeText="Location set"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-slate-800/60 border-slate-700/50 text-slate-300"
/>
{/* Active */}
<StatCard
variant="icon"
title="Active"
value="7"
icon={ShieldCheck}
badgeText="of 10"
badgeClassName="text-teal-400 font-mono font-medium"
iconContainerClassName="bg-emerald-950/60 border-emerald-800/40 text-emerald-400"
/>
{/* Approval Set */}
<StatCard
variant="icon"
title="Approval set"
value="5"
icon={CheckSquare}
badgeText="workflow"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-500"
/>
{/* Avg TAT */}
<StatCard
variant="icon"
title="Avg TAT"
value="40m"
icon={Clock}
badgeText="target"
badgeClassName="text-slate-500 lowercase font-mono"
iconContainerClassName="bg-blue-950/50 border-blue-800/30 text-blue-400"
/>
</div>
{/* --- ROW 2: SEARCH, FILTERS & ACTION BUTTONS --- */}
<div className="flex flex-col md:flex-row items-center justify-between gap-4 mb-8">
<div className="flex flex-col md:flex-row items-center justify-between gap-3 mb-4">
{/* Search & Select Filters */}
<div className="flex flex-1 flex-wrap items-center gap-3 w-full md:w-auto">
<div className="flex flex-1 flex-wrap items-center gap-2.5 w-full md:w-auto">
{/* Search Bar */}
<div className="relative flex-1 min-w-[240px] max-w-md">
<Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<div className="relative flex-1 min-w-[200px] max-w-md">
<Search size={16} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
<input
type="text"
placeholder="Search location, block, department..."
className="w-full bg-[#0b1727] border border-slate-800 rounded-lg pl-9 pr-4 py-2 text-xs text-slate-200 placeholder-slate-500 focus:outline-none focus:border-teal-500/50 transition-colors"
className="w-full bg-[#0b1727] border border-slate-800 rounded-lg pl-9 pr-3 py-1.5 text-xs text-slate-200 placeholder-slate-500 focus:outline-none focus:border-teal-500/50 transition-colors"
/>
</div>
{/* Block Dropdown */}
<select className="bg-[#081321] border border-slate-800/80 rounded-xl px-4 py-2 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none focus:ring-2 focus:ring-[#2dd4bf] focus:ring-offset-2 focus:ring-offset-[#081321] transition-all">
<option value="all">All blocks</option>
<option value="block 1">Block 1</option>
<option value="block 2">Block 2</option>
</select>
<select className="bg-[#081321] border border-slate-800/80 rounded-lg px-3 py-1.5 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none focus:ring-2 focus:ring-[#2dd4bf] focus:ring-offset-2 focus:ring-offset-[#081321] transition-all">
<option value="all">All blocks</option>
<option value="block 1">Block 1</option>
<option value="block 2">Block 2</option>
</select>
{/* Status Dropdown (Active State matching the screenshot) */}
<select className="bg-[#081321] border border-transparent ring-2 ring-[#2dd4bf] ring-offset-2 ring-offset-[#081321] rounded-xl px-4 py-2 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none transition-all">
<option value="all">All status</option>
<option value="active">Active</option>
<option value="inactive">InActive</option>
</select>
{/* Status Dropdown */}
<select className="bg-[#081321] border border-transparent ring-2 ring-[#2dd4bf] ring-offset-2 ring-offset-[#081321] rounded-lg px-3 py-1.5 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none transition-all">
<option value="all">All status</option>
<option value="active">Active</option>
<option value="inactive">InActive</option>
</select>
</div>
{/* Action Buttons */}
<div className="flex items-center gap-3 w-full md:w-auto justify-end">
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
<QrCode size={15} />
<div className="flex items-center gap-2 w-full md:w-auto justify-end">
<Button className="flex items-center gap-1.5 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3 py-1.5 text-xs font-semibold transition-colors">
<QrCode size={14} />
<span>All QR</span>
</Button>
<Button className="flex items-center gap-2 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-4 py-2 text-xs transition-colors">
<Plus size={16} className="stroke-[3]" />
<Button className="flex items-center gap-1.5 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-3.5 py-1.5 text-xs transition-colors">
<Plus size={15} className="stroke-[3]" />
<span>New Location Checklist</span>
</Button>
</div>
</div>
<LocationTable/>
{/* --- TABLE CONTAINER --- */}
<div className="mt-4 overflow-x-auto">
<Table headers={headers} rows={rows} />
</div>
</div>
);
}
}

View File

@@ -5,6 +5,190 @@ import LocationTable from '@/utils/dataTable';
import NavigationTabs from '@/utils/tabs';
import { Reqeusttabs,statusFilters } from '@/constants/constant';
import FilterTabs from '@/utils/roundedFilters';
import Table from '@/utils/dataTable';
const StatusBadge = ({
text,
color,
}: {
text: string;
color: string;
}) => (
<span
className={`inline-flex items-center gap-1 rounded-full px-3 py-1 text-[10px] font-semibold uppercase tracking-wider whitespace-nowrap ${color}`}
>
<span className="h-1.5 w-1.5 rounded-full bg-current" />
{text}
</span>
);
const headers = [
"Request No.",
"Department",
"Issue",
"Location",
"Assignee",
"Source",
"Status",
];
const rows = [
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1009</span>,
"Electrical",
"Lighting out",
"Block 1 · G",
"ME Local",
<span className="font-mono">Line</span>,
<StatusBadge
text="Overdue"
color="bg-red-500/15 text-red-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1016</span>,
"HVAC",
"AHU not cooling",
"Lobby · 1",
"Dhananjay T.",
<span className="font-mono">Walk-in</span>,
<StatusBadge
text="Registered"
color="bg-blue-500/15 text-blue-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1023</span>,
"HVAC",
"Thermostat fault",
"Block 1 · 1",
"ME Local",
<span className="font-mono">Mobile app</span>,
<StatusBadge
text="Assigned"
color="bg-blue-500/15 text-blue-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1030</span>,
"HVAC",
"Thermostat fault",
"Block 1 · 1",
"Dhananjay T.",
<span className="font-mono">Mobile app</span>,
<StatusBadge
text="Working"
color="bg-amber-500/15 text-amber-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1037</span>,
"Civil",
"Glass break",
"Block 1 · 1",
"Local Helpdesk",
<span className="font-mono">Walk-in</span>,
<StatusBadge
text="Completed"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1044</span>,
"HVAC",
"Chiller alarm",
"Block 1 · 1",
"Dhananjay T.",
<span className="font-mono">Mobile app</span>,
<StatusBadge
text="Overdue"
color="bg-red-500/15 text-red-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1051</span>,
"Plumbing",
"Water leakage",
"Lobby · 1",
"Local Helpdesk",
<span className="font-mono">Walk-in</span>,
<StatusBadge
text="Registered"
color="bg-blue-500/15 text-blue-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1058</span>,
"Civil",
"Glass break",
"Block 2 · 2",
"Dhananjay T.",
<span className="font-mono">Walk-in</span>,
<StatusBadge
text="Assigned"
color="bg-blue-500/15 text-blue-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1065</span>,
"Civil",
"Ceiling crack",
"Lobby · G",
"ME Local",
<span className="font-mono">Line</span>,
<StatusBadge
text="Working"
color="bg-amber-500/15 text-amber-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1072</span>,
"Plumbing",
"Water leakage",
"Block 2 · G",
"TECH Local",
<span className="font-mono">Line</span>,
<StatusBadge
text="Completed"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1079</span>,
"Electrical",
"DB switch fault",
"Tower A · 1",
"TECH Local",
<span className="font-mono">Mobile app</span>,
<StatusBadge
text="Overdue"
color="bg-red-500/15 text-red-400"
/>,
],
[
<span className="font-semibold text-[#2EE6C5]">TFM/AP/2026/1086</span>,
"HVAC",
"AHU not cooling",
"Lobby · 3",
"TECH Local",
<span className="font-mono">Walk-in</span>,
<StatusBadge
text="Registered"
color="bg-blue-500/15 text-blue-400"
/>,
],
];
export default function ServiceRequestsPortal() {
const [activeTab, setActiveTab] = useState<string>('TFM Requests');
@@ -36,7 +220,9 @@ export default function ServiceRequestsPortal() {
</div>
{/* --- NAVIGATION TABS & TOP ACTION BUTTONS --- */}
<div className="flex flex-col md:flex-row md:items-center justify-between border-b border-slate-800/80 mb-6 gap-4">
<div className="flex flex-col
md:flex-row md:items-center justify-between
border-b border-slate-800/80 mb-4 gap-4">
{/* Navigation Tabs */}
<NavigationTabs
tabs={Reqeusttabs}
@@ -64,7 +250,10 @@ export default function ServiceRequestsPortal() {
activeFilter={activeFilter}
onChange={setActiveFilter}
/>
<LocationTable/>
<div className='mt-5'>
<Table headers={headers} rows={rows}/>
</div>
</div>
);
}

View File

@@ -0,0 +1,279 @@
import { useState } from 'react';
import {
Star,
ShieldCheck,
Activity,
Clock,
BarChart2,
Plus
} from "lucide-react";
import Button from "@/utils/button";
import StatCard from "@/utils/dashStatCard";
import InfoBanner from "@/utils/infoBanner";
import FilterTabs from '@/utils/roundedFilters';
import { visitorFilters } from '@/constants/constant';
import Table from '@/utils/dataTable';
export default function VistiorManagementPortal() {
const headers = [
"Pass",
"Visitor",
"Company",
"Host",
"Purpose",
"Type",
"In",
"Out",
"Status",
"",
];
const Badge = ({
text,
color,
}: {
text: string;
color: string;
}) => (
<span
className={`inline-flex items-center rounded-full px-3 py-1 text-[10px] font-bold uppercase tracking-wider ${color}`}
>
<span className="mr-1.5 h-1.5 w-1.5 rounded-full bg-current"></span>
{text}
</span>
);
const rows = [
[
<span className="font-semibold text-[#2EE6C5]">VIS-4145</span>,
<span className="font-semibold text-slate-100">Rahul Mehta</span>,
"Voltas Ltd",
"Dhananjay T.",
"Maintenance visit",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Contractor</span>,
"09:12",
"—",
<Badge text="Checked-In" color="bg-emerald-500/15 text-emerald-400" />,
<Button className="text-teal-400 font-semibold hover:text-teal-300">Check out</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4146</span>,
<span className="font-semibold text-slate-100">Anita Desai</span>,
"Siemens",
"ME Local",
"Interview",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Interview</span>,
"—",
"—",
<Badge text="Expected" color="bg-blue-500/15 text-blue-400" />,
<Button className="text-emerald-400 font-semibold hover:text-emerald-300">Check in</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4147</span>,
<span className="font-semibold text-slate-100">Karthik R.</span>,
"Schindler",
"Site FM Lead",
"Client meeting",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Guest</span>,
"08:05",
"10:40",
<Badge text="Checked-Out" color="bg-slate-700 text-slate-400" />,
<span className="text-slate-500"></span>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4148</span>,
<span className="font-semibold text-slate-100">Priya Nair</span>,
"Amazon Logistics",
"Estates Office",
"Material delivery",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Delivery</span>,
"—",
"—",
<Badge text="Pre-Reg" color="bg-yellow-500/15 text-yellow-400" />,
<Button className="text-emerald-400 font-semibold hover:text-emerald-300">Check in</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4149</span>,
<span className="font-semibold text-slate-100">Imran Q.</span>,
"FreshMenu",
"TECH Local",
"AMC service",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Contractor</span>,
"10:30",
"—",
<Badge text="Checked-In" color="bg-emerald-500/15 text-emerald-400" />,
<Button className="text-teal-400 font-semibold hover:text-teal-300">Check out</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4145</span>,
<span className="font-semibold text-slate-100">Rahul Mehta</span>,
"Voltas Ltd",
"Dhananjay T.",
"Maintenance visit",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Contractor</span>,
"09:12",
"—",
<Badge text="Checked-In" color="bg-emerald-500/15 text-emerald-400" />,
<Button className="text-teal-400 font-semibold hover:text-teal-300">Check out</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4146</span>,
<span className="font-semibold text-slate-100">Anita Desai</span>,
"Siemens",
"ME Local",
"Interview",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Interview</span>,
"—",
"—",
<Badge text="Expected" color="bg-blue-500/15 text-blue-400" />,
<Button className="text-emerald-400 font-semibold hover:text-emerald-300">Check in</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4147</span>,
<span className="font-semibold text-slate-100">Karthik R.</span>,
"Schindler",
"Site FM Lead",
"Client meeting",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Guest</span>,
"08:05",
"10:40",
<Badge text="Checked-Out" color="bg-slate-700 text-slate-400" />,
<span className="text-slate-500"></span>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4148</span>,
<span className="font-semibold text-slate-100">Priya Nair</span>,
"Amazon Logistics",
"Estates Office",
"Material delivery",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Delivery</span>,
"—",
"—",
<Badge text="Pre-Reg" color="bg-yellow-500/15 text-yellow-400" />,
<Button className="text-emerald-400 font-semibold hover:text-emerald-300">Check in</Button>,
],
[
<span className="font-semibold text-[#2EE6C5]">VIS-4149</span>,
<span className="font-semibold text-slate-100">Imran Q.</span>,
"FreshMenu",
"TECH Local",
"AMC service",
<span className="px-3 py-1 rounded-lg bg-slate-900 border border-slate-700 text-xs">Contractor</span>,
"10:30",
"—",
<Badge text="Checked-In" color="bg-emerald-500/15 text-emerald-400" />,
<Button className="text-teal-400 font-semibold hover:text-teal-300">Check out</Button>,
],
];
const [activeFilter, setActiveFilter] = useState<string>('All');
return (
<div className="my-15 text-slate-100 font-sans">
{/* --- HEADER SECTION --- */}
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
<div>
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
<span className="text-teal-400">Apple One HQ Tower</span>
<span></span>
<span>Workplace</span>
<span></span>
<span>Visitors</span>
</div>
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
Visitor management
</h1>
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
Pre-registration, QR / OTP check-in, host notifications and on-site visitor tracking with a full audit trail.
</p>
</div>
{/* --- HEADER BUTTONS --- */}
<div className="flex items-center gap-3 self-start md:mt-10">
<Button className="bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 text-xs px-4 py-2 rounded-lg flex items-center gap-2 font-semibold transition-colors">
<BarChart2 className="w-4 h-4 text-slate-300" />
<span>Scan QR</span>
</Button>
<Button className="bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold text-xs px-4 py-2 rounded-lg flex items-center gap-2 transition-colors">
<Plus className="w-4 h-4 stroke-[3]" />
<span>Pre Register</span>
</Button>
</div>
</div>
{/* --- STAT CARDS --- */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{/* Active vendors */}
<StatCard
variant="icon"
title="Active vendors"
value="6"
icon={Star}
badgeText="contracted"
badgeClassName="text-slate-400 lowercase font-mono"
iconContainerClassName="bg-cyan-950/40 border-cyan-800/30 text-cyan-400"
/>
{/* Avg SLA adherence */}
<StatCard
variant="icon"
title="Avg SLA adherence"
value="93%"
icon={ShieldCheck}
badgeText="this month"
badgeClassName="text-teal-400 lowercase font-mono font-medium"
iconContainerClassName="bg-emerald-950/60 border-emerald-800/40 text-emerald-400"
/>
{/* Open field jobs */}
<StatCard
variant="icon"
title="Open field jobs"
value="12"
icon={Activity}
badgeText="in progress"
badgeClassName="text-cyan-400 lowercase font-mono"
iconContainerClassName="bg-blue-950/50 border-blue-800/30 text-blue-400"
/>
{/* Avg response time */}
<StatCard
variant="icon"
title="Avg response time"
value="2.7h"
icon={Clock}
badgeText="to site"
badgeClassName="text-slate-400 lowercase font-mono"
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-500"
/>
</div>
{/* --- STATUS PILL FILTERS --- */}
<div className="mb-6">
<FilterTabs
options={visitorFilters}
activeFilter={activeFilter}
onChange={setActiveFilter}
/>
</div>
<Table headers={headers} rows={rows}/>
<div className="mb-10 mt-10">
<InfoBanner
boldText="Flow:"
normalText="host pre-registers → visitor receives QR + OTP → reception scans for check-in (ID & photo captured) → host notified → auto check-out on exit. Denied visitors are held on a site watchlist."
/>
</div>
</div>
);
}