Admin part is added and navbar context file is added

This commit is contained in:
2026-07-27 19:05:11 +05:30
parent cdb52e717e
commit 954de712a7
23 changed files with 2525 additions and 131 deletions

View File

@@ -0,0 +1,19 @@
"use client"
import DynamicDashboardPortal from "@/components/dashboard/admin/Configuration/dynamicDashboard";
export default function DynamicDashboardPage() {
return (
<div className="p-8 mx-3">
<DynamicDashboardPortal/>
</div>
);
}

View File

@@ -0,0 +1,20 @@
"use client"
import LanguagePortal from "@/components/dashboard/admin/Configuration/language";
import ModulesPortal from "@/components/dashboard/admin/Configuration/modules";
export default function LanguagePage() {
return (
<div className="p-8 mx-3">
<LanguagePortal/>
</div>
);
}

View File

@@ -0,0 +1,21 @@
"use client"
import MobileFlowsPortal from "@/components/dashboard/admin/Configuration/mobileFlows";
export default function MobileFlowPage() {
return (
<div className="p-8 mx-3">
<MobileFlowsPortal/>
</div>
);
}

View File

@@ -0,0 +1,19 @@
"use client"
import ModulesPortal from "@/components/dashboard/admin/Configuration/modules";
export default function ModulesPage() {
return (
<div className="p-8 mx-3">
<ModulesPortal/>
</div>
);
}

View File

@@ -0,0 +1,18 @@
"use client"
import AdminOverviewPortal from "@/components/dashboard/admin/overview";
export default function OverviewPage() {
return (
<div className="p-8 mx-3">
<AdminOverviewPortal/>
</div>
);
}

View File

@@ -0,0 +1,23 @@
"use client"
import SecurityPortal from "@/components/dashboard/admin/Platform/security";
export default function SecurityPage() {
return (
<div className="p-8 mx-3">
<SecurityPortal/>
</div>
);
}

View File

@@ -0,0 +1,20 @@
"use client"
import SitePortal from "@/components/dashboard/admin/site";
export default function AuditSurveyPage() {
return (
<div className="p-8 mx-3">
<SitePortal/>
</div>
);
}

View File

@@ -0,0 +1,19 @@
"use client"
import TenantsPortal from "@/components/dashboard/admin/tenants";
export default function TenantsPage() {
return (
<div className="p-8 mx-3">
<TenantsPortal/>
</div>
);
}

View File

@@ -0,0 +1,20 @@
"use client"
import UserPortal from "@/components/dashboard/admin/users";
export default function UsersPage() {
return (
<div className="p-8 mx-3">
<UserPortal/>
</div>
);
}

View File

@@ -1,7 +1,9 @@
"use client"// app/dashboard/layout.tsx "use client";
import Sidebar from "@/components/layouts/sidebar"; import Sidebar from "@/components/layouts/sidebar";
import Navbar from "@/components/layouts/navbar"; import Navbar from "@/components/layouts/navbar";
import { Sparkles } from "lucide-react"; import { Sparkles } from "lucide-react";
import { NavProvider } from "@/components/layouts/context/navContext";
export default function DashboardLayout({ export default function DashboardLayout({
children, children,
@@ -9,27 +11,29 @@ export default function DashboardLayout({
children: React.ReactNode; children: React.ReactNode;
}) { }) {
return ( return (
<div className=" bg-[#071321] text-white "> <NavProvider>
{/* Shared Sidebar */}
{/* Shared Sidebar - Now inside NavProvider */}
<Sidebar /> <Sidebar />
<div className="flex-1 flex flex-col min-h-screen"> <div className="pl-64 flex flex-col ">
{/*Common Navbar for every webpage */} {/* Shared Navbar - Now inside NavProvider */}
<Navbar /> <Navbar />
{/* Main Page Content */}
<main className="flex-1 pl-68 bg-[#071321]"> <main className="flex-1 bg-[#071321]">
{children} {children}
</main> </main>
</div> </div>
{/* Ask AI Floating Button common */} {/* Ask AI Floating Button */}
<button className="fixed bottom-4 right-4 z-40 flex items-center gap-2 <button className="fixed bottom-4 right-4 z-40 flex items-center gap-2
rounded-full bg-[#17d4bd] hover:bg-[#13b19e] px-4 py-2.5 text-xs rounded-full bg-[#17d4bd] hover:bg-[#13b19e] px-4 py-2.5 text-xs
font-bold text-[#05111d] transition shadow-lg shadow-[#17d4bd]/20"> font-bold text-[#05111d] transition shadow-lg shadow-[#17d4bd]/20">
<Sparkles size={14} className="animate-pulse" /> <Sparkles size={14} className="animate-pulse" />
<span>Ask AI</span> <span>Ask AI</span>
</button> </button>
</div>
</NavProvider>
); );
} }

View File

@@ -0,0 +1,173 @@
import { ChevronDown, Trash2, X, Plus, Sparkles } from "lucide-react";
const initialRows = [
// Row 1 (2 cards)
{
id: "row-1",
widgets: [
{ id: "w1", title: "TFM Requests · Monthly", type: "report.widget" },
{ id: "w2", title: "Recent Activity", type: "report.widget" },
],
},
// Row 2 (2 cards)
{
id: "row-2",
widgets: [
{ id: "w3", title: "Schedule Performance", type: "report.widget" },
{ id: "w4", title: "Energy · Today", type: "report.widget" },
],
},
// Row 3 (3 cards)
{
id: "row-3",
widgets: [
{ id: "w5", title: "Top 5 Requester", type: "report.widget" },
{ id: "w6", title: "Asset Register", type: "report.widget" },
{ id: "w7", title: "PM Plan vs Started", type: "report.widget" },
],
},
];
// Note the 'export default' keyword here:
export default function DynamicDashboardPortal() {
return (
<div className="my-15 text-slate-100 font-sans">
{/* --- HEADER SECTION --- */}
<div className="flex flex-col md:flex-row md:items-start justify-between gap-4 mb-6">
<div>
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest uppercase font-mono text-slate-400 mb-1">
<span>PLATFORM</span>
<span></span>
<span>DYNAMIC DASHBOARD</span>
</div>
<h1 className="text-2xl font-bold text-white tracking-tight">
Dynamic dashboard
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-2xl leading-relaxed">
Design the dashboard each tenant's users land on in Operations. Arrange rows,
drop in report widgets, then publish.
</p>
</div>
{/* Action Buttons */}
<div className="flex items-center gap-3 self-start md:self-auto">
<span className="inline-flex items-center gap-1.5 bg-[#0C243B] border border-sky-800/50 text-sky-400 text-xs px-3 py-1.5 rounded-full font-mono font-medium">
<span className="w-1.5 h-1.5 rounded-full bg-sky-400"></span>
PUBLISHED
</span>
<button className="bg-[#0E1A2B] hover:bg-slate-800 text-slate-200 border border-slate-800 px-4 py-2 rounded-lg text-xs font-semibold transition-colors">
Save draft
</button>
<button className="bg-[#1DE9B6] hover:bg-[#19d4a5] text-slate-950 font-semibold text-xs px-4 py-2 rounded-lg transition-colors shadow-lg shadow-teal-500/10">
Publish
</button>
</div>
</div>
{/* --- CONTROL BAR: DESIGNING FOR TENANT --- */}
<div className=" border border-slate-800/80 rounded-xl p-2 mb-2 flex flex-col sm:flex-row sm:items-center justify-between gap-4 backdrop-blur-sm">
<div className="flex items-center gap-3">
<span className="text-xs font-medium text-slate-300 shrink-0">
Designing for
</span>
<div className="relative min-w-[200px]">
<select
defaultValue="Apple One"
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg pl-3.5 pr-8 py-1.5 text-xs text-slate-200 font-medium focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="Apple One">Apple One</option>
<option value="Tenant Two">Tenant Two</option>
</select>
<ChevronDown className="absolute right-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-slate-400 pointer-events-none" />
</div>
</div>
<div className="text-xs font-mono text-slate-400 bg-[#060B14] border border-slate-800 px-3 py-1.5 rounded-lg shrink-0">
3 rows · 7 widgets
</div>
</div>
{/* --- CANVAS GRID AREA --- */}
<div className="border border-slate-800/80 rounded-2xl p-4 sm:p-6 mb-8 space-y-6 bg-[radial-gradient(#1e293b_1px,transparent_1px)] [background-size:16px_16px]">
{initialRows.map((row) => (
<div key={row.id} className="relative group/row">
{/* Delete Row Button */}
<button
className="absolute -top-3 -right-2 z-10 p-1.5 text-slate-500 hover:text-rose-400 bg-[#060B14] border border-slate-800 rounded-md transition-colors opacity-80 group-hover/row:opacity-100"
title="Delete row"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
{/* Row Widgets Layout */}
<div
className={`grid gap-4 ${
row.widgets.length === 1
? "grid-cols-1"
: row.widgets.length === 2
? "grid-cols-1 md:grid-cols-2"
: "grid-cols-1 md:grid-cols-3"
}`}
>
{row.widgets.map((widget) => (
<div
key={widget.id}
className="bg-[#0B132B]/80 border border-slate-800/90 rounded-xl p-4 backdrop-blur-sm flex flex-col justify-between h-36 relative group/widget hover:border-slate-700/80 transition-all"
>
{/* Widget Top Header */}
<div className="flex items-start justify-between gap-2">
<div>
<h3 className="text-sm font-semibold text-white tracking-tight">
{widget.title}
</h3>
<p className="text-[11px] font-mono text-slate-400 mt-0.5">
{widget.type}
</p>
</div>
<button
className="p-1 rounded bg-slate-800/40 hover:bg-slate-700 text-slate-400 hover:text-white transition-colors"
title="Remove widget"
>
<X className="w-3.5 h-3.5" />
</button>
</div>
{/* Widget Placeholder Bar Chart */}
<div className="flex items-end justify-between gap-1.5 h-12 pt-2 px-1">
<div className="w-full bg-[#0E2433] rounded-t-sm h-[30%] border-t border-teal-500/50"></div>
<div className="w-full bg-[#0E2433] rounded-t-sm h-[45%] border-t border-teal-500/50"></div>
<div className="w-full bg-[#0E2433] rounded-t-sm h-[85%] border-t border-teal-500/50"></div>
<div className="w-full bg-[#0E2433] rounded-t-sm h-[60%] border-t border-teal-500/50"></div>
<div className="w-full bg-[#0E2433] rounded-t-sm h-[75%] border-t border-teal-500/50"></div>
</div>
</div>
))}
</div>
</div>
))}
</div>
{/* --- BOTTOM ADD ROW CONTROLS --- */}
<div className="flex flex-wrap items-center gap-3">
<span className="text-xs font-mono text-slate-400 uppercase tracking-wider">
Add row:
</span>
<button className="flex items-center gap-1.5 hover:bg-slate-800 border border-slate-800/80 text-slate-200 text-xs px-3 py-1.5 rounded-lg transition-colors font-medium">
<Plus className="w-3.5 h-3.5 text-teal-400" />
<span>1 card</span>
</button>
<button className="flex items-center gap-1.5 hover:bg-slate-800 border border-slate-800/80 text-slate-200 text-xs px-3 py-1.5 rounded-lg transition-colors font-medium">
<Plus className="w-3.5 h-3.5 text-teal-400" />
<span>2 cards</span>
</button>
<button className="flex items-center gap-1.5 hover:bg-slate-800 border border-slate-800/80 text-slate-200 text-xs px-3 py-1.5 rounded-lg transition-colors font-medium">
<Plus className="w-3.5 h-3.5 text-teal-400" />
<span>3 cards</span>
</button>
</div>
</div>
);
}

View File

@@ -0,0 +1,256 @@
"use client";
import { useState } from "react";
import { Plus, ChevronDown } from "lucide-react";
const initialLanguageValues = [
{
id: 1,
app: "Web Console",
menu: "Tenant",
screen: "Tenant List",
uiKey: "btn.add_tenant",
code: "EN",
value: "Add Tenant",
},
{
id: 2,
app: "Web Console",
menu: "Site",
screen: "Site List",
uiKey: "btn.add_site",
code: "EN",
value: "Add Site",
},
{
id: 3,
app: "Mobile App",
menu: "Inspections",
screen: "QR Round",
uiKey: "lbl.scan_qr",
code: "EN",
value: "Scan QR to begin round",
},
];
export default function LanguagePortal() {
const [languageValues, setLanguageValues] = useState(initialLanguageValues);
return (
<div className="my-15 text-slate-100 font-sans ">
{/* --- HEADER SECTION --- */}
<div className="mb-6">
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest uppercase font-mono text-slate-400 mb-1">
<span>PLATFORM</span>
<span></span>
<span>LANGUAGE</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight">Language</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-3xl leading-relaxed">
Manage translation keys across apps, menus and screens then resolve their values per tenant.
</p>
</div>
{/* --- FORM SECTION: LANGUAGE KEY MASTER --- */}
<div className=" border border-slate-800/80 rounded-xl
p-3 mb-6 shadow-xl backdrop-blur-sm">
<div className="mb-5">
<h2 className="text-base font-semibold text-white tracking-tight">
Language key master
</h2>
<p className="text-xs font-mono text-slate-400 mt-0.5">
Scope a translation key to an app, menu and screen
</p>
</div>
<form className="space-y-4">
{/* Top Row Selects */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
APP
</label>
<div className="relative">
<select
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="Web Console">Web Console</option>
<option value="Mobile App">Mobile App</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
<div>
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
MENU
</label>
<div className="relative">
<select
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="Tenant">Tenant</option>
<option value="Site">Site</option>
<option value="Inspections">Inspections</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
<div>
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
SCREEN
</label>
<div className="relative">
<select
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="Tenant List">Tenant List</option>
<option value="Site List">Site List</option>
<option value="QR Round">QR Round</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
</div>
{/* Bottom Row Inputs & Action Button */}
<div className="grid grid-cols-1 md:grid-cols-12 gap-4 items-end">
<div className="md:col-span-4">
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
UI KEY
</label>
<input
type="text"
placeholder="e.g. btn.add_tenant"
className="w-full bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 font-mono focus:outline-none focus:border-teal-500/50 placeholder:text-slate-600"
/>
</div>
<div className="md:col-span-2">
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
CODE
</label>
<div className="relative">
<select
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="en">en</option>
<option value="es">es</option>
<option value="fr">fr</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
<div className="md:col-span-4">
<label className="block text-[11px] font-mono tracking-wider text-slate-400 uppercase mb-1.5">
VALUE
</label>
<input
type="text"
placeholder="e.g. Add Tenant"
className="w-full bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-2 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 placeholder:text-slate-600"
/>
</div>
<div className="md:col-span-2">
<button
type="submit"
className="w-full bg-[#1DE9B6] hover:bg-[#19d4a5] text-slate-950 font-semibold text-sm px-4 py-2 rounded-lg flex items-center justify-center gap-1.5 transition-colors shadow-lg shadow-teal-500/10 cursor-pointer"
>
<Plus className="w-4 h-4 stroke-[2.5]" />
<span>Add</span>
</button>
</div>
</div>
</form>
</div>
{/* --- TABLE SECTION: LANGUAGE VALUES --- */}
<div className=" border border-slate-800/80 rounded-xl p-3 shadow-xl
backdrop-blur-sm">
{/* Table Header Controls */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mb-2">
<div>
<h2 className="text-base font-semibold text-white tracking-tight">
Language values
</h2>
<p className="text-xs font-mono text-slate-400 mt-0.5">
Resolved strings filtered to the selected tenant's locales
</p>
</div>
<div className="relative min-w-[180px]">
<select
className="w-full appearance-none bg-[#060B14] border border-slate-800 rounded-lg px-3.5 py-1.5 text-sm text-slate-200 focus:outline-none focus:border-teal-500/50 cursor-pointer"
>
<option value="Apple One">Apple One</option>
<option value="Tenant Two">Tenant Two</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
{/* Locale Tag */}
<div className="mb-4">
<span className="inline-flex items-center gap-1.5 bg-[#0C243B] border border-sky-800/50 text-sky-400 text-xs px-2.5 py-0.5 rounded-full font-mono font-medium">
<span className="w-1.5 h-1.5 rounded-full bg-sky-400"></span>
EN
</span>
</div>
{/* Data Table */}
<div className="overflow-x-auto">
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b border-slate-800 text-[10px] font-mono tracking-wider text-slate-400 uppercase">
<th className="py-3 px-2 font-medium">APP</th>
<th className="py-3 px-2 font-medium">MENU</th>
<th className="py-3 px-2 font-medium">SCREEN</th>
<th className="py-3 px-2 font-medium">UI KEY</th>
<th className="py-3 px-2 font-medium">CODE</th>
<th className="py-3 px-2 font-medium">VALUE</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-800/60 text-sm">
{languageValues.map((row) => (
<tr key={row.id} className="hover:bg-slate-800/20 transition-colors">
<td className="py-3.5 px-2 font-medium text-slate-200">{row.app}</td>
<td className="py-3.5 px-2 font-medium text-slate-200">{row.menu}</td>
<td className="py-3.5 px-2 text-slate-300">{row.screen}</td>
<td className="py-3.5 px-2">
<span className="font-mono text-xs text-slate-300 bg-[#060B14] border border-slate-800/80 px-2 py-1 rounded">
{row.uiKey}
</span>
</td>
<td className="py-3.5 px-2">
<span className="inline-flex items-center gap-1.5 bg-[#0C243B] border border-sky-800/50 text-sky-400 text-xs px-2 py-0.5 rounded-full font-mono font-medium">
<span className="w-1.5 h-1.5 rounded-full bg-sky-400"></span>
{row.code}
</span>
</td>
<td className="py-3.5 px-2 font-semibold text-white">{row.value}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,45 @@
import { Smartphone } from "lucide-react";
export default function MobileFlowsPortal() {
return (
<div className="my-15 text-slate-100 font-sans">
{/* --- HEADER SECTION --- */}
<div className="flex flex-col md:flex-row md:items-end justify-between gap-4 mb-6">
<div>
{/* Breadcrumb */}
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest uppercase font-mono text-slate-400 mb-1">
<span>PLATFORM</span>
<span></span>
<span>MOBILE</span>
</div>
{/* Title */}
<h1 className="text-2xl font-bold text-white tracking-tight">
KAFM Field mobile flows
</h1>
{/* Subtitle / Description */}
<p className="text-sm font-medium text-slate-400 mt-1 max-w-2xl leading-relaxed">
The technician mobile app, embedded live: two flow variations (1a Field Deck · dark, 1b Day Runner · light), 17 screens each jobs, checklists, QR, inventory, chat, incidents, reports. Every phone is fully interactive.
</p>
</div>
{/* Action Button */}
<div className="self-start md:self-auto shrink-0">
<button className="flex items-center gap-2 bg-[#0A1628] hover:bg-slate-800 text-slate-200 border border-slate-700/80 px-4 py-2 rounded-lg text-xs font-semibold transition-colors shadow-sm cursor-pointer">
<Smartphone className="w-4 h-4 text-slate-300" />
<span>Open full screen</span>
</button>
</div>
</div>
{/* --- EMBEDDED PHONE DISPLAY AREA --- */}
<div className="w-full bg-[#EAEFF4] rounded-2xl min-h-[500px] border border-slate-800 shadow-2xl p-6 flex items-center justify-center">
{/* Mobile preview frame container goes here */}
<p className="text-slate-500 font-mono text-xs">
[ Live Mobile App Frames ]
</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,219 @@
"use client";
import { useState } from "react";
import {
LayoutGrid,
CheckSquare,
Crosshair,
FileText,
Box,
Zap,
ClipboardCheck,
BarChart2,
Pencil,
Plus,
Sparkles,
} from "lucide-react";
// Mock Data matching the design
const initialModules = [
{
id: "MOD-01",
title: "Dashboard",
description: "Operational overview & live KPIs",
icon: LayoutGrid,
menus: [], // Currently empty as per screenshot
},
{
id: "MOD-02",
title: "Helpdesk",
description: "Tickets, requests & SLA tracking",
icon: CheckSquare,
menus: ["Tickets", "SLA Settings", "Queues"],
},
{
id: "MOD-03",
title: "Equipment",
description: "Asset register & equipment groups",
icon: Crosshair,
menus: ["Asset Register", "Categories"],
},
{
id: "MOD-04",
title: "Request",
description: "Service requests & approvals",
icon: FileText,
menus: ["My Requests", "Approvals"],
},
{
id: "MOD-05",
title: "Inventory",
description: "Stock, spares & consumables",
icon: Box,
menus: ["Stock Levels", "Suppliers"],
},
{
id: "MOD-06",
title: "Energy",
description: "Meters, consumption & analytics",
icon: Zap,
menus: ["Meters", "Analytics"],
},
{
id: "MOD-07",
title: "Inspections",
description: "QR rounds & checklists",
icon: ClipboardCheck,
menus: ["Rounds", "Checklists"],
},
{
id: "MOD-08",
title: "Reports",
description: "Analytics & export tools",
icon: BarChart2,
menus: ["Scheduled Reports", "Custom Builder"],
},
];
export default function ModulesPortal() {
const [selectedModuleId, setSelectedModuleId] = useState("MOD-01");
const activeModule = initialModules.find((m) => m.id === selectedModuleId);
return (
<div className="my-15 text-slate-100 font-saas">
{/* Header Section */}
<div className="mb-6">
<div className="flex items-center gap-2 text-[10px] font-semibold tracking-wider text-slate-400 uppercase font-mono mb-1">
<span>PLATFORM</span>
<span></span>
<span>MODULES</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight">Modules</h1>
<p className="text-sm text-slate-400 mt-1 max-w-2xl">
The catalog of system modules and their menus. These are what you switch on per tenant under feature access.
</p>
</div>
{/* Main Grid Container */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 items-start">
{/* Left Column: Modules List */}
<div className="lg:col-span-6 bg-[#0E1A2B]/60 border border-slate-800/80 rounded-xl p-5 backdrop-blur-sm">
{/* Section Header */}
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-base font-semibold text-white">Modules</h2>
<span className="text-xs font-mono text-slate-400">
{initialModules.length} available
</span>
</div>
<button className="flex items-center gap-1.5 text-emerald-400 hover:text-emerald-300 text-xs font-semibold transition-colors">
<Plus className="w-4 h-4" />
<span>Add module</span>
</button>
</div>
{/* Module Items */}
<div className="space-y-2 max-h-[620px] overflow-y-auto pr-1 custom-scrollbar">
{initialModules.map((module) => {
const Icon = module.icon;
const isSelected = module.id === selectedModuleId;
return (
<div
key={module.id}
onClick={() => setSelectedModuleId(module.id)}
className={`group relative flex items-center justify-between p-3.5 rounded-lg border cursor-pointer transition-all ${
isSelected
? "bg-teal-950/30 border-teal-500/40"
: "bg-[#09111E]/40 border-slate-800/60 hover:border-slate-700 hover:bg-[#0D1726]"
}`}
>
<div className="flex items-center gap-3.5 min-w-0">
<div
className={`p-2.5 rounded-lg border shrink-0 ${
isSelected
? "bg-teal-500/10 border-teal-500/30 text-teal-400"
: "bg-slate-900/80 border-slate-800 text-slate-400 group-hover:text-slate-200"
}`}
>
<Icon className="w-4 h-4" />
</div>
<div className="truncate">
<h3 className="text-sm font-semibold text-white truncate">
{module.title}
</h3>
<p className="text-xs text-slate-400 truncate mt-0.5">
{module.description}
</p>
</div>
</div>
<div className="flex items-center gap-2 shrink-0 ml-3">
<span className="text-[11px] font-mono bg-slate-900 border border-slate-800 text-slate-400 px-2 py-0.5 rounded">
{module.id}
</span>
<button
onClick={(e) => {
e.stopPropagation();
// Edit logic here
}}
className="p-1.5 text-slate-400 hover:text-white rounded-md hover:bg-slate-800 transition-colors"
title="Edit module"
>
<Pencil className="w-3.5 h-3.5" />
</button>
</div>
</div>
);
})}
</div>
</div>
{/* Right Column: Menus Panel */}
<div className="lg:col-span-6 bg-[#0E1A2B]/60 border border-slate-800/80 rounded-xl p-5 min-h-[500px] flex flex-col justify-between backdrop-blur-sm">
<div>
{/* Menus Header */}
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-base font-semibold text-white">Menus</h2>
<span className="text-xs font-mono text-slate-400">
Under {activeModule?.title || "Module"}
</span>
</div>
<button className="flex items-center gap-1.5 text-teal-400 hover:text-teal-300 text-xs font-semibold transition-colors">
<Plus className="w-4 h-4" />
<span>Add menu</span>
</button>
</div>
{/* Empty State / Menus List */}
{activeModule?.menus && activeModule.menus.length > 0 ? (
<div className="space-y-2 mt-4">
{activeModule.menus.map((menu, idx) => (
<div
key={idx}
className="flex items-center justify-between p-3 rounded-lg bg-[#09111E]/40 border border-slate-800/60"
>
<span className="text-sm font-medium text-slate-200">{menu}</span>
<button className="text-xs text-slate-400 hover:text-white">
Edit
</button>
</div>
))}
</div>
) : (
<div className="flex flex-col items-center justify-center h-80 text-center">
<p className="text-sm text-slate-400 font-medium">
No menus under this module yet.
</p>
</div>
)}
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,190 @@
import { useState } from 'react';
import {
ClipboardCheck,
Clock,
AlertTriangle,
Activity,
Building2,
Users,
MapPin,
Grid,
ArrowRight,
Sparkles
} from "lucide-react";
import StatCard from "@/components/common/dashStatCard";
export default function AdminOverviewPortal() {
// Sample data for the recent audits table below
const recentAudits = [
{ id: 'AUD-1004', location: 'Local 1', lead: 'Local Admin', date: '2026-02-02', status: 'ACTIVE' },
{ id: 'AUD-1003', location: 'SRM Campus', lead: 'Estates Office', date: '2026-01-08', status: 'ACTIVE' },
{ id: 'AUD-1002', location: 'Novotel Pune', lead: 'Site FM Lead', date: '2025-12-12', status: 'ACTIVE' },
{ id: 'AUD-1001', location: 'Apple One', lead: 'Dhananjay T.', date: '2025-11-04', status: 'ACTIVE' },
];
// Sample data for module/checklist adoption progress bars
const adoptionModules = [
{ name: 'Dashboard', score: 100, count: '4/4' },
{ name: 'Helpdesk', score: 75, count: '3/4' },
{ name: 'Equipment', score: 75, count: '3/4' },
{ name: 'Request', score: 75, count: '3/4' },
{ name: 'Inventory', score: 50, count: '2/4' },
{ name: 'Energy', score: 75, count: '3/4' },
];
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 uppercase font-mono">
<span className="text-teal-400">PLATFORM</span>
<span className="text-slate-500"></span>
<span className="text-slate-500">KAFM</span>
</div>
<h1 className="text-2xl font-bold text-white tracking-tight mt-1">
Platform overview
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-xl leading-relaxed">
Live snapshot of every organization, user, and site managed across the KAFM facility platform.
</p>
</div>
{/* Top Right Status Badge */}
<div className="flex items-center gap-2 px-3 py-1.5 rounded-full bg-slate-900/80 border border-teal-500/30 text-teal-400 text-xs font-mono w-fit">
<span className="h-2 w-2 rounded-full bg-teal-400 animate-pulse" />
<span>PRODUCTION</span>
<span className="text-slate-600"></span>
<span className="text-teal-300/80">region us-east-1</span>
</div>
</div>
{/* --- STAT CARDS --- */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{/* Card 1: Total Tenants */}
<StatCard
variant="icon"
title="Total tenants"
value="4"
icon={Building2}
badgeText="4 active"
badgeClassName="text-teal-400 font-mono text-xs"
iconContainerClassName="bg-teal-950/40 border-teal-800/30 text-teal-400"
/>
{/* Card 2: Platform users */}
<StatCard
variant="icon"
title="Platform users"
value="6"
icon={Users}
badgeText="+2 this wk"
badgeClassName="text-teal-400 font-mono text-xs"
iconContainerClassName="bg-slate-800/50 border-slate-700/50 text-blue-400"
/>
{/* Card 3: Managed sites */}
<StatCard
variant="icon"
title="Managed sites"
value="6"
icon={MapPin}
badgeText="6 live"
badgeClassName="text-teal-400 font-mono text-xs"
iconContainerClassName="bg-amber-950/30 border-amber-800/30 text-amber-400"
/>
{/* Card 4: System modules */}
<StatCard
variant="icon"
title="System modules"
value="13"
icon={Grid}
badgeText="all enabled"
badgeClassName="text-slate-400 font-mono text-xs"
iconContainerClassName="bg-purple-950/40 border-purple-800/30 text-purple-400"
/>
</div>
{/* --- LOWER SECTION (ADDITION FROM SCREENSHOT) --- */}
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left 2-Columns: Recently Onboarded Tenants Table */}
<div className="lg:col-span-2 bg-slate-900/60 border border-slate-800/80 rounded-xl p-5 flex flex-col justify-between">
<div>
<div className="flex items-center justify-between mb-4">
<div>
<h2 className="text-base font-semibold text-white">Recently onboarded tenants</h2>
<p className="text-xs text-slate-400 font-mono mt-0.5">Newest organizations on the platform</p>
</div>
<button className="text-teal-400 hover:text-teal-300 text-xs font-semibold flex items-center gap-1 transition-colors">
View all <ArrowRight className="w-3.5 h-3.5" />
</button>
</div>
{/* Table Header */}
<div className="grid grid-cols-12 text-[11px] font-mono text-slate-500 uppercase tracking-wider py-2 border-b border-slate-800/80">
<span className="col-span-4">Organization</span>
<span className="col-span-3">Tenant ID</span>
<span className="col-span-3">Created</span>
<span className="col-span-2 text-right">Status</span>
</div>
{/* Table Rows */}
<div className="divide-y divide-slate-800/40">
{recentAudits.map((item, index) => (
<div key={index} className="grid grid-cols-12 items-center py-3 text-xs">
<div className="col-span-4 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg bg-slate-800 border border-slate-700/60 flex items-center justify-center font-bold text-slate-300 text-xs">
{item.location.substring(0, 2).toUpperCase()}
</div>
<div>
<div className="font-semibold text-slate-200">{item.location}</div>
<div className="text-[11px] text-slate-400">{item.lead}</div>
</div>
</div>
<span className="col-span-3 font-mono text-slate-400 text-xs">{item.id}</span>
<span className="col-span-3 font-mono text-slate-400 text-xs">{item.date}</span>
<div className="col-span-2 flex justify-end">
<span className="inline-flex items-center gap-1.5 px-2.5 py-0.5 rounded-full text-[10px] font-mono font-bold bg-teal-950/80 text-teal-400 border border-teal-800/50">
<span className="h-1.5 w-1.5 rounded-full bg-teal-400"></span>
{item.status}
</span>
</div>
</div>
))}
</div>
</div>
</div>
{/* Right 1-Column: Module Adoption */}
<div className="bg-slate-900/60 border border-slate-800/80 rounded-xl p-5 flex flex-col justify-between">
<div>
<h2 className="text-base font-semibold text-white mb-5">Module adoption</h2>
<div className="space-y-4">
{adoptionModules.map((module, idx) => (
<div key={idx} className="space-y-1.5">
<div className="flex justify-between items-center text-xs">
<span className="font-semibold text-slate-200">{module.name}</span>
<span className="font-mono text-slate-400 text-[11px]">{module.count}</span>
</div>
{/* Progress Bar Container */}
<div className="w-full bg-slate-800/80 h-2 rounded-full overflow-hidden">
<div
className="bg-teal-400 h-full rounded-full transition-all duration-500"
style={{ width: `${module.score}%` }}
/>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,416 @@
import { useState } from "react";
import {
Shield,
Users,
AlertTriangle,
ShieldCheck,
BarChart3,
Sparkles,
ChevronDown
} from "lucide-react";
import Button from "@/components/common/button";
import StatCard from "@/components/common/dashStatCard";
import InfoBanner from '@/components/common/infoBanner';
import Table from '@/components/common/dataTable';
import { StatusBadge } from "@/components/common/statusBadge";
export const trustedDeviceHeaders = [
"USER",
"DEVICE",
"IP",
"LAST SEEN",
"EXPIRES",
"STATUS",
"ACTIONS",
];
export const trustedDeviceRows = [
[
<span className="font-semibold text-white">Dhananjay T.</span>,
"Chrome 126 · Windows 11",
<span className="font-mono">103.212.14.8</span>,
<span className="font-mono">Today, 08:12</span>,
<span className="font-mono">Jul 30, 2026</span>,
<StatusBadge
text="TRUSTED"
color="bg-emerald-500/15 text-emerald-400"
/>,
<Button className="rounded-md border border-slate-700/80 bg-slate-800/40 px-3 py-1 text-xs text-slate-300 hover:bg-slate-800 transition-colors">
Revoke
</Button>,
],
[
<span className="font-semibold text-white">Kavya Raghunathan</span>,
"Safari 18 · macOS",
<span className="font-mono">49.37.201.55</span>,
<span className="font-mono">Yesterday, 17:40</span>,
<span className="font-mono">Jul 21, 2026</span>,
<StatusBadge
text="TRUSTED"
color="bg-emerald-500/15 text-emerald-400"
/>,
<Button className="rounded-md border border-slate-700/80 bg-slate-800/40 px-3 py-1 text-xs text-slate-300 hover:bg-slate-800 transition-colors">
Revoke
</Button>,
],
[
<span className="font-semibold text-white">Rahul Iyer</span>,
"KAFM Mobile · Android 15",
<span className="font-mono">152.58.33.104</span>,
<span className="font-mono">Today, 06:58</span>,
<span className="font-mono">Jul 12, 2026</span>,
<StatusBadge
text="TRUSTED"
color="bg-emerald-500/15 text-emerald-400"
/>,
<Button className="rounded-md border border-slate-700/80 bg-slate-800/40 px-3 py-1 text-xs text-slate-300 hover:bg-slate-800 transition-colors">
Revoke
</Button>,
],
[
<span className="font-semibold text-white">Meera Pillai</span>,
"Edge 126 · Windows 10",
<span className="font-mono">103.212.14.31</span>,
<span className="font-mono">Jun 18, 2026</span>,
<span className="font-mono text-slate-400">Expired</span>,
<StatusBadge
text="REVOKED"
color="bg-slate-700/50 text-slate-400"
/>,
<span className="text-slate-500 text-center"></span>,
],
];
export const auditLogHeaders = [
"WHEN",
"USER",
"EVENT",
"IP",
"RESULT",
];
export const auditLogRows = [
[
<span className="font-mono text-slate-300">Today 08:12</span>,
<span className="font-mono text-slate-300">dhananjay@kafm.io</span>,
<span className="text-slate-200">Sign-in · password + MFA</span>,
<span className="font-mono text-slate-300">103.212.14.8</span>,
<StatusBadge
text="OK"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-mono text-slate-300">Today 07:56</span>,
<span className="font-mono text-slate-300">kavya@kafm.io</span>,
<span className="text-slate-200">
Role changed · Technician M&E Supervisor
</span>,
<span className="font-mono text-slate-300">49.37.201.55</span>,
<StatusBadge
text="OK"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-mono text-slate-300">Today 07:31</span>,
<span className="font-mono text-slate-300">unknown@ext.net</span>,
<span className="text-slate-200">
Sign-in · wrong password (3rd attempt)
</span>,
<span className="font-mono text-slate-300">91.240.118.7</span>,
<StatusBadge
text="FAILED"
color="bg-rose-500/15 text-rose-400"
/>,
],
[
<span className="font-mono text-slate-300">Today 06:58</span>,
<span className="font-mono text-slate-300">rahul@kafm.io</span>,
<span className="text-slate-200">
Device trusted · KAFM Mobile (30 days)
</span>,
<span className="font-mono text-slate-300">152.58.33.104</span>,
<StatusBadge
text="OK"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-mono text-slate-300">Yesterday 17:40</span>,
<span className="font-mono text-slate-300">kavya@kafm.io</span>,
<span className="text-slate-200">
Password reset · self-service link
</span>,
<span className="font-mono text-slate-300">49.37.201.55</span>,
<StatusBadge
text="OK"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
[
<span className="font-mono text-slate-300">Yesterday 14:05</span>,
<span className="font-mono text-slate-300">meera@kafm.io</span>,
<span className="text-slate-200">
MFA reset by admin · device revoked
</span>,
<span className="font-mono text-slate-300">103.212.14.31</span>,
<StatusBadge
text="PENDING"
color="bg-amber-500/15 text-amber-400"
/>,
],
[
<span className="font-mono text-slate-300">Jun 30 09:12</span>,
<span className="font-mono text-slate-300">admin@kafm.io</span>,
<span className="text-slate-200">
Sign-in policy updated · idle timeout 30 min
</span>,
<span className="font-mono text-slate-300">103.212.14.8</span>,
<StatusBadge
text="OK"
color="bg-emerald-500/15 text-emerald-400"
/>,
],
];
export default function SecurityPortal() {
const [enforceMfa, setEnforceMfa] = useState(true);
const [keepMeSignedIn, setKeepMeSignedIn] = useState(true);
const [reAuthenticate, setReAuthenticate] = useState(false);
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 uppercase font-mono">
<span className="text-teal-400">PLATFORM</span>
<span className="text-slate-500"></span>
<span className="text-slate-500">SECURITY</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight mt-1">
Security & sessions
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-2xl leading-relaxed">
Platform-wide sign-in policy, trusted devices and the immutable authentication audit trail. Sign-ins, MFA, resets and policy changes are logged automatically.
</p>
</div>
{/* --- HEADER BUTTONS --- */}
<div className="flex items-center gap-3 self-start md:mt-2">
<Button className="bg-slate-900/80 hover:bg-slate-800 text-slate-200 border border-slate-700/60 text-xs px-4 py-2.5 rounded-lg flex items-center gap-2 font-semibold transition-colors">
<BarChart3 className="w-4 h-4 text-slate-300" />
<span>Export log</span>
</Button>
<Button className="bg-emerald-400 hover:bg-emerald-300 text-slate-950 font-semibold text-xs px-4 py-2.5 rounded-lg flex items-center gap-2 transition-colors shadow-lg shadow-emerald-500/10">
<span>Publish policy</span>
</Button>
</div>
</div>
{/* --- STAT CARDS --- */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<StatCard
variant="icon"
title="MFA coverage"
value="83%"
icon={Shield}
badgeText="of platform users"
badgeClassName="text-amber-400 lowercase font-mono"
iconContainerClassName="bg-emerald-950/40 border-emerald-800/30 text-emerald-400"
/>
<StatCard
variant="icon"
title="Active sessions"
value="12"
icon={Users}
badgeText="right now"
badgeClassName="text-slate-400 lowercase font-mono"
iconContainerClassName="bg-slate-800/50 border-slate-700/40 text-sky-400"
/>
<StatCard
variant="icon"
title="Failed sign-ins"
value="3"
icon={AlertTriangle}
badgeText="last 24 h"
badgeClassName="text-rose-400 lowercase font-mono"
iconContainerClassName="bg-rose-950/40 border-rose-800/30 text-rose-400"
/>
<StatCard
variant="icon"
title="Trusted devices"
value="3"
icon={ShieldCheck}
badgeText="MFA skipped"
badgeClassName="text-slate-400 lowercase font-mono"
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-400"
/>
</div>
{/* --- TOP GRID SECTION: AUDIT LOG + SIGN-IN POLICY --- */}
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6 mb-6">
{/* --- LEFT: AUDIT LOG TABLE CONTAINER --- */}
<div className="lg:col-span-8 bg-slate-900/60 border border-slate-800/80 rounded-xl p-5 flex flex-col justify-between space-y-4">
<div>
<div className="mb-4">
<h2 className="text-base font-semibold text-white tracking-tight">
Authentication audit trail
</h2>
<div className="flex items-center gap-2 text-[11px] font-mono text-slate-400 mt-0.5">
<span>immutable</span>
<span></span>
<span>90-day retention</span>
</div>
</div>
{/* AUDIT LOG TABLE */}
<Table headers={auditLogHeaders} rows={auditLogRows} />
</div>
</div>
{/* --- RIGHT: SIGN-IN POLICY PANEL --- */}
<div className="lg:col-span-4 bg-slate-900/60 border border-slate-800/80 rounded-xl p-5 space-y-6">
<h2 className="text-base font-semibold text-white tracking-tight">
Sign-in policy
</h2>
<div className="space-y-6 text-sm">
{/* Toggle 1: Enforce MFA */}
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-semibold text-slate-100">Enforce MFA for all users</p>
<p className="text-xs text-slate-400 leading-snug mt-0.5">
6-digit code required on every untrusted device
</p>
</div>
<button
onClick={() => setEnforceMfa(!enforceMfa)}
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${enforceMfa ? 'bg-teal-400' : 'bg-slate-800'}`}
>
<span className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-slate-950 shadow ring-0 transition duration-200 ease-in-out ${enforceMfa ? 'translate-x-5' : 'translate-x-0'}`} />
</button>
</div>
{/* Select 1: Idle Session Timeout */}
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-semibold text-slate-100">Idle session timeout</p>
<p className="text-xs text-slate-400 leading-snug mt-0.5">
Auto sign-out after inactivity
</p>
</div>
<div className="relative shrink-0">
<select className="appearance-none bg-slate-950/80 border border-slate-800 rounded-lg pl-3 pr-8 py-1.5 text-xs text-slate-200 font-medium focus:outline-none focus:border-slate-700 cursor-pointer">
<option value="30">30 minutes</option>
<option value="15">15 minutes</option>
<option value="60">1 hour</option>
</select>
<ChevronDown className="absolute right-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-slate-400 pointer-events-none" />
</div>
</div>
{/* Toggle 2: Keep me signed in */}
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-semibold text-slate-100">Keep me signed in by default</p>
<p className="text-xs text-slate-400 leading-snug mt-0.5">
Pre-checks the persistent-session option at sign-in
</p>
</div>
<button
onClick={() => setKeepMeSignedIn(!keepMeSignedIn)}
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${keepMeSignedIn ? 'bg-teal-400' : 'bg-slate-800'}`}
>
<span className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-slate-950 shadow ring-0 transition duration-200 ease-in-out ${keepMeSignedIn ? 'translate-x-5' : 'translate-x-0'}`} />
</button>
</div>
{/* Toggle 3: Re-authenticate */}
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-semibold text-slate-100">Re-authenticate for sensitive actions</p>
<p className="text-xs text-slate-400 leading-snug mt-0.5">
Password prompt before user, role or policy changes
</p>
</div>
<button
onClick={() => setReAuthenticate(!reAuthenticate)}
className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${reAuthenticate ? 'bg-teal-400' : 'bg-slate-800'}`}
>
<span className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-slate-950 shadow ring-0 transition duration-200 ease-in-out ${reAuthenticate ? 'translate-x-5' : 'translate-x-0'}`} />
</button>
</div>
{/* Select 2: Trusted device window */}
<div className="flex items-center justify-between gap-4">
<div>
<p className="font-semibold text-slate-100">Trusted device window</p>
<p className="text-xs text-slate-400 leading-snug mt-0.5">
Days MFA is skipped on a trusted device
</p>
</div>
<div className="relative shrink-0">
<select className="appearance-none bg-slate-950/80 border border-slate-800 rounded-lg pl-3 pr-8 py-1.5 text-xs text-slate-200 font-medium focus:outline-none focus:border-slate-700 cursor-pointer">
<option value="30">30</option>
<option value="60">60</option>
<option value="90">90</option>
</select>
<ChevronDown className="absolute right-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-slate-400 pointer-events-none" />
</div>
</div>
</div>
</div>
</div>
{/* --- BOTTOM SECTION: TRUSTED DEVICES TABLE CONTAINER --- */}
<div className="bg-slate-900/60 border border-slate-800/80 rounded-xl p-5 mb-6 space-y-4">
<div className="flex items-start justify-between">
<div>
<h2 className="text-base font-semibold text-white tracking-tight">
Trusted devices
</h2>
<p className="text-[11px] font-mono text-slate-400 mt-0.5">
MFA skipped until expiry
</p>
</div>
<Button className="rounded-md border border-slate-700/80 bg-slate-800/50 px-3.5 py-1.5 text-xs font-semibold text-slate-200 hover:bg-slate-800 transition-colors">
Revoke all
</Button>
</div>
{/* TRUSTED DEVICES TABLE */}
<Table headers={trustedDeviceHeaders} rows={trustedDeviceRows} />
<p className="text-xs text-slate-400 pt-1">
Revoking forces MFA on the next sign-in from that device. Password changes revoke all devices automatically.
</p>
</div>
{/* --- INFO BANNER --- */}
<div className="mt-6">
<InfoBanner
boldText='Every event is logged:'
normalText='the audit trail above updates live — try signing out and back in, requesting a password reset, or trusting a device on the login screen.'
/>
</div>
</div>
);
}

View File

@@ -0,0 +1,228 @@
import { useState } from 'react';
import {
Building2,
Users,
MapPin,
Grid,
Plus,
Eye,
Settings,
Pencil,
GitBranch,
Search,
ChevronDown,
Sparkles
} from "lucide-react";
import Button from "@/components/common/button";
import Table from '@/components/common/dataTable';
import { StatusBadge } from '@/components/common/statusBadge';
import ActionButtons from '@/components/common/actionButton';
export const siteHeaders = [
"Site",
"Tenant",
"Coordinates",
"Radius",
"Status",
"Actions",
];
export const siteRows = [
[
<div>
<p className="font-semibold text-slate-100">Apple One HQ Tower</p>
<p className="font-mono text-xs text-slate-400">AO-HQ · SIT-3001</p>
</div>,
"Apple One",
<span className="font-mono whitespace-nowrap">19.0760, 72.8777</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">200 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
[
<div>
<p className="font-semibold text-slate-100">Apple One Annexe</p>
<p className="font-mono text-xs text-slate-400">AO-AX · SIT-3002</p>
</div>,
"Apple One",
<span className="font-mono whitespace-nowrap">19.1020, 72.8895</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">150 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
[
<div>
<p className="font-semibold text-slate-100">Novotel Pune Main</p>
<p className="font-mono text-xs text-slate-400">NP-M · SIT-3003</p>
</div>,
"Novotel Pune",
<span className="font-mono whitespace-nowrap">18.5604, 73.9090</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">180 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
[
<div>
<p className="font-semibold text-slate-100">
SRM Campus Academic Block
</p>
<p className="font-mono text-xs text-slate-400">SRM-AB · SIT-3004</p>
</div>,
"SRM Campus",
<span className="font-mono whitespace-nowrap">12.8230, 80.0444</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">400 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
[
<div>
<p className="font-semibold text-slate-100">
SRM Campus Hostel Block
</p>
<p className="font-mono text-xs text-slate-400">SRM-HB · SIT-3005</p>
</div>,
"SRM Campus",
<span className="font-mono whitespace-nowrap">12.8255, 80.0478</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">350 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
[
<div>
<p className="font-semibold text-slate-100">Local 1 Site</p>
<p className="font-mono text-xs text-slate-400">L1-S · SIT-3006</p>
</div>,
"Local 1",
<span className="font-mono whitespace-nowrap">19.2183, 72.9781</span>,
<span className="rounded-md bg-slate-900 px-2 py-1 font-mono text-xs">120 m</span>,
<div className="flex items-center gap-3">
<div className="h-7 w-12 rounded-full bg-teal-400 relative">
<div className="absolute right-1 top-1 h-5 w-5 rounded-full bg-slate-950" />
</div>
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>
</div>,
<ActionButtons />,
],
];
export default function SitePortal() {
const [searchQuery, setSearchQuery] = useState('');
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 uppercase font-mono">
<span className="text-teal-400">PLATFORM</span>
<span className="text-slate-500"></span>
<span className="text-slate-500">SITES</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight mt-1">
Sites
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-2xl leading-relaxed">
Physical locations under each tenant. Define geofences with map coordinates and a working radius. Operations site pickers read from here.
</p>
</div>
{/* --- HEADER BUTTON --- */}
<div className="flex items-center gap-3 self-start md:mt-2">
<Button className="bg-emerald-400 hover:bg-emerald-300 text-slate-950 font-semibold text-xs px-4 py-2.5 rounded-lg flex items-center gap-1.5 transition-colors shadow-lg shadow-emerald-500/10">
<Plus className="w-4 h-4 text-slate-950 stroke-[3]" />
<span>Add site</span>
</Button>
</div>
</div>
{/* --- TABLE CONTAINER WITH UPPER BAR --- */}
<div className="bg-slate-900/60 border border-slate-800 rounded-xl p-4 space-y-4">
{/* Table Upper UI (Search Bar & Filter Dropdown) */}
<div className="flex flex-col sm:flex-row items-center gap-3">
{/* Search Bar Input */}
<div className="relative flex-1 w-full">
<Search className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search sites..."
className="w-full pl-10 pr-4 py-2 bg-slate-950/80 border border-slate-800 rounded-lg text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:border-slate-700 transition"
/>
</div>
{/* All Tenants Filter Select Dropdown */}
<div className="relative w-full sm:w-44">
<select className="w-full appearance-none bg-slate-950/80 border border-slate-800 rounded-lg px-3.5 py-2 pr-8 text-sm text-slate-300 font-medium focus:outline-none focus:border-slate-700 cursor-pointer">
<option value="all">All tenants</option>
<option value="apple-one">Apple One</option>
<option value="novotel">Novotel Pune</option>
<option value="srm">SRM Campus</option>
<option value="local1">Local 1</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
{/* --- TABLE --- */}
<Table headers={siteHeaders} rows={siteRows} />
{/* Footer Record Count */}
<div className="text-xs text-slate-500 font-mono pt-1">
{siteRows.length} records
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,244 @@
import { useState } from 'react';
import {
Building2,
Users,
MapPin,
Grid,
Plus,
Eye,
Settings,
Pencil,
GitBranch,
Search,
ChevronDown
} from "lucide-react";
import Button from "@/components/common/button";
import StatCard from "@/components/common/dashStatCard";
import Table from '@/components/common/dataTable';
import { StatusBadge } from '@/components/common/statusBadge';
export const organizationHeaders = [
"Organization",
"Tenant ID",
"Contact",
"Time Zone",
"Status",
"Actions",
];
const ActionButtons = () => (
<div className="flex items-center justify-center gap-1">
<Button className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<Eye size={15} />
</Button>
<Button className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<Settings size={15} />
</Button>
<Button className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<GitBranch size={15} />
</Button>
<Button className="flex h-8 w-8 items-center justify-center rounded-md border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<Pencil size={15} />
</Button>
</div>
);
export const organizationRows = [
[
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-emerald-500/15 font-semibold text-emerald-400">
AO
</div>
<div>
<div className="font-semibold text-slate-100">Apple One</div>
<div className="text-slate-400">ops@appleone.com</div>
</div>
</div>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
TNT-1001
</span>,
<div>
<div className="font-semibold text-slate-100">Dhananjay T.</div>
<div className="text-sky-300">+91 98200 10001</div>
</div>,
<span className="font-mono">Asia/Kolkata</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons />,
],
[
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-blue-500/15 font-semibold text-blue-400">
NP
</div>
<div>
<div className="font-semibold text-slate-100">Novotel Pune</div>
<div className="text-slate-400">fm@novotelpune.com</div>
</div>
</div>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
TNT-1002
</span>,
<div>
<div className="font-semibold text-slate-100">Site FM Lead</div>
<div className="text-sky-300">+91 98220 20002</div>
</div>,
<span className="font-mono">Asia/Kolkata</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons />,
],
[
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-blue-500/15 font-semibold text-blue-400">
SC
</div>
<div>
<div className="font-semibold text-slate-100">SRM Campus</div>
<div className="text-slate-400">estates@srm.edu</div>
</div>
</div>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
TNT-1003
</span>,
<div>
<div className="font-semibold text-slate-100">Estates Office</div>
<div className="text-sky-300">+91 98430 30003</div>
</div>,
<span className="font-mono">Asia/Kolkata</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons />,
],
[
<div className="flex items-center gap-3">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-blue-500/15 font-semibold text-blue-400">
L1
</div>
<div>
<div className="font-semibold text-slate-100">Local 1</div>
<div className="text-slate-400">admin@local1.com</div>
</div>
</div>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
TNT-1004
</span>,
<div>
<div className="font-semibold text-slate-100">Local Admin</div>
<div className="text-sky-300">+91 98000 40004</div>
</div>,
<span className="font-mono">Asia/Kolkata</span>,
<StatusBadge
text="Active"
color="bg-emerald-500/15 text-emerald-400"
/>,
<ActionButtons />,
],
];
export default function TenantsPortal() {
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 uppercase font-mono">
<span className="text-teal-400">PLATFORM</span>
<span className="text-slate-500"></span>
<span className="text-slate-500">TENANTS</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight mt-1">
Tenants
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-xl leading-relaxed">
Every organization provisioned on the platform. Manage onboarding, feature access, branding, and status.
</p>
</div>
{/* --- HEADER BUTTON --- */}
<div className="flex items-center gap-3 self-start md:mt-2">
<Button className="bg-emerald-400 hover:bg-emerald-300 text-slate-950 font-semibold text-xs px-4 py-2.5 rounded-lg flex items-center gap-1.5 transition-colors shadow-lg shadow-emerald-500/10">
<Plus className="w-4 h-4 text-slate-950 stroke-[3]" />
<span>Add tenant</span>
</Button>
</div>
</div>
{/* --- TABLE CONTAINER WITH UPPER BAR --- */}
<div className="bg-slate-900/60 border border-slate-800 p-4 space-y-4">
{/* Table Upper UI (Search Bar & Filter Dropdown) */}
<div className="flex flex-col sm:flex-row items-center gap-3">
{/* Search Bar Input */}
<div className="relative flex-1 w-full">
<Search className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
placeholder="Search by name, ID or contact..."
className="w-full pl-10 pr-4 py-2 border border-slate-800 rounded-lg text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:border-slate-700 transition"
/>
</div>
{/* All Statuses Filter Select Dropdown */}
<div className="relative w-full sm:w-44">
<select className="w-full appearance-none bg-slate-950/80 border border-slate-800 rounded-lg px-3.5 py-2 pr-8 text-sm text-slate-300 font-medium focus:outline-none focus:border-slate-700 cursor-pointer">
<option value="all">All statuses</option>
<option value="active">Active</option>
<option value="inactive">Inactive</option>
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none" />
</div>
</div>
{/* --- TABLE --- */}
<Table headers={organizationHeaders} rows={organizationRows} />
{/* Footer Record Count */}
<div className="text-xs text-slate-500 font-mono pt-1">
{organizationRows.length} records
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,305 @@
import { useState } from 'react';
import {
Plus,
Eye,
KeyRound,
Search,
Sparkles
} from "lucide-react";
import Button from "@/components/common/button";
import Table from '@/components/common/dataTable';
import { StatusBadge } from '@/components/common/statusBadge';
export const userHeaders = [
"Name",
"Email",
"Phone",
"Tenants",
"MFA",
"User ID",
"Actions",
];
const UserActionButtons = () => (
<div className="flex items-center justify-center gap-2">
<Button className="flex h-9 w-9 items-center justify-center rounded-xl border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<Eye size={16} />
</Button>
<Button className="flex h-9 w-9 items-center justify-center rounded-xl border border-slate-700/60 text-slate-400 hover:bg-slate-800 hover:text-white transition">
<KeyRound size={16} />
</Button>
</div>
);
export const userRows = [
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-amber-500/15 font-semibold text-amber-400">
DT
</div>
<span className="font-semibold text-slate-100">
Dhananjay T.
</span>
</div>,
<span className="font-semibold text-slate-100">
dhananjay@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98200 10001
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
4 assigned
</span>,
<StatusBadge
text="On"
color="bg-emerald-500/15 text-emerald-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5001
</span>,
<UserActionButtons />,
],
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-cyan-500/15 font-semibold text-cyan-400">
MS
</div>
<span className="font-semibold text-slate-100">
ME Supervisor
</span>
</div>,
<span className="font-semibold text-slate-100">
me.local@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98201 10010
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
1 assigned
</span>,
<StatusBadge
text="On"
color="bg-emerald-500/15 text-emerald-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5002
</span>,
<UserActionButtons />,
],
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-amber-500/15 font-semibold text-amber-400">
T
</div>
<span className="font-semibold text-slate-100">
Technician
</span>
</div>,
<span className="font-semibold text-slate-100">
tech.local@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98202 10011
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
2 assigned
</span>,
<StatusBadge
text="Off"
color="bg-slate-700/50 text-slate-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5003
</span>,
<UserActionButtons />,
],
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-rose-500/15 font-semibold text-rose-400">
TC
</div>
<span className="font-semibold text-slate-100">
Tenant Customer
</span>
</div>,
<span className="font-semibold text-slate-100">
tenant@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98203 10012
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
1 assigned
</span>,
<StatusBadge
text="On"
color="bg-emerald-500/15 text-emerald-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5004
</span>,
<UserActionButtons />,
],
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-violet-500/15 font-semibold text-violet-400">
HA
</div>
<span className="font-semibold text-slate-100">
Helpdesk Agent
</span>
</div>,
<span className="font-semibold text-slate-100">
help@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98204 10013
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
2 assigned
</span>,
<StatusBadge
text="On"
color="bg-emerald-500/15 text-emerald-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5005
</span>,
<UserActionButtons />,
],
[
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-orange-500/15 font-semibold text-orange-400">
EA
</div>
<span className="font-semibold text-slate-100">
Energy Analyst
</span>
</div>,
<span className="font-semibold text-slate-100">
energy@kafm.io
</span>,
<span className="font-mono text-sky-300">
+91 98430 30030
</span>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
1 assigned
</span>,
<StatusBadge
text="On"
color="bg-emerald-500/15 text-emerald-400"
/>,
<span className="rounded-md border border-slate-700 bg-slate-900 px-3 py-1 font-mono text-slate-300">
USR-5006
</span>,
<UserActionButtons />,
],
];
export default function UserPortal() {
const [searchQuery, setSearchQuery] = useState('');
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 uppercase font-mono">
<span className="text-teal-400">PLATFORM</span>
<span className="text-slate-500"></span>
<span className="text-slate-500">USERS</span>
</div>
<h1 className="text-3xl font-bold text-white tracking-tight mt-1">
Users
</h1>
<p className="text-sm font-medium text-slate-400 mt-1 max-w-2xl leading-relaxed">
All platform user accounts. Create credentials, assign tenants, and manage passwords. This is the single account store Operations role access reads from here.
</p>
</div>
{/* --- HEADER BUTTON --- */}
<div className="flex items-center gap-3 self-start md:mt-2">
<Button className="bg-emerald-400 hover:bg-emerald-300 text-slate-950 font-semibold text-xs px-4 py-2.5 rounded-lg flex items-center gap-1.5 transition-colors shadow-lg shadow-emerald-500/10">
<Plus className="w-4 h-4 text-slate-950 stroke-[3]" />
<span>Add user</span>
</Button>
</div>
</div>
{/* --- TABLE CONTAINER WITH UPPER BAR --- */}
<div className="bg-slate-900/60 border border-slate-800/80 rounded-xl p-4 space-y-4">
{/* Table Upper UI (Full-width Search Bar matching screenshot) */}
<div className="flex items-center w-full">
<div className="relative w-full">
<Search className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search users by name, email or ID..."
className="w-full pl-10 pr-4 py-2.5 bg-slate-950/80 border border-slate-800 rounded-lg text-sm text-slate-200 placeholder-slate-500 focus:outline-none focus:border-slate-700 transition"
/>
</div>
</div>
{/* --- TABLE --- */}
<Table headers={userHeaders} rows={userRows} />
{/* Footer Record Count */}
<div className="text-xs text-slate-500 font-mono pt-1">
{userRows.length} records
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,31 @@
"use client";
import React, { createContext, useContext, useState } from "react";
type Mode = "operations" | "admin";
interface NavContextType {
mode: Mode;
setMode: (mode: Mode) => void;
}
// 1. Provide default values so useContext never returns null/undefined
const NavContext = createContext<NavContextType>({
mode: "operations",
setMode: () => {},
});
export function NavProvider({ children }: { children: React.ReactNode }) {
const [mode, setMode] = useState<Mode>("operations");
return (
<NavContext.Provider value={{ mode, setMode }}>
{children}
</NavContext.Provider>
);
}
export function useNav() {
const context = useContext(NavContext);
return context;
}

View File

@@ -1,31 +1,49 @@
import { Search, HelpCircle, Globe, Sun, Bell, ChevronDown, MapPin } from 'lucide-react'; "use client";
import { Search, HelpCircle, Globe, Sun, Bell, ChevronDown, MapPin, ShieldCheck } from "lucide-react";
import { useNav } from "./context/navContext";
export default function Navbar() { export default function Navbar() {
return ( const { mode } = useNav();
/*
- '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">
return (
<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">
{mode === "operations" ? (
/* ================= OPERATIONS NAVBAR ================= */
<>
{/* Left Group */} {/* Left Group */}
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{/* Company Selector */} {/* 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"> <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 relative">
<span className="h-2 w-2 rounded-full bg-[#10b981] shrink-0"></span> <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>
<select
className=" bg-[#0a1826] outline-none cursor-pointer font-medium pr-5"
defaultValue="Apple One"
>
<option>Apple One</option>
<option>Novotel Pune</option>
<option>SRM Campus</option>
<option>Local 1</option>
</select>
<ChevronDown className="absolute right-3 h-3.5 w-3.5 pointer-events-none" />
</div>
{/* Location Selector */} {/* 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"> <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 relative">
<MapPin className="h-4 w-4 text-gray-500 shrink-0" /> <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> <select
</span> className=" bg-[#0a1826] outline-none cursor-pointer font-medium pr-5"
<ChevronDown className="h-4 w-4 text-gray-500 shrink-0" /> defaultValue="Apple One — HQ Tower"
>
<option>Apple One HQ Tower</option>
<option>Apple One Annexe</option>
</select>
<ChevronDown className="absolute right-4 h-4 w-4 text-gray-500 pointer-events-none" />
</div> </div>
{/* Search Bar */} {/* Search Bar */}
@@ -41,31 +59,26 @@ export default function Navbar() {
{/* Right Utilities */} {/* Right Utilities */}
<div className="flex items-center gap-3"> <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]"> <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" /> <HelpCircle className="h-4 w-4 text-gray-400" />
<span>Tour</span> <span>Tour</span>
</button> </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]"> <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" /> <Globe className="h-4 w-4 text-gray-400" />
<span>English</span> <span>English</span>
<ChevronDown className="h-4 w-4 text-gray-500" /> <ChevronDown className="h-4 w-4 text-gray-500" />
</div> </div>
{/* Live Indicator */}
<div className="flex items-center gap-2 px-1 text-sm font-bold text-[#10b981] tracking-wider"> <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="h-1.5 w-1.5 rounded-full bg-[#10b981]"></span>
<span className="text-xs font-bold text-gray-400">LIVE</span> <span className="text-xs font-bold text-gray-400">LIVE</span>
</div> </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"> <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" /> <Sun className="h-4 w-4" />
</button> </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"> <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" /> <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"> <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">
@@ -73,11 +86,48 @@ export default function Navbar() {
</span> </span>
</div> </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"> <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 DH
</div> </div>
</div> </div>
</>
) : (
/* ================= ADMIN NAVBAR ================= */
<>
{/* Left Breadcrumb */}
<div className="flex items-center gap-2 text-sm text-slate-300">
<ShieldCheck size={18} className="text-[#3de0c4]" />
<span className="font-bold text-white">Administration</span>
<span className="text-slate-500">&rsaquo;</span>
<span className="text-slate-400">Mobile Flows</span>
</div>
{/* Right Utilities */}
<div className="flex items-center gap-3">
<div className="relative w-64">
<Search className="absolute left-3 top-2.5 h-4 w-4 text-slate-400" />
<input
type="text"
placeholder="Search tenants, users, sites..."
className="w-full rounded-lg bg-[#0a1826] pl-9 pr-4 py-1.5 text-xs text-white placeholder-slate-500 border border-[#132338] focus:outline-none focus:border-[#3de0c4]/50"
/>
</div>
<div className="flex items-center gap-1.5 rounded-full border border-teal-500/30 bg-teal-500/10 px-3 py-1 text-xs font-bold text-[#3de0c4]">
<span className="h-2 w-2 rounded-full bg-[#3de0c4]"></span>
<span>PRODUCTION &bull; us-east-1</span>
</div>
<button className="flex h-8 w-8 items-center justify-center rounded-lg border border-[#132338] bg-[#0a1826] text-slate-400 hover:text-white transition">
<Sun className="h-4 w-4" />
</button>
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-[#1b3454] text-xs font-bold text-white">
SA
</div>
</div>
</>
)}
</header> </header>
); );
} }

View File

@@ -1,65 +1,78 @@
import { SIDEBAR_ITEMS } from "@/constants/constant"; "use client";
import { OPERATIONS_SIDEBAR_ITEMS, ADMIN_SIDEBAR_ITEMS } from "@/constants/constant";
import SidebarItem from "./sideBarItem"; import SidebarItem from "./sideBarItem";
import { import { ShieldCheck, Activity, ChevronDown, ChevronsLeft } from "lucide-react";
ShieldCheck,
Activity,
ChevronDown,
ChevronsLeft,
} from "lucide-react";
import { useSession, signOut } from "next-auth/react"; import { useSession, signOut } from "next-auth/react";
import { useNav } from "./context/navContext";
export default function Sidebar() { export default function Sidebar() {
const { data: session } = useSession(); const { data: session } = useSession();
console.log("Session data ",session?.user?.name) const { mode, setMode } = useNav();
console.log("Session data ",session?.user?.email)
const currentItems = mode === "operations" ? OPERATIONS_SIDEBAR_ITEMS : ADMIN_SIDEBAR_ITEMS;
return ( return (
<aside className="fixed bg-[#071321] text-white h-screen overflow-y-auto [scrollbar-color:#1b2a40_#071321] [scrollbar-width:thin]"> <aside className="fixed bg-[#071321] text-white h-screen w-64 overflow-y-auto [scrollbar-color:#1b2a40_#071321] [scrollbar-width:thin]">
<div className="flex items-center gap-2 px-2 pt-2 pb-3"> {/* App Logo */}
<div className="flex h-9 w-9 items-center justify-center rounded-lg <div className="flex items-center gap-2 px-4 pt-3 pb-3">
bg-[#3de0c4] font-bold text-[#05111d] text-sm"> <div className="flex h-9 w-9 items-center justify-center rounded-lg bg-[#3de0c4] font-bold text-[#05111d] text-sm">
K K
</div> </div>
<span className="text-sm font-bold tracking-tight text-white"> <span className="text-sm font-bold tracking-tight text-white">
KA<span className="text-[#3de0c4]">FM</span> KA<span className="text-[#3de0c4]">FM</span>
</span> </span>
</div> </div>
<hr className="border-[#142538]" /> <hr className="border-[#142538]" />
<div className="mx-4 my-4 flex rounded-xl
bg-[#091624] p-2 border border-[#142538]"> {/* Mode Switcher Buttons */}
<button className="flex flex-1 <div className="mx-4 my-4 flex rounded-xl bg-[#091624] p-1 border border-[#142538]">
items-center justify-center gap-1 rounded-lg bg-[#3de0c4] py-1.5 text-xs <button
text-black font-bold shadow-sm"> onClick={() => setMode("operations")}
className={`flex flex-1 items-center justify-center gap-1 rounded-lg py-1.5 text-xs font-bold transition-all ${
mode === "operations"
? "bg-[#3de0c4] text-black shadow-sm"
: "text-slate-400 hover:text-white"
}`}
>
<Activity size={14} /> <Activity size={14} />
Operations Operations
</button> </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"> <button
onClick={() => setMode("admin")}
className={`flex flex-1 items-center justify-center gap-1 rounded-lg py-1.5 text-xs font-bold transition-all ${
mode === "admin"
? "bg-[#3de0c4] text-black shadow-sm"
: "text-slate-400 hover:text-white"
}`}
>
<ShieldCheck size={14} /> <ShieldCheck size={14} />
Admin Admin
</button> </button>
</div> </div>
<p className="px-2 text-xs font-semibold uppercase <p className="px-4 text-[10px] font-semibold uppercase tracking-wider text-slate-500">
tracking-wider text-slate-500"> {mode === "operations" ? "Tenant Runtime" : "System Platform"}
Tenant runtime
</p> </p>
<div className="py-1">
{SIDEBAR_ITEMS.map((item) => (
<SidebarItem
key={item.title}
item={item}
/> {/* Dynamic Navigation Items */}
<div className="py-1">
{currentItems.map((item) => (
<SidebarItem key={item.title} item={item} />
))} ))}
</div> </div>
<div className="px-3 pb-2 pt-1
border-t border-[#0f1f30]"> <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"> <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} /> <ChevronsLeft size={16} />
Minimise menu Minimise menu
</button> </button>
</div> </div>
{/* Profile Card Section */} {/* Profile Card */}
<div className="m-3 mt-0 rounded-xl border border-[#142538] bg-[#071321] p-2.5"> <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 items-center justify-between">
<div className="flex gap-2.5 items-center"> <div className="flex gap-2.5 items-center">
@@ -67,14 +80,17 @@ console.log("Session data ",session?.user?.email)
DT DT
</div> </div>
<div className="min-w-0 leading-tight"> <div className="min-w-0 leading-tight">
<h4 className="font-semibold text-xs text-white truncate">{session?.user?.name}</h4> <h4 className="font-semibold text-xs text-white truncate">{session?.user?.name || "User"}</h4>
<p className="text-[11px] text-slate-500 truncate"> <p className="text-[11px] text-slate-500 truncate">
{session?.user?.email} {session?.user?.email || "user@example.com"}
</p> </p>
</div> </div>
</div> </div>
<ChevronDown className="-rotate-90 text-slate-500 shrink-0" size={14} <ChevronDown
onClick={() => signOut({ callbackUrl: "/login" })} /> className="-rotate-90 text-slate-500 shrink-0 cursor-pointer"
size={14}
onClick={() => signOut({ callbackUrl: "/login" })}
/>
</div> </div>
</div> </div>
</aside> </aside>

View File

@@ -1,5 +1,4 @@
import { TableRowData } from '@/types/types';
import { import {
LayoutGrid, LayoutGrid,
Smile, Smile,
@@ -36,11 +35,16 @@ import {
Wallet, Wallet,
Radar, Radar,
Cog, Cog,
Smartphone,
Users,
Database,
Globe,
LayoutDashboard,
} from "lucide-react"; } from "lucide-react";
//Side bar Drop Down menu //Side bar Drop Down menu
export const SIDEBAR_ITEMS = [ export const OPERATIONS_SIDEBAR_ITEMS = [
{ {
title: "Overview", title: "Overview",
icon: LayoutGrid, icon: LayoutGrid,
@@ -130,6 +134,31 @@ export const SIDEBAR_ITEMS = [
{ title: "Settings", href: "/configure/settings", icon: Sun },], { title: "Settings", href: "/configure/settings", icon: Sun },],
}, },
]; ];
export const ADMIN_SIDEBAR_ITEMS = [
{
title: "Platform",
icon: Building2,
children: [
{ title: "Overview", href: "/admin/overview", icon: LayoutGrid },
{ title: "Tenants", href: "/admin/tenants", icon: Building2 },
{ title: "Users", href: "/admin/users", icon: Users },
{ title: "Sites", href: "/admin/sites", icon: MapPin },
{ title: "Security", href: "/admin/security", icon: Shield },
],
},
{
title: "Configuration",
icon: LayoutGrid,
children: [
{ title: "Modules", href: "/admin/configuration/modules", icon: LayoutGrid },
{ title: "Language", href: "/admin/configuration/language", icon: Globe },
{ title: "Dynamic Dashboard", href: "/admin/configuration/dynamic-dashboard", icon: LayoutDashboard },
{ title: "Mobile Flows", href: "/admin/configuration/mobile-flows", icon: Smartphone },
],
},
];
//Overview dashboard data //Overview dashboard data
@@ -147,6 +176,8 @@ export const chartData = [
{ month: 'N', height: 'h-26' }, { month: 'N', height: 'h-26' },
{ month: 'D', height: 'h-24' }, { month: 'D', height: 'h-24' },
]; ];
export const energyData = [ export const energyData = [
{ month: "Jan", height: 'h-20' }, { month: "Jan", height: 'h-20' },
{ month: "Feb", height: 'h-12' }, { month: "Feb", height: 'h-12' },
@@ -197,8 +228,15 @@ export const equipmentTabs: string[] = [
'AMC Schedule', 'AMC Schedule',
'Maintenance Calendar' 'Maintenance Calendar'
]; ];
export const helpdeskTtabs: string[] = ['Location', 'Technical',
'Missed', 'Departments', 'HSQE'];
export const helpdeskTtabs: string[] =
['Location',
'Technical',
'Missed',
'Departments',
'HSQE'];