Component Library
This document provides documentation for the reusable components in the Kuviq application.
Overview
Components are organized into these categories:
- Common - Generic, reusable UI components
- Forms - Form controls and layouts
- Dashboard - Dashboard widgets
- Inspections - Inspection-specific components
- Layout - Page layout components
All components are built with Material-UI and follow consistent patterns for:
- Accessibility (WCAG AA compliant)
- Responsive design
- Theme integration
- TypeScript types
Common Components
DataTable
A feature-rich data table with sorting, filtering, and pagination.
import DataTable from '@/components/common/DataTable'
Props
| Prop | Type | Description |
|---|---|---|
data | T[] | Array of data items |
columns | Column<T>[] | Column definitions |
loading | boolean | Show loading state |
onRowClick | (row: T) => void | Row click handler |
sortable | boolean | Enable sorting |
filterable | boolean | Enable filtering |
pagination | boolean | Enable pagination |
pageSize | number | Items per page |
selectable | boolean | Enable row selection |
onSelectionChange | (selected: T[]) => void | Selection handler |
Example
<DataTable
data={items}
columns={[
{ field: 'name', header: 'Name', sortable: true },
{ field: 'status', header: 'Status', render: (row) => <StatusChip status={row.status} /> },
{ field: 'updatedAt', header: 'Updated', format: 'date' },
]}
onRowClick={(item) => navigate(`/items/${item.id}`)}
pagination
pageSize={25}
/>
List (Deprecated)
The List component is deprecated as of November 2025. Use UnifiedDataTable from the design system instead. Scheduled for removal in Q1 2026.
A unified list component for displaying collections.
// Deprecated - use UnifiedDataTable instead
import List from '@/components/common/list/List'
// New approach
import { UnifiedDataTable } from '@/design-system'
Props
| Prop | Type | Description |
|---|---|---|
items | T[] | Array of items |
renderItem | (item: T) => ReactNode | Item renderer |
loading | boolean | Loading state |
emptyMessage | string | Empty state message |
searchable | boolean | Enable search |
onSearch | (query: string) => void | Search handler |
QRScannerClean
QR code scanner using the device camera.
import QRScannerClean from '@/components/common/QRScannerClean'
Props
| Prop | Type | Description |
|---|---|---|
onScan | (data: string) => void | Scan success handler |
onError | (error: Error) => void | Error handler |
onClose | () => void | Close handler |
facingMode | 'user' | 'environment' | Camera selection |
Example
<QRScannerClean
onScan={(data) => {
const itemId = parseQRCode(data)
navigate(`/items/${itemId}`)
}}
onError={(error) => showError('Failed to scan QR code')}
onClose={() => setShowScanner(false)}
/>
OptimizedImage
Lazy-loaded image with placeholder and error handling.
import OptimizedImage from '@/components/common/OptimizedImage'
Props
| Prop | Type | Description |
|---|---|---|
src | string | Image source URL |
alt | string | Alt text (required) |
width | number | Image width |
height | number | Image height |
placeholder | string | Placeholder image |
onLoad | () => void | Load handler |
onError | () => void | Error handler |
SessionWarning
Displays session timeout warning dialog.
import SessionWarning from '@/components/common/SessionWarning'
Props
| Prop | Type | Description |
|---|---|---|
open | boolean | Dialog visibility |
timeRemaining | number | Seconds until logout |
onExtend | () => void | Extend session |
onLogout | () => void | Manual logout |
UsageLimit
Shows usage limit progress and warnings.
import UsageLimit from '@/components/common/UsageLimit'
Props
| Prop | Type | Description |
|---|---|---|
current | number | Current usage |
limit | number | Maximum limit |
label | string | Display label |
showWarning | boolean | Show warning at 80% |
SkipLink
Accessibility skip link for keyboard navigation.
import SkipLink from '@/components/common/SkipLink'
Usage
Add at the top of your app:
<SkipLink />
<main id="main-content" role="main">
{/* Page content */}
</main>
Form Components
FormLayout
Consistent form layout wrapper.
import { FormLayout } from '@/components/forms/FormLayout'
Props
| Prop | Type | Description |
|---|---|---|
title | string | Form title |
subtitle | string | Optional subtitle |
loading | boolean | Loading state |
onSubmit | () => void | Submit handler |
onCancel | () => void | Cancel handler |
submitLabel | string | Submit button text |
cancelLabel | string | Cancel button text |
FormField
Form field wrapper with label, error, and helper text.
import { FormField } from '@/components/forms/FormField'
Props
| Prop | Type | Description |
|---|---|---|
label | string | Field label |
required | boolean | Mark as required |
error | string | Error message |
helperText | string | Helper text |
children | ReactNode | Input element |
Example
<FormField label="Name" required error={errors.name}>
<TextField
value={values.name}
onChange={(e) => handleChange('name', e.target.value)}
/>
</FormField>
Dashboard Widgets
DashboardWidget
Base widget component for dashboard cards.
import DashboardWidget from '@/components/dashboard/DashboardWidget'
Props
| Prop | Type | Description |
|---|---|---|
title | string | Widget title |
subtitle | string | Optional subtitle |
loading | boolean | Loading state |
error | Error | Error state |
actions | ReactNode | Header actions |
children | ReactNode | Widget content |
StatusSummaryCards
Displays inspection status summary with smart conditional rendering. Shows actionable status cards (Overdue, Due Today, Due This Week) when there are inspections needing attention, or an "All OK" message when everything is up to date.
import { StatusSummaryCards } from '@/components/dashboard/widgets/StatusSummaryCards'
Behavior
- When inspections need attention: Shows non-zero status cards:
- Overdue (red) - Past due date
- Due Today (yellow) - Scheduled for today
- Due This Week (green) - Next 7 days (excluding today)
- When all caught up: Shows "All inspections up to date" message, plus optional "Due This Week" card if any upcoming
Navigation
Clicking each card navigates to the filtered inspections list:
- Overdue →
/inspections?view=overdue - Due Today →
/inspections?view=due-today - Due This Week →
/inspections?view=due-this-week
InspectionTrendsWidget
Shows inspection trends over time.
import InspectionTrendsWidget from '@/components/dashboard/InspectionTrendsWidget'
Props
| Prop | Type | Description |
|---|---|---|
data | TrendData[] | Trend data points |
period | 'week' | 'month' | 'year' | Time period |
NextInspectionsList
Lists upcoming inspections.
import NextInspectionsList from '@/components/dashboard/NextInspectionsList'
Props
| Prop | Type | Description |
|---|---|---|
inspections | Inspection[] | Upcoming inspections |
limit | number | Max items to show |
onViewAll | () => void | View all handler |
QuickActions
Quick action buttons for common operations.
import QuickActions from '@/components/dashboard/QuickActions'
ScanButton
QR scan action button.
import ScanButton from '@/components/dashboard/ScanButton'
Props
| Prop | Type | Description |
|---|---|---|
onScan | (itemId: string) => void | Scan handler |
disabled | boolean | Disabled state |
Inspection Components
InspectionList
Displays list of inspections with actions.
import InspectionList from '@/components/inspections/InspectionList'
Props
| Prop | Type | Description |
|---|---|---|
inspections | Inspection[] | Inspection list |
loading | boolean | Loading state |
onView | (inspection: Inspection) => void | View handler |
onStart | (inspection: Inspection) => void | Start handler |
onPause | (inspection: Inspection) => void | Pause handler |
onResume | (inspection: Inspection) => void | Resume handler |
onCancel | (inspection: Inspection) => void | Cancel handler |
InspectionExecution
Inspection execution interface.
import InspectionExecution from '@/components/inspections/InspectionExecution'
TemplateList
Displays inspection templates.
import TemplateList from '@/components/inspections/TemplateList'
TemplateForm
Template creation/editing form.
import TemplateForm from '@/components/inspections/TemplateForm'
Layout Components
PageLayout
Standard page layout with header and navigation.
import PageLayout from '@/components/layout/PageLayout'
Props
| Prop | Type | Description |
|---|---|---|
title | string | Page title |
breadcrumbs | Breadcrumb[] | Breadcrumb trail |
actions | ReactNode | Header actions |
children | ReactNode | Page content |
NavigationDrawer
Side navigation drawer.
import NavigationDrawer from '@/components/layout/NavigationDrawer'
ErrorBoundary
React error boundary for graceful error handling.
import ErrorBoundary from '@/components/layout/ErrorBoundary'
Example
<ErrorBoundary fallback={<ErrorPage />}>
<Dashboard />
</ErrorBoundary>
Permission Components
PermissionGuard
Conditionally renders children based on permissions.
import PermissionGuard from '@/components/permissions/PermissionGuard'
Props
| Prop | Type | Description |
|---|---|---|
permission | Permission | Required permission |
permissions | Permission[] | Any of these permissions |
requireAll | boolean | Require all permissions |
fallback | ReactNode | Fallback when denied |
children | ReactNode | Protected content |
Example
<PermissionGuard permission="items.delete">
<Button onClick={handleDelete}>Delete Item</Button>
</PermissionGuard>
FeatureGate
Conditionally renders based on subscription features.
import FeatureGate from '@/components/permissions/FeatureGate'
Props
| Prop | Type | Description |
|---|---|---|
feature | string | Feature name |
fallback | ReactNode | Upgrade prompt |
children | ReactNode | Feature content |
Export Components
ExportDialog
Main export dialog that orchestrates the export workflow with templates and custom configuration.
import { ExportDialog } from '@/components/common/export/ExportDialog'
Props
| Prop | Type | Description |
|---|---|---|
open | boolean | Dialog visibility |
onClose | () => void | Close handler |
entityType | EntityType | Type of data to export |
title | string | Dialog title |
selectedIds | string[] | Pre-selected record IDs |
defaultConfig | Partial<ExportConfig> | Default configuration |
Sub-Components
The ExportDialog uses these modular sub-components for configuration:
| Component | Description |
|---|---|
ExportBasicConfig | Format selection and filename configuration |
ExportFieldSelection | Field picker with headers/metadata options |
ExportFilterConfig | Dynamic filter rule management |
ExportConditionalFormatting | Excel-specific cell styling rules |
ExportFormatOptions | Date/number formats, CSV delimiter, Excel multi-sheet |
Example
<ExportDialog
open={showExport}
onClose={() => setShowExport(false)}
entityType="items"
title="Items"
selectedIds={selectedItemIds}
/>
ExportTemplateManager
Manages export templates with CRUD operations.
import { ExportTemplateManager } from '@/components/common/export/ExportTemplateManager'
Props
| Prop | Type | Description |
|---|---|---|
open | boolean | Dialog visibility |
onClose | () => void | Close handler |
onTemplateSelect | (template: ExportTemplate) => void | Template selection handler |
Best Practices
Accessibility
All components follow WCAG AA guidelines:
- Minimum 4.5:1 color contrast
- Keyboard navigation support
- ARIA attributes where needed
- Focus management
Responsive Design
Components use MUI breakpoints:
sx={{
width: { xs: '100%', sm: '50%', md: '33%' },
padding: { xs: 2, md: 3 },
}}
Touch Targets
Minimum 44px touch targets on mobile:
sx={{
minWidth: { '@media (pointer: coarse)': 44 },
minHeight: { '@media (pointer: coarse)': 44 },
}}
Responsive Button Layout
Button groups must be responsive. Use column layout on mobile with full-width buttons:
import { useTheme, useMediaQuery, Box, Button } from '@mui/material'
function MyComponent() {
const theme = useTheme()
const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
return (
<Box
sx={{
display: 'flex',
flexDirection: isMobile ? 'column' : 'row',
gap: 1,
}}
>
<Button variant="outlined" fullWidth={isMobile}>
Cancel
</Button>
<Button variant="contained" fullWidth={isMobile}>
{isMobile ? 'Save' : 'Save Changes'}
</Button>
</Box>
)
}
Key patterns:
- Use
fullWidth={isMobile}on all action buttons - Use column layout on mobile, row on desktop:
flexDirection: isMobile ? 'column' : 'row' - Shorten button text on mobile when needed (e.g., "Save" vs "Save Changes")
- For dialogs, use
fullScreen={isMobile}prop on the Dialog component - For DialogActions, apply custom styling to stack buttons:
<DialogActions
sx={{
flexDirection: isMobile ? 'column' : 'row',
gap: isMobile ? 1 : 0,
p: isMobile ? 2 : undefined,
'& > :not(:first-of-type)': { ml: isMobile ? 0 : undefined },
}}
>
Theme Integration
Use theme tokens for consistency:
sx={{
color: 'primary.main',
backgroundColor: 'background.paper',
borderColor: 'divider',
}}
TypeScript
All components are fully typed:
interface MyComponentProps {
title: string
onAction?: () => void
children: React.ReactNode
}
export default function MyComponent({ title, onAction, children }: MyComponentProps) {
// ...
}