Maintenance log book app — initial scaffold
Multi-department fault log: NewBook-synced room locations + manual locations with categories, six-state task flow (submitted/in progress/ hold-parts/hold-later/temporary fix/fixed), photos per stage, priorities with unusable flag and per-task NewBook out-of-order push, costs on resolve, comment/audit thread, recurring task templates with note-to-template carryover, asset register, contractor register with document attachments, staff/contractor allocation, occupancy-aware summary filter, searchable history with CSV export, email notifications. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
commit
6ca395097e
47 changed files with 6727 additions and 0 deletions
44
frontend/src/components/shared.tsx
Normal file
44
frontend/src/components/shared.tsx
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import { AlertTriangle } from 'lucide-react'
|
||||
import type { Priority, TaskStatus } from '../types'
|
||||
import { PRIORITY_LABELS, STATUS_LABELS } from '../types'
|
||||
|
||||
export function PriorityBadge({ priority }: { priority: Priority }) {
|
||||
return <span className={`badge badge-prio-${priority}`}>{PRIORITY_LABELS[priority]}</span>
|
||||
}
|
||||
|
||||
export function StatusBadge({ status }: { status: TaskStatus }) {
|
||||
return <span className={`badge badge-st-${status}`}>{STATUS_LABELS[status]}</span>
|
||||
}
|
||||
|
||||
export function UnusableBadge() {
|
||||
return (
|
||||
<span className="badge badge-unusable">
|
||||
<AlertTriangle size={11} strokeWidth={1.75} />
|
||||
Unusable
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export function formatDate(iso: string | null | undefined): string {
|
||||
if (!iso) return '—'
|
||||
const d = new Date(iso)
|
||||
return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })
|
||||
}
|
||||
|
||||
export function formatDateTime(iso: string | null | undefined): string {
|
||||
if (!iso) return '—'
|
||||
const d = new Date(iso)
|
||||
return d.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' }) + ' ' +
|
||||
d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' })
|
||||
}
|
||||
|
||||
export function daysOpen(createdAt: string): number {
|
||||
return Math.floor((Date.now() - new Date(createdAt).getTime()) / 86400000)
|
||||
}
|
||||
|
||||
export function ageLabel(createdAt: string): string {
|
||||
const days = daysOpen(createdAt)
|
||||
if (days === 0) return 'today'
|
||||
if (days === 1) return '1 day'
|
||||
return `${days} days`
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue