334 lines
15 KiB
TypeScript
334 lines
15 KiB
TypeScript
import { useState } from 'react';
|
||
import {
|
||
BarChart2,
|
||
Check,
|
||
AlertTriangle,
|
||
Clock,
|
||
CheckCircle2,
|
||
Cpu
|
||
} from "lucide-react";
|
||
import Button from "@/components/common/button";
|
||
import StatCard from "@/components/common/dashStatCard";
|
||
|
||
export default function AlarmRulesPortal() {
|
||
// State for automation rules toggles
|
||
const [autoWorkOrder, setAutoWorkOrder] = useState(true);
|
||
const [escalateCriticals, setEscalateCriticals] = useState(true);
|
||
const [offHoursSetpoint, setOffHoursSetpoint] = useState(true);
|
||
const [filterPpmTask, setFilterPpmTask] = 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-[12px] font-bold tracking-widest text-emerald-400 uppercase font-mono">
|
||
<span>APPLE ONE — HQ TOWER</span>
|
||
<span className="text-slate-600">•</span>
|
||
<span className="text-slate-400">IOT</span>
|
||
<span className="text-slate-600">•</span>
|
||
<span className="text-slate-400">ALARMS & RULES</span>
|
||
</div>
|
||
<h1 className="text-2xl font-bold text-white tracking-tight mt-1">
|
||
Alarms & automation rules
|
||
</h1>
|
||
<p className="text-sm font-medium text-slate-400 mt-2 max-w-xl leading-relaxed">
|
||
Acknowledge live alarms, raise work orders in one click, and manage the automation rules that act on telemetry.
|
||
</p>
|
||
</div>
|
||
|
||
{/* --- HEADER BUTTONS --- */}
|
||
<div className="flex items-center gap-3 self-start md:mt-4">
|
||
<Button className="bg-[#0e1c2e] hover:bg-slate-800 text-slate-200 border border-[#162942] text-xs px-4 py-2 rounded-lg flex items-center gap-2 font-semibold transition-colors">
|
||
<BarChart2 className="w-4 h-4 text-slate-400" />
|
||
<span>History</span>
|
||
</Button>
|
||
|
||
<Button className="bg-[#10b981] hover:bg-emerald-400 text-slate-950 font-bold text-xs px-4 py-2 rounded-lg flex items-center gap-2 transition-colors shadow-lg">
|
||
<Check className="w-4 h-4 text-slate-950 stroke-[3]" />
|
||
<span>Acknowledge all (3)</span>
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* --- STAT CARDS --- */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
|
||
{/* Card 1: Critical open */}
|
||
<StatCard
|
||
variant="icon"
|
||
title="Critical open"
|
||
value="2"
|
||
icon={AlertTriangle}
|
||
badgeText="act now"
|
||
badgeClassName="text-rose-400 lowercase font-mono font-medium"
|
||
iconContainerClassName="bg-rose-950/50 border-rose-800/30 text-rose-400"
|
||
/>
|
||
|
||
{/* Card 2: Warnings open */}
|
||
<StatCard
|
||
variant="icon"
|
||
title="Warnings open"
|
||
value="1"
|
||
icon={Clock}
|
||
badgeText="review"
|
||
badgeClassName="text-amber-400 lowercase font-mono font-medium"
|
||
iconContainerClassName="bg-amber-950/50 border-amber-800/30 text-amber-400"
|
||
/>
|
||
|
||
{/* Card 3: Acknowledged */}
|
||
<StatCard
|
||
variant="icon"
|
||
title="Acknowledged"
|
||
value="2"
|
||
icon={CheckCircle2}
|
||
badgeText="today"
|
||
badgeClassName="text-emerald-400 lowercase font-mono font-medium"
|
||
iconContainerClassName="bg-emerald-950/60 border-emerald-800/40 text-emerald-400"
|
||
/>
|
||
|
||
{/* Card 4: Rules active */}
|
||
<StatCard
|
||
variant="icon"
|
||
title="Rules active"
|
||
value="3 / 4"
|
||
icon={Cpu}
|
||
badgeText="automation"
|
||
badgeClassName="text-slate-400 lowercase font-mono font-medium"
|
||
iconContainerClassName="bg-blue-950/60 border-blue-800/40 text-blue-400"
|
||
/>
|
||
</div>
|
||
|
||
{/* --- MAIN CONTENT GRID --- */}
|
||
<div className="grid grid-cols-1 lg:grid-cols-12 gap-6">
|
||
{/* --- LEFT SECTION: ALARMS LIST --- */}
|
||
<div className="lg:col-span-7 bg-[#091322]/80 border border-slate-800/80 rounded-xl p-5 shadow-sm">
|
||
<div className="mb-4">
|
||
<h2 className="text-base font-bold text-white">Alarms</h2>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">live · newest first</p>
|
||
</div>
|
||
|
||
<div className="divide-y divide-slate-800/60">
|
||
{/* Alarm Item 1 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-3">
|
||
<div className="flex items-start gap-2.5 min-w-0">
|
||
<span className="w-2 h-2 rounded-full bg-rose-500 shrink-0 mt-1.5" />
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-semibold text-slate-100 truncate">
|
||
<span className="text-teal-400 font-mono">CH-02</span>
|
||
{" · "}
|
||
<span>Condenser approach high — 4.2 K above design</span>
|
||
</p>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">08:41 · open</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 shrink-0">
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Ack
|
||
</Button>
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Work order
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Alarm Item 2 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-3">
|
||
<div className="flex items-start gap-2.5 min-w-0">
|
||
<span className="w-2 h-2 rounded-full bg-amber-500 shrink-0 mt-1.5" />
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-semibold text-slate-100 truncate">
|
||
<span className="text-teal-400 font-mono">AHU-07</span>
|
||
{" · "}
|
||
<span>Filter ΔP beyond limit — replacement due</span>
|
||
</p>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">08:12 · open</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 shrink-0">
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Ack
|
||
</Button>
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Work order
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Alarm Item 3 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-3">
|
||
<div className="flex items-start gap-2.5 min-w-0">
|
||
<span className="w-2 h-2 rounded-full bg-rose-500 shrink-0 mt-1.5" />
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-semibold text-slate-100 truncate">
|
||
<span className="text-teal-400 font-mono">CTL-14</span>
|
||
{" · "}
|
||
<span>Controller offline — no heartbeat for 30 min</span>
|
||
</p>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">07:56 · open</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 shrink-0">
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Ack
|
||
</Button>
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Work order
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Alarm Item 4 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-3">
|
||
<div className="flex items-start gap-2.5 min-w-0">
|
||
<span className="w-2 h-2 rounded-full bg-amber-500 shrink-0 mt-1.5" />
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-semibold text-slate-100 truncate">
|
||
<span className="text-teal-400 font-mono">SNS-208</span>
|
||
{" · "}
|
||
<span>CO₂ trending up in open office L3 — 1,080 ppm</span>
|
||
</p>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">07:30 · acknowledged</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 shrink-0">
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Work order
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Alarm Item 5 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-3">
|
||
<div className="flex items-start gap-2.5 min-w-0">
|
||
<span className="w-2 h-2 rounded-full bg-blue-500 shrink-0 mt-1.5" />
|
||
<div className="min-w-0">
|
||
<p className="text-sm font-semibold text-slate-100 truncate">
|
||
<span className="text-teal-400 font-mono">GW-01</span>
|
||
{" · "}
|
||
<span>Gateway reconnected after 90 s network drop</span>
|
||
</p>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">06:58 · acknowledged</p>
|
||
</div>
|
||
</div>
|
||
<div className="flex items-center gap-2 shrink-0">
|
||
<Button className="rounded-md border border-slate-700/60 bg-slate-900/40 px-3 py-1 text-xs font-medium text-slate-300 hover:bg-slate-800 hover:text-white transition">
|
||
Work order
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* --- RIGHT SECTION: AUTOMATION RULES --- */}
|
||
<div className="lg:col-span-5 bg-[#091322]/80 border border-slate-800/80 rounded-xl p-5 shadow-sm h-fit">
|
||
<div className="mb-4">
|
||
<h2 className="text-base font-bold text-white">Automation rules</h2>
|
||
<p className="text-xs font-mono text-slate-400 mt-0.5">telemetry → action</p>
|
||
</div>
|
||
|
||
<div className="divide-y divide-slate-800/60">
|
||
{/* Rule 1 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-4">
|
||
<div>
|
||
<h3 className="text-sm font-semibold text-slate-100">
|
||
Auto work order on critical alarm
|
||
</h3>
|
||
<p className="text-xs text-slate-400 mt-0.5">
|
||
Raises a Help Desk request with the asset and fault pre-filled
|
||
</p>
|
||
</div>
|
||
<Button
|
||
|
||
onClick={() => setAutoWorkOrder(!autoWorkOrder)}
|
||
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 ${
|
||
autoWorkOrder ? 'bg-[#2dd4bf]' : 'bg-slate-700'
|
||
}`}
|
||
>
|
||
<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 ${
|
||
autoWorkOrder ? 'translate-x-5' : 'translate-x-0'
|
||
}`}
|
||
/>
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Rule 2 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-4">
|
||
<div>
|
||
<h3 className="text-sm font-semibold text-slate-100">
|
||
Escalate unacknowledged criticals
|
||
</h3>
|
||
<p className="text-xs text-slate-400 mt-0.5">
|
||
Notifies the escalation matrix after 15 minutes
|
||
</p>
|
||
</div>
|
||
<Button
|
||
|
||
onClick={() => setEscalateCriticals(!escalateCriticals)}
|
||
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 ${
|
||
escalateCriticals ? 'bg-[#2dd4bf]' : 'bg-slate-700'
|
||
}`}
|
||
>
|
||
<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 ${
|
||
escalateCriticals ? 'translate-x-5' : 'translate-x-0'
|
||
}`}
|
||
/>
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Rule 3 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-4">
|
||
<div>
|
||
<h3 className="text-sm font-semibold text-slate-100">
|
||
Off-hours setpoint relaxation
|
||
</h3>
|
||
<p className="text-xs text-slate-400 mt-0.5">
|
||
Widens comfort bands 22:00–06:00 to save energy
|
||
</p>
|
||
</div>
|
||
<Button
|
||
|
||
onClick={() => setOffHoursSetpoint(!offHoursSetpoint)}
|
||
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 ${
|
||
offHoursSetpoint ? 'bg-[#2dd4bf]' : 'bg-slate-700'
|
||
}`}
|
||
>
|
||
<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 ${
|
||
offHoursSetpoint ? 'translate-x-5' : 'translate-x-0'
|
||
}`}
|
||
/>
|
||
</Button>
|
||
</div>
|
||
|
||
{/* Rule 4 */}
|
||
<div className="py-3.5 flex items-center justify-between gap-4">
|
||
<div>
|
||
<h3 className="text-sm font-semibold text-slate-100">
|
||
Filter ΔP → PPM task
|
||
</h3>
|
||
<p className="text-xs text-slate-400 mt-0.5">
|
||
Adds a filter change to the next PPM visit automatically
|
||
</p>
|
||
</div>
|
||
<Button
|
||
|
||
onClick={() => setFilterPpmTask(!filterPpmTask)}
|
||
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 ${
|
||
filterPpmTask ? 'bg-[#2dd4bf]' : 'bg-slate-800'
|
||
}`}
|
||
>
|
||
<span
|
||
className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-slate-300 shadow ring-0 transition duration-200 ease-in-out ${
|
||
filterPpmTask ? 'translate-x-5 bg-slate-950' : 'translate-x-0'
|
||
}`}
|
||
/>
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
} |