Operations dashboard screens are added
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use client"// app/dashboard/layout.tsx
|
"use client"// app/dashboard/layout.tsx
|
||||||
import Sidebar from "@/components/dashboard/sidebar";
|
import Sidebar from "@/utils/sidebar";
|
||||||
import Navbar from "@/components/dashboard/navbar";
|
import Navbar from "@/utils/navbar";
|
||||||
import { Sparkles } from "lucide-react";
|
import { Sparkles } from "lucide-react";
|
||||||
|
|
||||||
export default function DashboardLayout({
|
export default function DashboardLayout({
|
||||||
@@ -14,7 +14,7 @@ export default function DashboardLayout({
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
|
||||||
<div className="flex-1 flex flex-col min-h-screen">
|
<div className="flex-1 flex flex-col min-h-screen">
|
||||||
{/* Shared Navbar */}
|
{/*Common Navbar for every webpage */}
|
||||||
<Navbar />
|
<Navbar />
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ export default function DashboardLayout({
|
|||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Shared Ask AI Floating Button */}
|
{/* Ask AI Floating Button common */}
|
||||||
<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">
|
||||||
|
|||||||
14
app/dashboard/operations/equipment/page.tsx
Normal file
14
app/dashboard/operations/equipment/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import EquipmentPortal from "@/components/dashboard/equipment"
|
||||||
|
export default function HelpDeskPage() {
|
||||||
|
return (
|
||||||
|
|
||||||
|
|
||||||
|
<div className="p-8 mx-3">
|
||||||
|
<EquipmentPortal/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
14
app/dashboard/operations/helpDesk/page.tsx
Normal file
14
app/dashboard/operations/helpDesk/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import HelpDeskPortal from "@/components/dashboard/helpDesk"
|
||||||
|
export default function HelpDeskPage() {
|
||||||
|
return (
|
||||||
|
|
||||||
|
|
||||||
|
<div className="p-8 mx-3">
|
||||||
|
<HelpDeskPortal/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
0
app/dashboard/operations/inventory/page.tsx
Normal file
0
app/dashboard/operations/inventory/page.tsx
Normal file
14
app/dashboard/operations/request/page.tsx
Normal file
14
app/dashboard/operations/request/page.tsx
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import ServiceRequestsPortal from "@/components/dashboard/request"
|
||||||
|
export default function ServiceRequestPage() {
|
||||||
|
return (
|
||||||
|
|
||||||
|
|
||||||
|
<div className="p-8 mx-3">
|
||||||
|
<ServiceRequestsPortal/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ export default function OverViewDashboardPage() {
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-8 mx-3">
|
||||||
<BuildingOwnerPortal/>
|
<BuildingOwnerPortal/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export default function OverViewDashboardPage() {
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-8 mx-3">
|
||||||
<OperationsDashboard />
|
<OperationsDashboard />
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default function EngagementDashboard() {
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-8 mx-3">
|
||||||
<EngagementPage/>
|
<EngagementPage/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default function EngagementDashboard() {
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-8 mx-3">
|
||||||
<PropertyManagerPortal/>
|
<PropertyManagerPortal/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default function EngagementDashboard() {
|
|||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<div className="p-6">
|
<div className="p-8 mx-3 ">
|
||||||
<ServiceEngineerPortal/>
|
<ServiceEngineerPortal/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,8 @@ import { authOptions } from "@/app/api/auth/[...nextauth]/route"; // Changed fro
|
|||||||
export default async function DashboardBasePage() {
|
export default async function DashboardBasePage() {
|
||||||
|
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
|
// if user is not aunthenticated then its redirects to the
|
||||||
|
// login page else redirect to dashboard page
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,7 @@
|
|||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme inline {
|
|
||||||
--color-background: var(--background);
|
|
||||||
--color-foreground: var(--foreground);
|
|
||||||
--font-sans: var(--font-geist-sans);
|
|
||||||
--font-mono: var(--font-geist-mono);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import { AuthLayoutProps } from "@/constants/types";
|
||||||
import LeftPanel from "./leftpanel";
|
import LeftPanel from "./leftpanel";
|
||||||
|
|
||||||
interface AuthLayoutProps {
|
|
||||||
children: React.ReactNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function AuthLayout({ children }: AuthLayoutProps) {
|
export default function AuthLayout({ children }: AuthLayoutProps) {
|
||||||
return (
|
return (
|
||||||
<main className="min-h-screen grid lg:grid-cols-2 bg-[#081321]">
|
<main className="min-h-screen grid lg:grid-cols-2 bg-[#081321]">
|
||||||
|
{/* Left Panel of the KAFM page */}
|
||||||
<LeftPanel />
|
<LeftPanel />
|
||||||
|
|
||||||
|
|
||||||
|
{/* Login Page Right Side */}
|
||||||
<div className="flex items-center justify-center p-8">
|
<div className="flex items-center justify-center p-8">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,8 +3,11 @@ import Button from "@/utils/button";
|
|||||||
|
|
||||||
|
|
||||||
import {Props} from "@/constants/types"
|
import {Props} from "@/constants/types"
|
||||||
|
|
||||||
|
|
||||||
export default function ForgotPassword({ onBack }: Props) {
|
export default function ForgotPassword({ onBack }: Props) {
|
||||||
return (
|
return (
|
||||||
|
// Forget Password Screen
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<button
|
<button
|
||||||
onClick={onBack}
|
onClick={onBack}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function LeftPanel() {
|
|||||||
Unified Facility Platform
|
Unified Facility Platform
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Punchy Catchphrase */}
|
{/*Text*/}
|
||||||
<h1 className="max-w-lg text-4xl font-bold tracking-tight text-white leading-[1]">
|
<h1 className="max-w-lg text-4xl font-bold tracking-tight text-white leading-[1]">
|
||||||
One platform.
|
One platform.
|
||||||
<br />
|
<br />
|
||||||
@@ -67,7 +67,7 @@ export default function LeftPanel() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Global Statistics Footer */}
|
{/*StatCard is used */}
|
||||||
<div className=" flex gap-2 mt-10 text-slate-300 ">
|
<div className=" flex gap-2 mt-10 text-slate-300 ">
|
||||||
<Stat number="4" label="TENANTS" />
|
<Stat number="4" label="TENANTS" />
|
||||||
<Stat number="8" label="MODULES" />
|
<Stat number="8" label="MODULES" />
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ import { signIn } from "next-auth/react";
|
|||||||
|
|
||||||
|
|
||||||
export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
|
||||||
|
const [showPassword, setShowPassword] = useState(false);// For Visibility of Password
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<div className="w-full max-w-md space-y-3">
|
<div className="w-full max-w-md space-y-3">
|
||||||
@@ -32,6 +33,7 @@ export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
// Password Input
|
||||||
<FormInput
|
<FormInput
|
||||||
label="PASSWORD"
|
label="PASSWORD"
|
||||||
value=""
|
value=""
|
||||||
@@ -69,7 +71,7 @@ export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Actions / Submit Stack */}
|
{/* Action on Button */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<Button
|
<Button
|
||||||
onClick={onSuccess}
|
onClick={onSuccess}
|
||||||
@@ -78,7 +80,7 @@ export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
|||||||
Sign in
|
Sign in
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{/* Custom Visual Divider */}
|
{/* Divider */}
|
||||||
<div className="flex items-center gap-4 py-2">
|
<div className="flex items-center gap-4 py-2">
|
||||||
<div className="h-px flex-1 bg-slate-800" />
|
<div className="h-px flex-1 bg-slate-800" />
|
||||||
<span className="text-xs uppercase tracking-wider text-slate-600 font-medium">or</span>
|
<span className="text-xs uppercase tracking-wider text-slate-600 font-medium">or</span>
|
||||||
@@ -95,7 +97,7 @@ export default function LoginForm({ onSuccess,onForgotPassword }: Props) {
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bottom Legal Info */}
|
{/* Bottom Info */}
|
||||||
<p className="text-center text-xs tracking-wide text-slate-500 font-mono">
|
<p className="text-center text-xs tracking-wide text-slate-500 font-mono">
|
||||||
Protected by 2-factor auth • every sign-in is
|
Protected by 2-factor auth • every sign-in is
|
||||||
<br/> audit-logged
|
<br/> audit-logged
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {Props} from "@/constants/types"
|
|||||||
export default function VerifyCode({ onBack }: Props) {
|
export default function VerifyCode({ onBack }: Props) {
|
||||||
const [timer, setTimer] = useState(30);
|
const [timer, setTimer] = useState(30);
|
||||||
|
|
||||||
|
//Timer Functionality Sets 30s to 0s
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (timer === 0) return;
|
if (timer === 0) return;
|
||||||
|
|
||||||
@@ -21,7 +22,7 @@ export default function VerifyCode({ onBack }: Props) {
|
|||||||
const handleResend = () => {
|
const handleResend = () => {
|
||||||
if (timer > 0) return;
|
if (timer > 0) return;
|
||||||
|
|
||||||
// Call your resend OTP API here
|
// Call your resend OTP
|
||||||
|
|
||||||
setTimer(30);
|
setTimer(30);
|
||||||
};
|
};
|
||||||
@@ -49,6 +50,8 @@ export default function VerifyCode({ onBack }: Props) {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* OTP input box */}
|
||||||
<OTPInput />
|
<OTPInput />
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function BuildingOwnerPortal() {
|
|||||||
<div className="mt-20 font-sans antialiased">
|
<div className="mt-20 font-sans antialiased">
|
||||||
|
|
||||||
{/* --- HEADER SECTION --- */}
|
{/* --- HEADER SECTION --- */}
|
||||||
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-8">
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
<span className="text-teal-400">Apple One — HQ Tower</span>
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
@@ -33,7 +33,10 @@ export default function BuildingOwnerPortal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Button */}
|
{/* Action Button */}
|
||||||
<Button className="flex items-center gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538] rounded-xl text-xs font-semibold text-slate-200 transition-colors duration-200 shadow-sm self-start md:mt-6">
|
<Button className="flex items-center
|
||||||
|
gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538]
|
||||||
|
rounded-xl text-xs font-semibold text-slate-200 transition-colors
|
||||||
|
duration-200 shadow-sm self-start md:mt-15">
|
||||||
<FileSpreadsheet size={14} className="text-slate-400" />
|
<FileSpreadsheet size={14} className="text-slate-400" />
|
||||||
Portfolio report
|
Portfolio report
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -18,14 +18,16 @@ import { CardHeader } from "@/utils/cardHeader";
|
|||||||
|
|
||||||
export default function EngagementPage() {
|
export default function EngagementPage() {
|
||||||
return (
|
return (
|
||||||
<div className=" text-[#93a3b8] font-sans antialiased ">
|
<div className=" text-[#93a3b8] font-sans ">
|
||||||
|
|
||||||
<main className="mt-20">
|
<main className="mt-20">
|
||||||
|
|
||||||
{/* Page Header Section */}
|
{/* Page Header Section */}
|
||||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 mt-4">
|
<div className="flex flex-col md:flex-row md:items-center
|
||||||
|
justify-between gap-4 mt-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 text-xs font-bold uppercase tracking-widest text-teal-500">
|
<div className="flex items-center gap-2 text-xs
|
||||||
|
font-bold uppercase tracking-widest text-teal-500">
|
||||||
<span>Apple One — HQ Tower</span>
|
<span>Apple One — HQ Tower</span>
|
||||||
<span className="text-gray-600">·</span>
|
<span className="text-gray-600">·</span>
|
||||||
<span className="text-gray-400">Intelligence</span>
|
<span className="text-gray-400">Intelligence</span>
|
||||||
@@ -41,7 +43,9 @@ export default function EngagementPage() {
|
|||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Button
|
<Button
|
||||||
className="flex items-center gap-2 rounded-xl border border-[#142538] bg-[#091624] px-4 py-2.5 text-xs font-bold text-white hover:bg-[#11233a] transition">
|
className="flex items-center gap-2 rounded-xl border
|
||||||
|
border-[#142538] bg-[#091624] px-4 py-4 text-xs font-bold text-white
|
||||||
|
hover:bg-[#11233a] transition">
|
||||||
<Smile size={15} className="text-slate-400" />
|
<Smile size={15} className="text-slate-400" />
|
||||||
Pulse survey
|
Pulse survey
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
168
components/dashboard/equipment.tsx
Normal file
168
components/dashboard/equipment.tsx
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Target,
|
||||||
|
AlertTriangle,
|
||||||
|
Clock,
|
||||||
|
ShieldCheck,
|
||||||
|
Search,
|
||||||
|
QrCode,
|
||||||
|
Plus,
|
||||||
|
Download,
|
||||||
|
Upload
|
||||||
|
} from 'lucide-react';
|
||||||
|
import Button from "@/utils/button";
|
||||||
|
import StatCard from "@/utils/dashStatCard";
|
||||||
|
import LocationTable from '../../utils/table';
|
||||||
|
|
||||||
|
export default function EquipmentPortal() {
|
||||||
|
const [activeTab, setActiveTab] = useState<string>('Site-Wise Equipment');
|
||||||
|
|
||||||
|
const tabs: string[] = [
|
||||||
|
'Equipment Groups',
|
||||||
|
'Site-Wise Equipment',
|
||||||
|
'PPM Checklist',
|
||||||
|
'AMC Checklist',
|
||||||
|
'PPM Schedule',
|
||||||
|
'AMC Schedule',
|
||||||
|
'Maintenance Calendar'
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-15 text-slate-100 max-w-7xl mx-auto font-sans">
|
||||||
|
{/* --- HEADER SECTION --- */}
|
||||||
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 text-[10px]
|
||||||
|
font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Operations</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Equipment</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
|
||||||
|
Equipment & maintenance
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||||||
|
Asset register, preventive (PPM) and contract (AMC) maintenance, scoped to the active tenant.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* --- NAVIGATION TABS --- */}
|
||||||
|
<div className="flex items-center gap-8 border-b border-slate-800/80 mb-6 text-sm font-semibold overflow-x-auto no-scrollbar">
|
||||||
|
{tabs.map((tab) => (
|
||||||
|
<button
|
||||||
|
key={tab}
|
||||||
|
onClick={() => setActiveTab(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>
|
||||||
|
|
||||||
|
{/* --- STAT CARDS --- */}
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||||
|
{/* Total Equipment */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Total equipment"
|
||||||
|
value="8"
|
||||||
|
icon={Target}
|
||||||
|
badgeText="4 groups"
|
||||||
|
badgeClassName="text-slate-400 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-cyan-950/40 border-cyan-800/30 text-cyan-400"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Critical Assets */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Critical assets"
|
||||||
|
value="3"
|
||||||
|
icon={AlertTriangle}
|
||||||
|
badgeText="priority"
|
||||||
|
badgeClassName="text-rose-400 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-rose-950/40 border-rose-800/30 text-rose-400"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* PPM Attention */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="PPM attention"
|
||||||
|
value="5"
|
||||||
|
icon={Clock}
|
||||||
|
badgeText="due / overdue"
|
||||||
|
badgeClassName="text-amber-400 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-400"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* On Plan */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="On plan"
|
||||||
|
value="3"
|
||||||
|
icon={ShieldCheck}
|
||||||
|
badgeText="healthy"
|
||||||
|
badgeClassName="text-emerald-400 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-emerald-950/40 border-emerald-800/30 text-emerald-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* --- ROW 2: SEARCH, FILTERS & ACTION BUTTONS --- */}
|
||||||
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4 mb-8">
|
||||||
|
{/* Search & Select Filters */}
|
||||||
|
<div className="flex flex-1 flex-wrap items-center gap-3 w-full md:w-auto">
|
||||||
|
{/* Search Bar */}
|
||||||
|
<div className="relative flex-1 min-w-[240px] max-w-md">
|
||||||
|
<Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search code, name, block, provider..."
|
||||||
|
className="w-full bg-[#0b1727] border border-slate-800 rounded-lg pl-9 pr-4 py-2 text-xs text-slate-200 placeholder-slate-500 focus:outline-none focus:border-teal-500/50 transition-colors"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Groups Dropdown */}
|
||||||
|
<select className="bg-[#0b1727] border border-slate-800 rounded-lg px-4 py-2 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none focus:border-teal-500/50 transition-all">
|
||||||
|
<option value="all">All groups</option>
|
||||||
|
<option value="group 1">HVAC</option>
|
||||||
|
|
||||||
|
<option value="group 1">Electrical</option>
|
||||||
|
<option value="group 3">Plumbing</option>
|
||||||
|
<option value="group 4">Fire</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="flex items-center gap-3 w-full md:w-auto justify-end">
|
||||||
|
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
|
||||||
|
<QrCode size={15} />
|
||||||
|
<span>All QR</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
|
||||||
|
<Download size={15} />
|
||||||
|
<span>Download</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
|
||||||
|
<Upload size={15} />
|
||||||
|
<span>Bulk upload</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className="flex items-center gap-2 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-4 py-2 text-xs transition-colors">
|
||||||
|
<Plus size={16} className="stroke-[3]" />
|
||||||
|
<span>Add equipment</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<LocationTable />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
155
components/dashboard/helpDesk.tsx
Normal file
155
components/dashboard/helpDesk.tsx
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Layers,
|
||||||
|
ShieldCheck,
|
||||||
|
CheckSquare,
|
||||||
|
Clock,
|
||||||
|
Search,
|
||||||
|
QrCode,
|
||||||
|
Plus,
|
||||||
|
} from 'lucide-react';
|
||||||
|
import Button from "@/utils/button";
|
||||||
|
import StatCard from "@/utils/dashStatCard";
|
||||||
|
import LocationTable from '../../utils/table';
|
||||||
|
import NavigationTabs from '@/utils/tabs';
|
||||||
|
|
||||||
|
|
||||||
|
export default function HelpDeskPortal() {
|
||||||
|
const [activeTab, setActiveTab] = useState<string>('Location');
|
||||||
|
|
||||||
|
const tabs: string[] = ['Location', 'Technical', 'Missed', 'Departments', 'HSQE'];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-15 text-slate-100 max-w-7xl mx-auto font-sans">
|
||||||
|
{/* --- HEADER SECTION --- */}
|
||||||
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Operations</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Help Desk</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-extrabold text-white tracking-tight mt-2">
|
||||||
|
Help Desk & checklists
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||||||
|
Scheduled location and equipment inspections, missed-checklist recovery, and HSQE incident management.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{/* Reusable Navigation Tabs */}
|
||||||
|
<NavigationTabs
|
||||||
|
tabs={tabs}
|
||||||
|
activeTab={activeTab}
|
||||||
|
onTabChange={setActiveTab}
|
||||||
|
className="mb-6"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Rest of your page content */}
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||||||
|
{/* Total Checklists */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Total checklists"
|
||||||
|
value="10"
|
||||||
|
icon={Layers}
|
||||||
|
badgeText="Location set"
|
||||||
|
badgeClassName="text-slate-500 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-slate-800/60 border-slate-700/50 text-slate-300"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Active */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Active"
|
||||||
|
value="7"
|
||||||
|
icon={ShieldCheck}
|
||||||
|
badgeText="of 10"
|
||||||
|
badgeClassName="text-teal-400 font-mono font-medium"
|
||||||
|
iconContainerClassName="bg-emerald-950/60 border-emerald-800/40 text-emerald-400"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Approval Set */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Approval set"
|
||||||
|
value="5"
|
||||||
|
icon={CheckSquare}
|
||||||
|
badgeText="workflow"
|
||||||
|
badgeClassName="text-slate-500 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-amber-950/40 border-amber-800/30 text-amber-500"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Avg TAT */}
|
||||||
|
<StatCard
|
||||||
|
variant="icon"
|
||||||
|
title="Avg TAT"
|
||||||
|
value="40m"
|
||||||
|
icon={Clock}
|
||||||
|
badgeText="target"
|
||||||
|
badgeClassName="text-slate-500 lowercase font-mono"
|
||||||
|
iconContainerClassName="bg-blue-950/50 border-blue-800/30 text-blue-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{/* --- ROW 2: SEARCH, FILTERS & ACTION BUTTONS --- */}
|
||||||
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4 mb-8">
|
||||||
|
{/* Search & Select Filters */}
|
||||||
|
<div className="flex flex-1 flex-wrap items-center gap-3 w-full md:w-auto">
|
||||||
|
{/* Search Bar */}
|
||||||
|
<div className="relative flex-1 min-w-[240px] max-w-md">
|
||||||
|
<Search size={18} className="absolute left-3 top-1/2 -translate-y-1/2 text-slate-400" />
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search location, block, department..."
|
||||||
|
className="w-full bg-[#0b1727] border border-slate-800 rounded-lg pl-9 pr-4 py-2 text-xs text-slate-200 placeholder-slate-500 focus:outline-none focus:border-teal-500/50 transition-colors"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Block Dropdown */}
|
||||||
|
<select className="bg-[#081321] border border-slate-800/80 rounded-xl px-4 py-2 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none focus:ring-2 focus:ring-[#2dd4bf] focus:ring-offset-2 focus:ring-offset-[#081321] transition-all">
|
||||||
|
<option value="all">All blocks</option>
|
||||||
|
<option value="block 1">Block 1</option>
|
||||||
|
<option value="block 2">Block 2</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
{/* Status Dropdown (Active State matching the screenshot) */}
|
||||||
|
<select className="bg-[#081321] border border-transparent ring-2 ring-[#2dd4bf] ring-offset-2 ring-offset-[#081321] rounded-xl px-4 py-2 text-xs font-semibold text-slate-200 cursor-pointer focus:outline-none transition-all">
|
||||||
|
<option value="all">All status</option>
|
||||||
|
<option value="active">Active</option>
|
||||||
|
<option value="inactive">InActive</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="flex items-center gap-3 w-full md:w-auto justify-end">
|
||||||
|
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-3.5 py-2 text-xs font-semibold transition-colors">
|
||||||
|
<QrCode size={15} />
|
||||||
|
<span>All QR</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className="flex items-center gap-2 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-4 py-2 text-xs transition-colors">
|
||||||
|
<Plus size={16} className="stroke-[3]" />
|
||||||
|
<span>New Location Checklist</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<LocationTable/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
0
components/dashboard/inventory.tsx
Normal file
0
components/dashboard/inventory.tsx
Normal file
@@ -16,11 +16,11 @@ import { Card } from "@/utils/cardWrapper";
|
|||||||
import { CardHeader } from "@/utils/cardHeader";
|
import { CardHeader } from "@/utils/cardHeader";
|
||||||
export default function PropertyManagerPortal() {
|
export default function PropertyManagerPortal() {
|
||||||
return (
|
return (
|
||||||
<div className=" text-slate-100 mt-15 font-sans antialiased
|
<div className=" text-slate-100 mt-15
|
||||||
">
|
">
|
||||||
|
|
||||||
{/* --- HEADER SECTION --- */}
|
{/* --- HEADER SECTION --- */}
|
||||||
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-8">
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
<span className="text-teal-400">Apple One — HQ Tower</span>
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
@@ -29,7 +29,7 @@ export default function PropertyManagerPortal() {
|
|||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>Property Manager</span>
|
<span>Property Manager</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl font-extrabold text-white tracking-tight mt-2">
|
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
|
||||||
Property manager portal
|
Property manager portal
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||||||
@@ -38,7 +38,7 @@ export default function PropertyManagerPortal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex items-center gap-3 self-start md:mt-6">
|
<div className="flex items-center gap-3 self-start md:mt-15">
|
||||||
<Button className="flex items-center gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538] rounded-xl text-xs font-semibold text-slate-200 transition-colors duration-200 shadow-sm">
|
<Button className="flex items-center gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538] rounded-xl text-xs font-semibold text-slate-200 transition-colors duration-200 shadow-sm">
|
||||||
<FileText size={14} className="text-slate-400" />
|
<FileText size={14} className="text-slate-400" />
|
||||||
Documents
|
Documents
|
||||||
|
|||||||
96
components/dashboard/request.tsx
Normal file
96
components/dashboard/request.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { Plus } from 'lucide-react';
|
||||||
|
import Button from "@/utils/button";
|
||||||
|
import LocationTable from '@/utils/table';
|
||||||
|
import NavigationTabs from '@/utils/tabs';
|
||||||
|
import { Reqeusttabs } from '@/constants/constant';
|
||||||
|
|
||||||
|
export default function ServiceRequestsPortal() {
|
||||||
|
const [activeTab, setActiveTab] = useState<string>('TFM Requests');
|
||||||
|
const [activeFilter, setActiveFilter] = useState<string>('All');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const statusFilters = [
|
||||||
|
{ label: 'All', count: 18 },
|
||||||
|
{ label: 'Registered', count: 4 },
|
||||||
|
{ label: 'Assigned', count: 3 },
|
||||||
|
{ label: 'Working', count: 4 },
|
||||||
|
{ label: 'Completed', count: 3 },
|
||||||
|
{ label: 'Overdue', count: 4 },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-15 text-slate-100 max-w-7xl mx-auto font-sans">
|
||||||
|
{/* --- HEADER SECTION --- */}
|
||||||
|
<div className="flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-6">
|
||||||
|
<div>
|
||||||
|
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Operations</span>
|
||||||
|
<span>•</span>
|
||||||
|
<span>Requests</span>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
|
||||||
|
Service requests
|
||||||
|
</h1>
|
||||||
|
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||||||
|
TFM helpdesk tickets, engineering breakdowns, and auto-generated recurring complaints — one request lifecycle.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* --- NAVIGATION TABS & TOP ACTION BUTTONS --- */}
|
||||||
|
<div className="flex flex-col md:flex-row md:items-center justify-between border-b border-slate-800/80 mb-6 gap-4">
|
||||||
|
{/* Navigation Tabs */}
|
||||||
|
<NavigationTabs
|
||||||
|
tabs={Reqeusttabs}
|
||||||
|
activeTab={activeTab}
|
||||||
|
onTabChange={setActiveTab}
|
||||||
|
className="mb-6"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Action Buttons */}
|
||||||
|
<div className="flex items-center gap-3 pb-3 md:pb-0">
|
||||||
|
<Button className="flex items-center gap-2 bg-[#0b1727] hover:bg-slate-800/60 text-slate-200 border border-slate-800 rounded-lg px-4 py-2 text-xs font-semibold transition-colors">
|
||||||
|
<span>Export</span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button className="flex items-center gap-2 bg-teal-400 hover:bg-teal-300 text-slate-950 font-bold rounded-lg px-4 py-2 text-xs transition-colors">
|
||||||
|
<Plus size={16} className="stroke-[3]" />
|
||||||
|
<span>New request</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* --- STATUS PILL FILTERS --- */}
|
||||||
|
<div className="flex items-center gap-2.5 overflow-x-auto no-scrollbar py-1">
|
||||||
|
{statusFilters.map((filter) => {
|
||||||
|
const isActive = activeFilter === filter.label;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={filter.label}
|
||||||
|
onClick={() => setActiveFilter(filter.label)}
|
||||||
|
className={`flex items-center gap-2 px-3.5 py-1.5 rounded-full text-xs font-medium transition-all ${
|
||||||
|
isActive
|
||||||
|
? 'bg-teal-950/40 text-teal-400 border border-teal-500/50'
|
||||||
|
: 'bg-[#0b1727] text-slate-400 border border-slate-800/80 hover:text-slate-200'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<span>{filter.label}</span>
|
||||||
|
<span
|
||||||
|
className={`font-mono text-[11px] ${
|
||||||
|
isActive ? 'text-teal-400 font-bold' : 'text-slate-400'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{filter.count}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<LocationTable/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -16,10 +16,11 @@ import { CardHeader } from "@/utils/cardHeader";
|
|||||||
|
|
||||||
export default function ServiceEngineerPortal() {
|
export default function ServiceEngineerPortal() {
|
||||||
return (
|
return (
|
||||||
<div className="text-slate-100 font-sans antialiased">
|
<div className="my-15 text-slate-100 ">
|
||||||
|
|
||||||
{/* --- HEADER SECTION --- */}
|
{/* --- HEADER SECTION --- */}
|
||||||
<div className="pt-20 flex flex-col md:flex-row md:items-start md:justify-between gap-4 mb-8">
|
<div className=" flex flex-col
|
||||||
|
md:flex-row md:items-start md:justify-between gap-4 mb-4">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
<div className="flex items-center gap-2 text-[10px] font-bold tracking-widest text-slate-500 uppercase font-mono">
|
||||||
<span className="text-teal-400">Apple One — HQ Tower</span>
|
<span className="text-teal-400">Apple One — HQ Tower</span>
|
||||||
@@ -28,7 +29,7 @@ export default function ServiceEngineerPortal() {
|
|||||||
<span>•</span>
|
<span>•</span>
|
||||||
<span>Service Engineer</span>
|
<span>Service Engineer</span>
|
||||||
</div>
|
</div>
|
||||||
<h1 className="text-2xl font-extrabold text-white tracking-tight mt-2">
|
<h1 className="text-2xl font-bold text-white tracking-tight mt-2">
|
||||||
Service engineer portal
|
Service engineer portal
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||||||
@@ -37,7 +38,7 @@ export default function ServiceEngineerPortal() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* Action Buttons */}
|
||||||
<div className="flex items-center gap-3 self-start md:mt-6">
|
<div className="flex items-center gap-3 self-start md:mt-15">
|
||||||
<Button className="flex items-center gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538] rounded-xl text-xs font-semibold text-slate-200 transition-colors duration-200 shadow-sm">
|
<Button className="flex items-center gap-2 px-4 py-2 bg-[#091624] hover:bg-[#11233a] border border-[#142538] rounded-xl text-xs font-semibold text-slate-200 transition-colors duration-200 shadow-sm">
|
||||||
<FileText size={14} className="text-slate-400" />
|
<FileText size={14} className="text-slate-400" />
|
||||||
Scan Qr
|
Scan Qr
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ import {
|
|||||||
Cog,
|
Cog,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
|
//Side bar Drop Down menu
|
||||||
|
|
||||||
export const SIDEBAR_ITEMS = [
|
export const SIDEBAR_ITEMS = [
|
||||||
{
|
{
|
||||||
title: "Overview",
|
title: "Overview",
|
||||||
@@ -126,6 +128,8 @@ export const SIDEBAR_ITEMS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
//Overview dashboard data
|
||||||
|
|
||||||
export const chartData = [
|
export const chartData = [
|
||||||
{ month: 'J', height: 'h-24' },
|
{ month: 'J', height: 'h-24' },
|
||||||
{ month: 'F', height: 'h-16' },
|
{ month: 'F', height: 'h-16' },
|
||||||
@@ -179,3 +183,23 @@ export const TICKETS_DATA = [
|
|||||||
{ title: "More water dispensers", type: "Complaint", status: "CLOSED" },
|
{ title: "More water dispensers", type: "Complaint", status: "CLOSED" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
//Tabs used in Operations dashboards
|
||||||
|
|
||||||
|
export const equipmentTabs: string[] = [
|
||||||
|
'Equipment Groups',
|
||||||
|
'Site-Wise Equipment',
|
||||||
|
'PPM Checklist',
|
||||||
|
'AMC Checklist',
|
||||||
|
'PPM Schedule',
|
||||||
|
'AMC Schedule',
|
||||||
|
'Maintenance Calendar'
|
||||||
|
];
|
||||||
|
export const helpdeskTtabs: string[] = ['Location', 'Technical',
|
||||||
|
'Missed', 'Departments', 'HSQE'];
|
||||||
|
|
||||||
|
|
||||||
|
export const Reqeusttabs: string[] = [
|
||||||
|
'TFM Requests',
|
||||||
|
'Engineering',
|
||||||
|
'Schedule Complaints'
|
||||||
|
];
|
||||||
@@ -2,8 +2,14 @@ import { LucideProps } from "lucide-react";
|
|||||||
import { ComponentType } from "react";
|
import { ComponentType } from "react";
|
||||||
import { colorMap } from "./constant";
|
import { colorMap } from "./constant";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Types and interfaces used in login Page
|
||||||
export type Step = "login" | "verify" | "forgot";
|
export type Step = "login" | "verify" | "forgot";
|
||||||
|
|
||||||
|
export interface AuthLayoutProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onBack?: () => void;
|
onBack?: () => void;
|
||||||
@@ -11,6 +17,17 @@ export type Step = "login" | "verify" | "forgot";
|
|||||||
onForgotPassword?:()=>void;
|
onForgotPassword?:()=>void;
|
||||||
item?: any;
|
item?: any;
|
||||||
}
|
}
|
||||||
|
export interface FormProps {
|
||||||
|
label: string;
|
||||||
|
value?: string;
|
||||||
|
type?: string;
|
||||||
|
placeholder?:string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Overview dashboard interface used
|
||||||
|
|
||||||
|
|
||||||
interface Activity {
|
interface Activity {
|
||||||
time: string;
|
time: string;
|
||||||
text: string;
|
text: string;
|
||||||
@@ -54,6 +71,7 @@ export interface StatusProgressProps {
|
|||||||
barColor?: string;
|
barColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For footer banner
|
||||||
export interface InfoBannerProps {
|
export interface InfoBannerProps {
|
||||||
boldText?: string;
|
boldText?: string;
|
||||||
normalText?: string;
|
normalText?: string;
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
interface Props {
|
|
||||||
label: string;
|
|
||||||
value?: string;
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
import { FormProps } from "@/constants/types";
|
||||||
export default function FormInput({
|
export default function FormInput({
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
type = "text",
|
type = "text",
|
||||||
}: Props) {
|
placeholder="string"
|
||||||
|
}: FormProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<label className="mb-2 block text-sm text-slate-400">
|
<label className="mb-2 block text-sm text-slate-400">
|
||||||
@@ -18,7 +15,10 @@ export default function FormInput({
|
|||||||
<input
|
<input
|
||||||
type={type}
|
type={type}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
|
placeholder={placeholder}
|
||||||
|
|
||||||
className="h-10 w-full rounded-xl border border-slate-700 text-white px-5"
|
className="h-10 w-full rounded-xl border border-slate-700 text-white px-5"
|
||||||
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export default function Sidebar() {
|
|||||||
console.log("Session data ",session?.user?.name)
|
console.log("Session data ",session?.user?.name)
|
||||||
console.log("Session data ",session?.user?.email)
|
console.log("Session data ",session?.user?.email)
|
||||||
return (
|
return (
|
||||||
<aside className="fixed bg-[#071321] text-white h-screen overflow-y-auto">
|
<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 items-center gap-2 px-2 pt-2 pb-3">
|
||||||
<div className="flex h-9 w-9 items-center justify-center rounded-lg
|
<div className="flex h-9 w-9 items-center justify-center rounded-lg
|
||||||
bg-[#3de0c4] font-bold text-[#05111d] text-sm">
|
bg-[#3de0c4] font-bold text-[#05111d] text-sm">
|
||||||
226
utils/table.tsx
Normal file
226
utils/table.tsx
Normal 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:00–17: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:00–23: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:00–17: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:00–18: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:00–18:00', tat: '60m', sched: 8, approval: true, status: 'ACTIVE' },
|
||||||
|
{ id: '6', block: 'Block 2', floor: 'G', zone: 'Z1', department: 'Electrical', location: 'Reception', window: '06:00–18: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:00–17:30', tat: '60m', sched: 8, approval: true, status: 'ACTIVE' },
|
||||||
|
{ id: '8', block: 'Block 2', floor: '3', zone: 'Z2', department: 'Electrical', location: 'Cafeteria', window: '08:00–23: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:00–17:30', tat: '20m', sched: 8, approval: true, status: 'INACTIVE' },
|
||||||
|
{ id: '10', block: 'Block 1', floor: 'G', zone: 'Z1', department: 'HVAC', location: 'Lobby', window: '08:00–18: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
36
utils/tabs.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user