Table component is added and visitor management screen is added
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
"use client"
|
||||
|
||||
import VistiorManagementPortal from "@/components/dashboard/visitorManagement";
|
||||
|
||||
|
||||
export default function InventoryPage() {
|
||||
return (
|
||||
|
||||
|
||||
<div className="p-8 mx-3">
|
||||
<VistiorManagementPortal/>
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
}
|
||||
@@ -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="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">
|
||||
|
||||
<div className="w-full bg-[#071321] text-slate-300 rounded-xl overflow-hidden border border-slate-800/80 shadow-2xl relative font-sans">
|
||||
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||
|
||||
<div className="overflow-x-auto">
|
||||
<span>•</span>
|
||||
|
||||
<table className="w-full text-left text-xs border-collapse">
|
||||
<span>Operations</span>
|
||||
|
||||
{/* TABLE HEADER */}
|
||||
<span>•</span>
|
||||
|
||||
<thead>
|
||||
<span>Equipment</span>
|
||||
|
||||
<tr className="border-b border-slate-800/90 text-[11px] font-bold tracking-wider text-slate-500 uppercase font-mono">
|
||||
</div>
|
||||
|
||||
<th className="py-4 px-4 w-12 text-center">
|
||||
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
|
||||
|
||||
<input
|
||||
Equipment & maintenance
|
||||
|
||||
type="checkbox"
|
||||
</h1>
|
||||
|
||||
checked={selectedIds.length === mockData.length}
|
||||
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl mb-10 leading-relaxed">
|
||||
|
||||
onChange={toggleSelectAll}
|
||||
Asset register, preventive (PPM) and
|
||||
|
||||
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"
|
||||
contract (AMC) maintenance, scoped to the active tenant.
|
||||
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
|
||||
{/* Navigation Tabs */}
|
||||
<NavigationTabs
|
||||
tabs={equipmentTabs}
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
className="mb-6"
|
||||
/>
|
||||
|
||||
</th>
|
||||
{/* --- 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>
|
||||
|
||||
<th className="py-4 px-3 font-mono">BLOCK</th>
|
||||
{/* 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>
|
||||
|
||||
<th className="py-4 px-3 font-mono">FLOOR</th>
|
||||
{/* 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>
|
||||
|
||||
<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" />
|
||||
|
||||
)}
|
||||
{/* 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="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"
|
||||
|
||||
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"
|
||||
/>
|
||||
|
||||
</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>
|
||||
{/* 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>
|
||||
|
||||
</tr>
|
||||
{/* 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>
|
||||
);
|
||||
|
||||
})}
|
||||
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
Layers,
|
||||
@@ -8,24 +6,307 @@ import {
|
||||
Clock,
|
||||
Search,
|
||||
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:56–18: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:56–23: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:00–23: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:00–18: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:00–18: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:00–17: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:56–18: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:56–23: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:00–23: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:00–18: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:00–18: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:00–17: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,10 +315,10 @@ 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>
|
||||
@@ -49,12 +330,11 @@ export default function HelpDeskPortal() {
|
||||
tabs={helpdeskTtabs}
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
className="mb-6"
|
||||
className="mb-4"
|
||||
/>
|
||||
|
||||
{/* Rest of your page content */}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3 mb-4">
|
||||
{/* Total Checklists */}
|
||||
<StatCard
|
||||
variant="icon"
|
||||
@@ -98,59 +378,55 @@ export default function HelpDeskPortal() {
|
||||
badgeClassName="text-slate-500 lowercase font-mono"
|
||||
iconContainerClassName="bg-blue-950/50 border-blue-800/30 text-blue-400"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
</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">
|
||||
<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>
|
||||
</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">
|
||||
{/* 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>
|
||||
</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>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
279
components/dashboard/visitorManagement.tsx
Normal file
279
components/dashboard/visitorManagement.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -226,6 +226,15 @@ export const Reqeusttabs: string[] = [
|
||||
{ label: 'Rejected', count: 1 },
|
||||
];
|
||||
|
||||
export const visitorFilters = [
|
||||
{ label: 'All', count: 12 },
|
||||
{ label: 'Expected', count: 2 },
|
||||
{ label: 'Pre-Reg', count: 2},
|
||||
{ label: 'Onsite', count: 4 },
|
||||
{ label: 'Left', count: 3 },
|
||||
{ label: 'Denied', count: 1 },
|
||||
|
||||
];
|
||||
|
||||
// Data model interface
|
||||
|
||||
|
||||
@@ -1,397 +1,55 @@
|
||||
import { useState } from 'react';
|
||||
|
||||
import {
|
||||
|
||||
Pencil,
|
||||
|
||||
Menu,
|
||||
|
||||
ShieldCheck,
|
||||
|
||||
QrCode,
|
||||
|
||||
Power,
|
||||
|
||||
Check,
|
||||
|
||||
Sparkles,
|
||||
|
||||
} from 'lucide-react';
|
||||
|
||||
import { mockData } from '@/constants/constant';
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
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]
|
||||
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
import React from "react";
|
||||
|
||||
type TableProps = {
|
||||
headers: string[];
|
||||
rows: React.ReactNode[][];
|
||||
};
|
||||
|
||||
export default function Table({ headers, rows }: TableProps) {
|
||||
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="w-full overflow-hidden rounded-2xl border border-slate-800 bg-[#0B1727] shadow-xl">
|
||||
<div className="overflow-x-auto">
|
||||
|
||||
<table className="w-full text-left text-xs border-collapse">
|
||||
|
||||
{/* TABLE HEADER */}
|
||||
|
||||
<table className="w-full border-collapse text-[12px]">
|
||||
<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"
|
||||
|
||||
/>
|
||||
|
||||
<tr className="border-b border-slate-800 uppercase tracking-[0.12em] text-[10px] text-slate-500">
|
||||
{headers.map((header, index) => (
|
||||
<th
|
||||
key={index}
|
||||
className={`px-5 py-4 font-semibold whitespace-nowrap ${
|
||||
index === headers.length - 1
|
||||
? "text-center"
|
||||
: "text-left"
|
||||
}`}
|
||||
>
|
||||
{header}
|
||||
</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 (
|
||||
|
||||
<tbody className="divide-y divide-slate-800/70">
|
||||
{rows.map((row, rowIndex) => (
|
||||
<tr
|
||||
|
||||
key={row.id}
|
||||
|
||||
className={`group relative transition-colors ${
|
||||
|
||||
isSelected
|
||||
|
||||
? 'bg-[#0e2238]/60 hover:bg-[#122942]/80'
|
||||
|
||||
: 'hover:bg-slate-800/30'
|
||||
|
||||
key={rowIndex}
|
||||
className="transition-colors duration-200 hover:bg-slate-800/30"
|
||||
>
|
||||
{row.map((cell, cellIndex) => (
|
||||
<td
|
||||
key={cellIndex}
|
||||
className={`px-5 py-4 text-[12px] text-slate-300 whitespace-nowrap ${
|
||||
cellIndex === row.length - 1
|
||||
? "text-center"
|
||||
: "text-left"
|
||||
}`}
|
||||
|
||||
>
|
||||
|
||||
{/* 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"
|
||||
|
||||
/>
|
||||
|
||||
{cell}
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
);
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user