Operations dashboard screens are added

This commit is contained in:
2026-07-20 18:06:04 +05:30
parent a0426e8c8a
commit 0c9151b3a0
33 changed files with 826 additions and 47 deletions

View File

@@ -1,14 +1,11 @@
interface Props {
label: string;
value?: string;
type?: string;
}
import { FormProps } from "@/constants/types";
export default function FormInput({
label,
value,
type = "text",
}: Props) {
placeholder="string"
}: FormProps) {
return (
<div>
<label className="mb-2 block text-sm text-slate-400">
@@ -18,7 +15,10 @@ export default function FormInput({
<input
type={type}
defaultValue={value}
placeholder={placeholder}
className="h-10 w-full rounded-xl border border-slate-700 text-white px-5"
/>
</div>
);

83
utils/navbar.tsx Normal file
View File

@@ -0,0 +1,83 @@
import { Search, HelpCircle, Globe, Sun, Bell, ChevronDown, MapPin } from 'lucide-react';
export default function Navbar() {
return (
/*
- 'fixed top-0 right-0 left-64' anchors the navbar to the top of the screen,
leaving room exactly for your 64-width sidebar.
- 'bg-[#071321]' matches your master UI background.
- 'h-16' gives it a consistent height.
*/
<header className="fixed top-0 right-0 left-64 z-30 flex h-16 items-center justify-between border-b border-[#142538]/50 bg-[#071321] px-8">
{/* Left Group */}
<div className="flex items-center gap-2">
{/* Company Selector */}
<div className="flex items-center gap-2 rounded-full bg-[#0a1826] px-3 py-1.5 text-sm font-semibold text-[#10b981] border border-[#132338] cursor-pointer hover:bg-[#0f2338] transition whitespace-nowrap">
<span className="h-2 w-2 rounded-full bg-[#10b981] shrink-0"></span>
<span className="font-semibold">Apple One</span>
<ChevronDown className="h-3.5 w-3.5 text-[#10b981]" />
</div>
{/* Location Selector */}
<div className="flex items-center gap-2 rounded-full bg-[#0a1826] px-4 py-2 text-sm font-medium text-[#cbd5e1] border border-[#132338] cursor-pointer hover:bg-[#0f2338] transition whitespace-nowrap">
<MapPin className="h-4 w-4 text-gray-500 shrink-0" />
<span className="whitespace-nowrap">
Apple One <span className="text-[#cbd5e1] font-semibold">HQ Tower</span>
</span>
<ChevronDown className="h-4 w-4 text-gray-500 shrink-0" />
</div>
{/* Search Bar */}
<div className="relative w-60">
<Search className="absolute left-4 top-2.5 h-4 w-4 text-gray-500" />
<input
type="text"
placeholder="Search requests, equipment, met"
className="w-full rounded-xl bg-[#0a1826] pl-10 pr-4 py-2 text-sm text-white placeholder-gray-500 border border-[#132338] focus:outline-none focus:border-[#3de0c4]/50"
/>
</div>
</div>
{/* Right Utilities */}
<div className="flex items-center gap-3">
{/* Tour */}
<button className="flex items-center gap-2 rounded-xl border border-[#132338] bg-[#0a1826] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#14253d]">
<HelpCircle className="h-4 w-4 text-gray-400" />
<span>Tour</span>
</button>
{/* Language */}
<div className="flex items-center gap-1.5 rounded-xl border border-[#132338] bg-[#0a1826] px-4 py-2 text-sm font-semibold text-[#cbd5e1] cursor-pointer hover:bg-[#14253d]">
<Globe className="h-4 w-4 text-gray-400" />
<span>English</span>
<ChevronDown className="h-4 w-4 text-gray-500" />
</div>
{/* Live Indicator */}
<div className="flex items-center gap-2 px-1 text-sm font-bold text-[#10b981] tracking-wider">
<span className="h-1.5 w-1.5 rounded-full bg-[#10b981]"></span>
<span className="text-xs font-bold text-gray-400">LIVE</span>
</div>
{/* Theme Toggle */}
<button className="flex h-9 w-9 items-center justify-center rounded-xl border border-[#132338] bg-[#0a1826] text-gray-400 hover:text-white transition">
<Sun className="h-4 w-4" />
</button>
{/* Notifications */}
<div className="relative flex h-9 w-9 items-center justify-center rounded-xl border border-[#132338] bg-[#0a1826] cursor-pointer hover:bg-[#14253d] transition">
<Bell className="h-4 w-4 text-gray-400" />
<span className="absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#ef4444] text-[9px] font-bold text-white">
8
</span>
</div>
{/* Profile Avatar */}
<div className="flex h-9 w-9 items-center justify-center rounded-full bg-[#1b3454] text-xs font-bold text-white cursor-pointer hover:opacity-90 transition">
DH
</div>
</div>
</header>
);
}

49
utils/sideBarItem.tsx Normal file
View File

@@ -0,0 +1,49 @@
"use client";
import Link from "next/link";
import { useState } from "react";
import { ChevronDown } from "lucide-react";
import {Props} from "@/constants/types";
import Button from "@/utils/button";
export default function SidebarItem({ item }: Props) {
const [open, setOpen] = useState(item.title === "Overview");
return (
<div className=" border-b border-slate-800 ">
<Button
onClick={() => setOpen(!open)}
className="flex w-full items-center justify-between px-5 py-4 hover:bg-slate-900"
>
<div className="flex items-center gap-3">
<item.icon size={20} />
<span className="font-medium">{item.title}</span>
</div>
<ChevronDown
size={18}
className={`transition-transform ${
open ? "rotate-180" : ""
}`}
/>
</Button>
{open && item.children.length > 0 && (
<div className="ml-6 border-l border-slate-700 pl-4 pb-2">
{item.children.map((child: any) => (
<Link
key={child.title}
href={child.href}
className="group mt-2 flex items-center gap-3 rounded-lg px-3 py-2 text-slate-300 hover:bg-teal-500/20 hover:text-teal-400"
>
<child.icon size={18} />
<span>{child.title}</span>
</Link>
))}
</div>
)}
</div>
);
}

82
utils/sidebar.tsx Normal file
View File

@@ -0,0 +1,82 @@
import { SIDEBAR_ITEMS } from "@/constants/constant";
import SidebarItem from "./sideBarItem";
import {
ShieldCheck,
Activity,
ChevronDown,
ChevronsLeft,
} from "lucide-react";
import { useSession, signOut } from "next-auth/react";
export default function Sidebar() {
const { data: session } = useSession();
console.log("Session data ",session?.user?.name)
console.log("Session data ",session?.user?.email)
return (
<aside className="fixed bg-[#071321] text-white h-screen overflow-y-auto [scrollbar-color:#1b2a40_#071321] [scrollbar-width:thin]">
<div className="flex items-center gap-2 px-2 pt-2 pb-3">
<div className="flex h-9 w-9 items-center justify-center rounded-lg
bg-[#3de0c4] font-bold text-[#05111d] text-sm">
K
</div>
<span className="text-sm font-bold tracking-tight text-white">
KA<span className="text-[#3de0c4]">FM</span>
</span>
</div>
<hr className="border-[#142538]" />
<div className="mx-4 my-4 flex rounded-xl
bg-[#091624] p-2 border border-[#142538]">
<button className="flex flex-1
items-center justify-center gap-1 rounded-lg bg-[#3de0c4] py-1.5 text-xs
text-black font-bold shadow-sm">
<Activity size={14} />
Operations
</button>
<button className="flex flex-1 items-center justify-center gap-1
rounded-lg py-1 text-xs text-slate-400 font-medium hover:text-white transition-colors">
<ShieldCheck size={14} />
Admin
</button>
</div>
<p className="px-4 pt-2 pb-1 text-xs font-semibold uppercase
tracking-wider text-slate-500">
Tenant runtime
</p>
<div className="py-3">
{SIDEBAR_ITEMS.map((item) => (
<SidebarItem
key={item.title}
item={item}
/>
))}
</div>
<div className="px-3 pb-2 pt-1
border-t border-[#0f1f30]">
<button className="flex w-full items-center gap-2 rounded-xl py-2 px-3 text-xs text-slate-400 hover:bg-[#091624] hover:text-white transition-colors">
<ChevronsLeft size={16} />
Minimise menu
</button>
</div>
{/* Profile Card Section */}
<div className="m-3 mt-0 rounded-xl border border-[#142538] bg-[#071321] p-2.5">
<div className="flex items-center justify-between">
<div className="flex gap-2.5 items-center">
<div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-cyan-600/50 bg-[#0d2430] text-cyan-400 text-xs font-bold">
DT
</div>
<div className="min-w-0 leading-tight">
<h4 className="font-semibold text-xs text-white truncate">{session?.user?.name}</h4>
<p className="text-[11px] text-slate-500 truncate">
{session?.user?.email}
</p>
</div>
</div>
<ChevronDown className="-rotate-90 text-slate-500 shrink-0" size={14}
onClick={() => signOut({ callbackUrl: "/login" })} />
</div>
</div>
</aside>
);
}

226
utils/table.tsx Normal file
View File

@@ -0,0 +1,226 @@
import React, { useState } from 'react';
import {
Pencil,
Menu,
ShieldCheck,
QrCode,
Power,
Check,
Sparkles,
} from 'lucide-react';
// Data model interface
interface TableRowData {
id: string;
block: string;
floor: string;
zone: string;
department: string;
location: string;
window: string;
tat: string;
sched: number;
approval: boolean;
status: 'ACTIVE' | 'INACTIVE';
}
// Sample data extracted from your screenshot
const mockData: TableRowData[] = [
{ id: '1', block: 'Block 1', floor: 'G', zone: 'Z2', department: 'Electrical', location: 'Main lobby', window: '06:0017:30', tat: '60m', sched: 8, approval: true, status: 'INACTIVE' },
{ id: '2', block: 'Block 2', floor: '3', zone: 'Z2', department: 'Electrical', location: 'Main lobby', window: '08:0023:59', tat: '30m', sched: 4, approval: false, status: 'ACTIVE' },
{ id: '3', block: 'Block 2', floor: '3', zone: 'Z1', department: 'Inspection work', location: 'Cafeteria', window: '06:0017:30', tat: '20m', sched: 8, approval: true, status: 'ACTIVE' },
{ id: '4', block: 'Block 1', floor: 'G', zone: 'Z1', department: 'HVAC', location: 'Server room', window: '08:0018:00', tat: '30m', sched: 4, approval: false, status: 'INACTIVE' },
{ id: '5', block: 'Block 2', floor: '3', zone: 'N', department: 'Inspection work', location: 'Server room', window: '08:0018:00', tat: '60m', sched: 8, approval: true, status: 'ACTIVE' },
{ id: '6', block: 'Block 2', floor: 'G', zone: 'Z1', department: 'Electrical', location: 'Reception', window: '06:0018:00', tat: '60m', sched: 6, approval: false, status: 'ACTIVE' },
{ id: '7', block: 'Block 1', floor: 'G', zone: 'Z2', department: 'Electrical', location: 'Food court', window: '06:0017:30', tat: '60m', sched: 8, approval: true, status: 'ACTIVE' },
{ id: '8', block: 'Block 2', floor: '3', zone: 'Z2', department: 'Electrical', location: 'Cafeteria', window: '08:0023:59', tat: '30m', sched: 4, approval: false, status: 'ACTIVE' },
{ id: '9', block: 'Block 2', floor: '3', zone: 'Z1', department: 'Inspection work', location: 'Gym', window: '06:0017:30', tat: '20m', sched: 8, approval: true, status: 'INACTIVE' },
{ id: '10', block: 'Block 1', floor: 'G', zone: 'Z1', department: 'HVAC', location: 'Lobby', window: '08:0018:00', tat: '30m', sched: 4, approval: false, status: 'ACTIVE' },
];
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]
);
};
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>
</div>
);
}

36
utils/tabs.tsx Normal file
View File

@@ -0,0 +1,36 @@
// @/utils/NavigationTabs.tsx
import React from 'react';
interface NavigationTabsProps {
tabs: string[];
activeTab: string;
onTabChange: (tab: string) => void;
className?: string;
}
export default function NavigationTabs({
tabs,
activeTab,
onTabChange,
className = '',
}: NavigationTabsProps) {
return (
<div
className={`flex items-center gap-8 border-b border-slate-800 text-sm font-semibold overflow-x-auto no-scrollbar ${className}`}
>
{tabs.map((tab) => (
<button
key={tab}
onClick={() => onTabChange(tab)}
className={`pb-3 relative transition-colors whitespace-nowrap ${
activeTab === tab
? 'text-teal-400 border-b-2 border-teal-400 font-bold'
: 'text-slate-400 hover:text-slate-200'
}`}
>
{tab}
</button>
))}
</div>
);
}