asset-management/src/pages/employees/EmployeeList.jsx

131 lines
4.0 KiB
JavaScript

import { useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { Button, Card, Icon, PageHeader, SelectInput } from '../../components/ui'
import RecordView from '../../components/activity/RecordView'
import useRecordView from '../../hooks/useRecordView'
import { RECORD_VIEWS } from '../../api/recordView'
import {
EMPLOYMENT_FILTERS,
fetchEmployeeListPage,
resolveEmployeeInstanceId,
} from '../../api/employees'
import { useAuth } from '../../store/AuthContext'
import AddEmployeeModal from './AddEmployeeModal'
const VIEW = RECORD_VIEWS.employees
export default function EmployeeList() {
const navigate = useNavigate()
const { logout } = useAuth()
// The table misses Update fields Zino doesn't map and knows nothing of the
// workflow state, so this fetcher overlays both and filters Active/Inactive.
const view = useRecordView(VIEW.templateUid, { fetcher: fetchEmployeeListPage, filter: 'active' })
const [adding, setAdding] = useState(false)
const [opening, setOpening] = useState(false)
const [notice, setNotice] = useState(null)
/** `values` is keyed by Zino field id — see EMPLOYEE_FIELDS in api/employees.js. */
function handleCreated(values) {
const name = [values.field_1, values.field_34].filter(Boolean).join(' ').trim()
setNotice({
tone: 'success',
text: `${name || 'Employee'} was created and onboarding has started.`,
})
// Records live in the workflow, so re-read the view rather than guessing.
view.reload()
}
/**
* Open the detail view for a row. The detail view needs the workflow
* instance_id: use the row's own when it has one, otherwise look it up.
*/
async function openDetails(row) {
if (opening) return
setOpening(true)
setNotice(null)
try {
const instanceId = await resolveEmployeeInstanceId(row)
if (instanceId == null) {
setNotice({
tone: 'danger',
text: 'Could not find this employee in the workflow, so its details cannot be shown.',
})
return
}
navigate(`/employees/${encodeURIComponent(instanceId)}`)
} catch (err) {
if (err?.status === 401) {
logout()
navigate('/login', { replace: true })
return
}
setNotice({ tone: 'danger', text: err?.message || 'Could not open this employee.' })
} finally {
setOpening(false)
}
}
return (
<>
<PageHeader
title="Employees"
subtitle="All staff on record, read live from the employee workflow."
actions={
<Button variant="primary" icon="plus" onClick={() => setAdding(true)}>
Add Employee
</Button>
}
/>
{opening && (
<div className="notice notice--warning" role="status">
<Icon name="search" size={15} />
<span>Opening employee…</span>
</div>
)}
{notice && (
<div className={`notice notice--${notice.tone}`} role="status">
<Icon name={notice.tone === 'success' ? 'check' : 'alert'} size={15} />
<span>{notice.text}</span>
<button
type="button"
className="notice__close"
onClick={() => setNotice(null)}
aria-label="Dismiss"
>
<Icon name="close" size={14} />
</button>
</div>
)}
<Card flush>
<RecordView
{...view}
rowKey={VIEW.rowKey}
emptyText={
view.query.filter === 'inactive' ? 'No inactive employees.' : 'No employees yet.'
}
onRowClick={openDetails}
toolbar={
<div className="filter">
<SelectInput
value={view.query.filter}
onChange={(e) => view.setFilter(e.target.value)}
options={EMPLOYMENT_FILTERS}
aria-label="Show employees"
/>
</div>
}
/>
</Card>
<AddEmployeeModal
open={adding}
onClose={() => setAdding(false)}
onCreated={handleCreated}
/>
</>
)
}