diff --git a/public/icons.svg b/public/icons.svg deleted file mode 100644 index e952219..0000000 --- a/public/icons.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 9474b3f..0000000 --- a/src/App.css +++ /dev/null @@ -1,8 +0,0 @@ -:root{--ink:#182522;--muted:#71807b;--line:#e2e7e2;--paper:#fff;--canvas:#f7f8f5;--coral:#ee7258;--teal:#275c55} -.app-shell{min-height:100vh;display:flex;background:var(--canvas)}.sidebar{width:246px;flex:none;min-height:100vh;padding:28px 15px 18px;display:flex;flex-direction:column;background:#163c37;color:#dce8e0}.brand,.workspace-switcher,.profile,.nav-link,.header-actions,.page-intro,.panel-heading,.table-toolbar,.incident-name,.table-location,.site-row,.form-footer{display:flex;align-items:center}.brand{gap:10px;padding:0 12px 31px;color:#fff}.brand strong{display:block;font:700 22px 'Space Grotesk',sans-serif;letter-spacing:-.6px}.brand span,.profile span{display:block;color:#9bb4ab;font-size:10px;margin-top:2px}.brand-mark{width:34px;height:34px;display:grid;place-items:center;border-radius:9px;background:var(--coral);color:#fff}.close-menu{display:none!important;margin-left:auto}.workspace-switcher{gap:10px;padding:11px;border:1px solid #316158;border-radius:7px;margin-bottom:27px}.workspace-switcher strong{display:block;color:#fff;font-size:13px}.workspace-switcher>span{margin-left:auto}.workspace-avatar,.profile-avatar,.header-avatar{display:grid;place-items:center;border-radius:50%;font-weight:700}.workspace-avatar{width:29px;height:29px;color:#143d37;background:#f1c865;font:700 11px 'DM Mono'}.eyebrow{display:block;color:#8da29b;font:500 10px 'DM Mono',monospace;letter-spacing:.09em;text-transform:uppercase}.primary-nav,.nav-section{border-bottom:1px solid #315b54;padding-bottom:23px;margin-bottom:24px}.nav-section>.eyebrow{padding:0 12px 8px}.nav-link{gap:12px;height:42px;padding:0 12px;color:#aac0b8;text-decoration:none;font-size:13px;border-radius:6px}.nav-link:hover,.nav-link.active{color:#fff;background:#28574f}.nav-link em{font:11px 'DM Mono';background:var(--coral);color:#fff;border-radius:10px;padding:2px 7px;margin-left:auto;font-style:normal}.sidebar-bottom{margin-top:auto}.profile{border-top:1px solid #315b54;gap:10px;padding:20px 8px 0;margin-top:16px}.profile strong{display:block;color:#fff;font-size:12px}.profile-avatar{width:31px;height:31px;color:#fff;background:#5c8274;font:11px 'DM Mono'}.more-dots{color:#91aaa0;letter-spacing:2px;margin-left:auto} -.main-area{min-width:0;flex:1}.topbar{height:72px;border-bottom:1px solid var(--line);padding:0 42px;display:flex;align-items:center;justify-content:space-between;background:#ffffff99}.breadcrumb{display:flex;gap:10px;align-items:center;color:#94a09b;font-size:12px}.breadcrumb strong{color:var(--ink)}.header-actions{gap:15px}.icon-button{display:grid;place-items:center;border:0;background:transparent;color:#72817d;cursor:pointer;padding:6px}.notification{position:relative}.notification i{position:absolute;width:6px;height:6px;background:var(--coral);border-radius:50%;top:4px;right:3px;border:1px solid var(--canvas)}.header-avatar{width:32px;height:32px;background:#d2e0d8;color:#275c55;font:11px 'DM Mono'}.mobile-menu{display:none}.content{max-width:1360px;margin:0 auto;padding:40px 42px 60px}.page-intro{justify-content:space-between;margin-bottom:31px}.page-intro h1{font:600 34px 'Space Grotesk',sans-serif;letter-spacing:-1px;margin:7px 0 5px;color:var(--ink)}.page-intro p{margin:0;color:var(--muted);font-size:14px}.primary-button,.secondary-button,.light-button{display:inline-flex;gap:8px;align-items:center;justify-content:center;border:0;border-radius:5px;padding:11px 14px;cursor:pointer;text-decoration:none;font-size:12px;font-weight:700}.primary-button{background:var(--coral);color:#fff;box-shadow:0 4px 12px #ee725833}.secondary-button{background:#fff;border:1px solid var(--line);color:var(--ink)}.panel{background:var(--paper);border:1px solid var(--line);border-radius:8px;box-shadow:0 5px 20px #1b252305}.metric-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:13px;margin-bottom:20px}.metric-card{padding:18px;border:1px solid var(--line);border-radius:8px;background:#fff}.metric-card>span{display:block;font-size:12px;color:var(--muted);margin:15px 0 6px}.metric-card strong{display:block;color:var(--ink);font:600 30px 'Space Grotesk';letter-spacing:-1px}.metric-card small{display:block;color:#7d8b86;margin-top:9px;font-size:11px}.metric-card small b{color:#38a17f}.metric-icon{width:35px;height:35px;display:grid;place-items:center;border-radius:7px}.metric-icon.coral{background:#fbe3dd;color:var(--coral)}.metric-icon.yellow{background:#fff2cd;color:#b47a11}.metric-icon.mint{background:#dcefe4;color:#3d9576}.metric-icon.blue{background:#dcebf0;color:#39819c} -.dashboard-grid{display:grid;grid-template-columns:minmax(0,1.7fr) minmax(260px,.8fr);gap:20px;margin-bottom:20px}.bottom-grid{display:grid;grid-template-columns:1.3fr .9fr;gap:20px}.panel-heading{justify-content:space-between;padding:24px 24px 18px}.panel-heading h2{margin:6px 0 0;font:600 19px 'Space Grotesk';color:var(--ink)}.text-link{display:inline-flex;align-items:center;gap:5px;color:var(--coral);font-size:12px;font-weight:700;text-decoration:none}.table-scroll{overflow:auto}.incidents-panel table{min-width:720px}.full-panel{overflow:hidden}.full-panel table{min-width:900px}.table-toolbar{gap:10px;padding:18px 24px;border-bottom:1px solid var(--line)}.search-field,.filter-group{display:flex;align-items:center;gap:8px;height:38px;border:1px solid var(--line);border-radius:5px;padding:0 11px;color:#85938e}.search-field{flex:1;max-width:350px}.search-field input{border:0;outline:0;width:100%;color:var(--ink);background:transparent;font-size:12px}.filter-group{margin-left:auto}.filter-group select,select{border:0;outline:0;color:var(--ink);background:transparent;font-size:12px} -table{width:100%;border-collapse:collapse;text-align:left}th{color:#9aa6a2;font:500 10px 'DM Mono';text-transform:uppercase;letter-spacing:.06em;padding:12px 10px;border-bottom:1px solid var(--line)}th:first-child,td:first-child{padding-left:24px}th:last-child,td:last-child{padding-right:18px}td{padding:14px 10px;border-bottom:1px solid #edf0ec;color:#53625d;font-size:11px;white-space:nowrap}tr:last-child td{border-bottom:0}.incident-name{gap:10px;min-width:250px}.incident-name strong{display:block;color:var(--ink);font-size:12px;font-weight:600}.incident-name small,.table-location small{display:block;color:#9aa6a2;font-size:10px;margin-top:4px}.incident-dot{width:28px;height:28px;display:grid;place-items:center;border-radius:7px}.incident-dot.amber{background:#fff0c8;color:#b68118}.incident-dot.blue{background:#e0eff3;color:#38859d}.incident-dot.red{background:#fbe2dd;color:#dc634e}.incident-dot.green{background:#def0e5;color:#3e9777}.severity{display:inline-flex;align-items:center;gap:5px}.severity i{width:6px;height:6px;border-radius:50%;background:#db6652}.severity.medium i{background:#d8a631}.severity.low i{background:#52a57e}.status{padding:5px 8px;background:#fff0d2;color:#9a7319;border-radius:4px;font-size:10px}.status.resolved{background:#dff1e6;color:#398461}.status.review{background:#fbe0da;color:#c75e4b}.table-location{gap:4px;flex-wrap:wrap;max-width:120px}.table-location small{width:100%;margin-left:18px}.date-cell{color:#87938f}.response-panel{padding-bottom:18px}.progress-ring{width:126px;height:126px;margin:12px auto 20px;border:10px solid #e8f0e9;border-top-color:var(--coral);border-right-color:var(--coral);border-radius:50%;display:grid;place-items:center;transform:rotate(28deg)}.progress-ring>div{transform:rotate(-28deg);text-align:center}.progress-ring strong{display:block;font:600 25px 'Space Grotesk'}.progress-ring span{color:var(--muted);font-size:10px}.action-row{padding:12px 24px 0}.action-row>div:first-child{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}.action-row strong{color:var(--ink);font:11px 'DM Mono'}.progress-track{height:6px;background:#edf1ed;border-radius:20px;overflow:hidden;margin-top:8px}.progress-track i{display:block;height:100%;background:var(--coral);border-radius:20px}.location-bar{padding:7px 24px}.location-bar>div:first-child{display:flex;justify-content:space-between;font-size:11px;color:#63726d}.location-bar strong{font:11px 'DM Mono';color:var(--ink)}.location-bar .progress-track i{background:#70a59a}.chart-panel{min-height:260px}.chart{position:relative;height:170px;margin:4px 25px 22px}.chart svg{width:100%;height:100%;position:absolute;inset:0}.grid-lines{position:absolute;inset:0;background:repeating-linear-gradient(to bottom,transparent 0,transparent 41px,#edf0ec 42px)}.chart-x{display:flex;justify-content:space-between;position:absolute;left:0;right:0;bottom:-20px;color:#a0aba7;font:10px 'DM Mono'} -.location-layout{display:grid;grid-template-columns:1fr 1fr;gap:20px}.site-row{gap:13px;padding:18px 24px;border-top:1px solid var(--line)}.site-icon{width:38px;height:38px;display:grid;place-items:center;border-radius:7px}.site-icon.coral{background:#fbe3dd;color:var(--coral)}.site-icon.yellow{background:#fff1ca;color:#b9821e}.site-icon.blue{background:#dfedf1;color:#3c849a}.site-row>div:nth-child(2){flex:1}.site-row strong,.site-row span{display:block}.site-row strong{font-size:13px}.site-row span{font-size:11px;color:var(--muted);margin-top:4px}.site-row>b{font:15px 'DM Mono'}.site-row b small{font:10px 'DM Sans';color:var(--muted)}.map-placeholder{min-height:360px;position:relative;overflow:hidden;background:#e3eee7}.map-grid{position:absolute;inset:0;opacity:.45;background-image:linear-gradient(30deg,transparent 47%,#b1cfc0 48%,transparent 49%),linear-gradient(115deg,transparent 47%,#b1cfc0 48%,transparent 49%);background-size:80px 80px;transform:rotate(12deg) scale(1.4)}.map-label{position:absolute;left:28px;bottom:27px;padding:11px 14px;background:#fff;border-radius:6px;box-shadow:0 5px 15px #275c5520}.map-label svg{color:var(--coral);vertical-align:middle;margin-right:5px}.map-label strong{font-size:12px}.map-label span{display:block;color:var(--muted);font-size:10px;margin-top:4px}.map-pin{position:absolute;width:14px;height:14px;background:var(--coral);border:3px solid #fff;border-radius:50%;box-shadow:0 2px 5px #275c5550}.pin-one{left:40%;top:30%}.pin-two{left:64%;top:48%}.pin-three{left:24%;top:65%} -.analytics-grid{display:grid;grid-template-columns:1.3fr 1fr 1fr;gap:20px}.insight-card{min-height:240px;padding:26px;display:flex;flex-direction:column;align-items:flex-start}.insight-card.dark{background:#1d4841;color:#dfece5;border-color:#1d4841}.insight-card.dark .eyebrow{color:#9dc0b3}.insight-card h2{font:600 24px 'Space Grotesk';line-height:1.15;margin:20px 0 10px}.insight-card p{color:var(--muted);font-size:12px;line-height:1.6}.dark p{color:#a7c0b8}.light-button{margin-top:auto;background:#edf6ef;color:var(--teal)}.big-stat{margin:32px 0 25px;font:600 25px 'Space Grotesk'}.stat-line{display:flex;justify-content:space-between;width:100%;color:var(--muted);font-size:11px}.insight-card>.progress-track{width:100%;margin-top:10px}.mini-bars{display:flex;align-items:flex-end;gap:7px;height:70px;width:100%;margin-top:auto}.mini-bars i{flex:1;min-height:15px;background:#76a99d;border-radius:3px 3px 0 0}.narrow-content{max-width:900px}.report-form{overflow:hidden}.form-section{padding:28px 36px;border-bottom:1px solid var(--line)}.form-heading{display:flex;gap:13px;margin-bottom:24px}.step{color:var(--coral);font:12px 'DM Mono';padding-top:3px}.form-heading h2{margin:0 0 4px;font:600 19px 'Space Grotesk'}.form-heading p{margin:0;color:var(--muted);font-size:12px}.report-form label{display:block;color:#586762;font-size:11px;font-weight:600;margin-bottom:17px}.report-form input,.report-form select,.report-form textarea{display:block;width:100%;border:1px solid var(--line);border-radius:5px;padding:11px 12px;margin-top:7px;outline:0;color:var(--ink);background:#fbfcfa;font-size:12px}.report-form textarea{resize:vertical}.report-form input:focus,.report-form textarea:focus,.report-form select:focus{border-color:#83b3a5;box-shadow:0 0 0 3px #83b3a522}.form-row{display:grid;grid-template-columns:1fr 1fr;gap:18px}.report-form label em{float:right;font-style:normal;color:#9ba7a2;font-weight:400}.form-footer{justify-content:space-between;padding:20px 36px;color:var(--muted);font-size:11px}.form-footer>span:before{content:'✓';color:#3d9c7e;margin-right:6px}.form-footer>div{display:flex;gap:9px} -@media(max-width:900px){.sidebar{width:210px}.topbar,.content{padding-left:24px;padding-right:24px}.metric-grid{grid-template-columns:repeat(2,1fr)}.bottom-grid,.location-layout,.analytics-grid{grid-template-columns:1fr}.dashboard-grid{grid-template-columns:1fr}}@media(max-width:650px){.sidebar{position:fixed;z-index:10;left:-250px;transition:left .2s;box-shadow:8px 0 30px #163c3740}.sidebar.mobile-visible{left:0}.close-menu,.mobile-menu{display:grid!important}.topbar{height:62px;padding:0 17px}.mobile-menu{margin-right:10px}.breadcrumb span:not(:last-child){display:none}.content{padding:28px 17px 40px}.page-intro{align-items:flex-start;gap:20px;flex-direction:column}.page-intro h1{font-size:29px}.metric-grid{gap:9px}.metric-card{padding:13px}.metric-card strong{font-size:25px}.metric-card>span{margin-top:10px}.metric-card small{font-size:9px}.panel-heading{padding:19px 17px 14px}.table-toolbar{flex-wrap:wrap;padding:14px 17px}.search-field{max-width:none;flex-basis:100%}.filter-group{margin-left:0}.incidents-panel{overflow:hidden}.form-section{padding:24px 20px}.form-row{grid-template-columns:1fr;gap:0}.form-footer{padding:17px 20px;align-items:flex-end;gap:15px;flex-direction:column}.form-footer>div{width:100%;justify-content:flex-end}} \ No newline at end of file diff --git a/src/App.jsx b/src/App.jsx deleted file mode 100644 index bc0abdc..0000000 --- a/src/App.jsx +++ /dev/null @@ -1,32 +0,0 @@ -import { useState } from 'react' -import { BrowserRouter, NavLink, Route, Routes, useLocation, useNavigate } from 'react-router-dom' -import { AlertTriangle, ArrowUpRight, BarChart3, Bell, Building2, CheckCircle2, ClipboardList, FileText, Filter, Flame, LayoutDashboard, MapPin, Menu, Plus, Search, Settings, ShieldCheck, Siren, X } from 'lucide-react' -import './App.css' - -const seed = [ - { id: 'INC-2408', title: 'Forklift reversed into pallet stack', type: 'Near miss', severity: 'High', status: 'Investigation', building: 'Warehouse 04', location: 'Loading bay C', date: 'Aug 28, 2024', reporter: 'Jordan Lee', color: 'amber' }, - { id: 'INC-2407', title: 'Wet floor near employee entrance', type: 'Hazard', severity: 'Medium', status: 'Action open', building: 'Building A', location: 'Ground floor lobby', date: 'Aug 27, 2024', reporter: 'Maya Chen', color: 'blue' }, - { id: 'INC-2406', title: 'Hand laceration during material handling', type: 'Injury', severity: 'High', status: 'Review required', building: 'Fabrication 02', location: 'Line 3', date: 'Aug 26, 2024', reporter: 'Sam Rivera', color: 'red' }, - { id: 'INC-2405', title: 'Damaged guardrail on mezzanine', type: 'Property damage', severity: 'Low', status: 'Resolved', building: 'Warehouse 01', location: 'Mezzanine east', date: 'Aug 24, 2024', reporter: 'Priya Shah', color: 'green' }, -] -const navItems = [{ label: 'Overview', to: '/', icon: LayoutDashboard }, { label: 'Incidents', to: '/incidents', icon: ClipboardList, count: '24' }, { label: 'Locations', to: '/locations', icon: MapPin }, { label: 'Analytics', to: '/analytics', icon: BarChart3 }] - -export default function App() { - const [incidents, setIncidents] = useState(seed) - const [mobileOpen, setMobileOpen] = useState(false) - return
setMobileOpen(false)} />
setMobileOpen(true)} />} />} />} />} /> setIncidents([incident, ...incidents])} />} />
-} -function Sidebar({ open, close }) { return } -function Header({ openMenu }) { const location = useLocation(); const titles = { '/': 'Overview', '/incidents': 'Incidents', '/locations': 'Locations', '/analytics': 'Analytics', '/report': 'Report an incident' }; return
Safety workspace/{titles[location.pathname]}
JL
} -function Intro({ eyebrow, title, copy, action }) { return
{eyebrow}

{title}

{copy &&

{copy}

}
{action}
} -function Heading({ eyebrow, title, link, to }) { return
{eyebrow}

{title}

{link && {link} }
} -function Overview({ incidents }) { return
Report incident} />
68%complete
Aug 01Aug 08Aug 15Aug 22Aug 29
} -function Metric({ label, value, delta, icon: Icon, tone }) { return
{label}{value}↗ {delta}
} -function Action({ label, value, percent }) { return
{label}{value}
} -function Location({ label, value, width }) { return
{label}{value}
} -function Table({ incidents }) { return
{incidents.slice(0, 5).map((incident) => )}
IncidentTypeSeverityLocationStatusDate
{incident.title}{incident.id} · {incident.reporter}
{incident.type}{incident.severity}
{incident.location}{incident.building}
{incident.status}{incident.date}
} -function Incidents({ incidents }) { const [query, setQuery] = useState(''); const [filter, setFilter] = useState('All incidents'); const filtered = incidents.filter((incident) => `${incident.title} ${incident.id} ${incident.location}`.toLowerCase().includes(query.toLowerCase()) && (filter === 'All incidents' || incident.type === filter)); return
Report incident} />
setQuery(event.target.value)} placeholder="Search incidents" />
} -function Locations() { const locations = [['North Campus', 'Building A, Warehouse 04, Lab 2', 12, 'coral'], ['South Campus', 'Fabrication 02, Yard, Office block', 7, 'yellow'], ['West Campus', 'Warehouse 01, Loading dock', 5, 'blue']]; return
Add location} />
{locations.map(([name, detail, count, tone]) =>
{name}{detail}
{count} incidents⌄
)}
North Campus12 incidents this year
} -function Analytics() { return
Top signal

Material handling is your highest-risk activity.

42% of incidents in the last 90 days relate to movement or storage of materials.

Most common root causeInadequate guarding
8 incidents31% of total
Response quality94%

of corrective actions are completed on time.

} -function ReportIncident({ addIncident }) { const navigate = useNavigate(); const [form, setForm] = useState({ title: '', type: 'Near miss', severity: 'Medium', site: 'North Campus', building: 'Building A', location: '', details: '', cause: '', response: '' }); const update = (key) => (event) => setForm({ ...form, [key]: event.target.value }); const submit = (event) => { event.preventDefault(); if (!form.title || !form.location) return; addIncident({ ...form, id: `INC-${2410 + Math.floor(Math.random() * 9)}`, status: 'New report', date: 'Aug 29, 2024', reporter: 'Jordan Lee', color: form.severity === 'High' ? 'red' : 'amber' }); navigate('/incidents'); }; return