feat: initialize project structure with core components and pages
- Added shared styles for UI components in `src/components/styles.ts`. - Created configuration constants in `src/lib/config.ts`. - Implemented date formatting and greeting functions in `src/lib/dates.ts` and corresponding tests in `src/lib/dates.test.ts`. - Developed incident management functions in `src/lib/incidents.ts` with tests in `src/lib/incidents.test.ts`. - Established navigation structure in `src/lib/navigation.ts`. - Seeded demo data for incidents and users in `src/lib/seed.ts`. - Defined tone classes for UI elements in `src/lib/tones.ts`. - Created type definitions for incidents and related entities in `src/lib/types.ts`. - Set up main application entry point in `src/main.tsx`. - Developed various pages including About, Analytics, Incidents, Locations, Overview, and Report Incident. - Configured testing setup in `src/test/setup.ts` and TypeScript configurations. - Integrated Vite for development and testing with appropriate plugins.
This commit is contained in:
1 parent
732d4d8931
commit
a382fa904a
53 files changed
+3523
-31
No files matched your search
@@ -0,0 +1,89 @@
|
||||
import { ChevronDown, FileText, Info, Plus, Settings, ShieldCheck, X } from 'lucide-react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { primaryNav } from '../lib/navigation'
|
||||
import { currentUser, workspace } from '../lib/seed'
|
||||
import { Eyebrow } from './Eyebrow'
|
||||
import { iconButton } from './styles'
|
||||
|
||||
const navLink = ({ isActive }: { isActive: boolean }) =>
|
||||
`flex h-[42px] items-center gap-3 rounded-md px-3 text-[13px] hover:bg-forest-800 hover:text-white ${isActive ? 'bg-forest-800 text-white' : 'text-forest-200'}`
|
||||
const navSection = 'mb-6 border-b border-forest-700 pb-[23px]'
|
||||
|
||||
interface SidebarProps {
|
||||
open: boolean
|
||||
onClose: () => void
|
||||
openIncidentCount: number
|
||||
}
|
||||
|
||||
export function Sidebar({ open, onClose, openIncidentCount }: SidebarProps) {
|
||||
return (
|
||||
<aside
|
||||
className={`fixed inset-y-0 z-10 flex w-[260px] flex-none flex-col overflow-y-auto bg-forest-950 px-[15px] pt-7 pb-[18px] text-forest-100 shadow-[8px_0_30px_#163c3740] transition-[left] duration-200 sm:static sm:min-h-screen sm:w-[230px] sm:shadow-none lg:w-[246px] ${open ? 'left-0' : '-left-[270px]'}`}
|
||||
>
|
||||
<div className="flex items-center gap-2.5 px-3 pb-[31px] text-white">
|
||||
<div className="grid size-[34px] flex-none place-items-center rounded-[9px] bg-coral text-white">
|
||||
<ShieldCheck size={20} />
|
||||
</div>
|
||||
<div>
|
||||
<strong className="block font-display text-[19px] leading-tight sm:text-[17px] lg:text-[19px] font-bold tracking-[-0.5px] whitespace-nowrap">Open SafetyLog</strong>
|
||||
<span className="mt-0.5 block text-3xs text-forest-300">EHS · Incident intelligence</span>
|
||||
</div>
|
||||
<button type="button" className={`${iconButton} absolute top-2 right-2 sm:hidden`} onClick={onClose} aria-label="Close menu">
|
||||
<X size={20} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-[27px] flex items-center gap-2.5 rounded-[7px] border border-[#316158] p-[11px]">
|
||||
<div className="grid size-[29px] place-items-center rounded-full bg-[#f1c865] font-mono text-2xs font-bold text-[#143d37]">{workspace.initials}</div>
|
||||
<div>
|
||||
<Eyebrow>Workspace</Eyebrow>
|
||||
<strong className="block text-[13px] text-white">{workspace.name}</strong>
|
||||
</div>
|
||||
<ChevronDown size={16} className="ml-auto" />
|
||||
</div>
|
||||
|
||||
<nav className={navSection}>
|
||||
{primaryNav.map(({ label, to, icon: Icon }) => (
|
||||
<NavLink key={to} to={to} end onClick={onClose} className={navLink}>
|
||||
<Icon size={18} />
|
||||
<span>{label}</span>
|
||||
{to === '/incidents' && openIncidentCount > 0 && (
|
||||
<em className="ml-auto rounded-[10px] bg-coral px-[7px] py-0.5 font-mono text-2xs text-white not-italic">{openIncidentCount}</em>
|
||||
)}
|
||||
</NavLink>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className={navSection}>
|
||||
<Eyebrow className="px-3 pb-2 text-[#8da29b]">Manage</Eyebrow>
|
||||
<NavLink to="/report" onClick={onClose} className={navLink}>
|
||||
<Plus size={18} />
|
||||
<span>Report an incident</span>
|
||||
</NavLink>
|
||||
<a href="#resources" className={navLink({ isActive: false })}>
|
||||
<FileText size={18} />
|
||||
<span>Resources</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="mt-auto">
|
||||
<NavLink to="/about" onClick={onClose} className={navLink}>
|
||||
<Info size={18} />
|
||||
<span>About</span>
|
||||
</NavLink>
|
||||
<a href="#settings" className={navLink({ isActive: false })}>
|
||||
<Settings size={18} />
|
||||
<span>Settings</span>
|
||||
</a>
|
||||
<div className="mt-4 flex items-center gap-2.5 border-t border-forest-700 px-2 pt-5">
|
||||
<div className="grid size-[31px] place-items-center rounded-full bg-[#5c8274] font-mono text-2xs text-white">{currentUser.initials}</div>
|
||||
<div>
|
||||
<strong className="block text-xs text-white">{currentUser.name}</strong>
|
||||
<span className="mt-0.5 block text-3xs text-forest-300">{currentUser.role}</span>
|
||||
</div>
|
||||
<span className="ml-auto tracking-[2px] text-[#91aaa0]">•••</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user