feat(employee-management): add scope filtering and management for employees
- Introduced a new `Scopes` field in the `EmployeeRecord` interface to categorize employee scopes. - Implemented scope filtering in the employee listing, allowing users to filter by various scopes. - Enhanced employee detail and edit views to manage employee scopes with checkboxes. - Updated API endpoints to support scope data during employee creation and updates. - Adjusted UI components to display employee scopes in lists and detail views.
This commit is contained in:
@@ -115,7 +115,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function patchField(field: keyof EmployeeRecord, value: string) {
|
||||
async function patchField(field: keyof EmployeeRecord, value: string | string[]) {
|
||||
if (!employee?.id) return;
|
||||
patchSaving = true;
|
||||
patchError = null;
|
||||
@@ -125,8 +125,15 @@
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ [field]: value })
|
||||
});
|
||||
if (!res.ok) throw new Error('Update failed');
|
||||
const json = await res.json();
|
||||
const json = await res.json().catch(() => ({}));
|
||||
if (!res.ok) {
|
||||
const msg = (json as { error?: string }).error ?? 'Update failed';
|
||||
const data = (json as { data?: unknown }).data;
|
||||
patchError = data && typeof data === 'object' && data !== null
|
||||
? `${msg}: ${JSON.stringify(data)}`
|
||||
: msg;
|
||||
return;
|
||||
}
|
||||
employee = (json.employee ?? employee) as EmployeeRecord;
|
||||
} catch (e) {
|
||||
patchError = e instanceof Error ? e.message : 'Update failed';
|
||||
@@ -135,6 +142,37 @@
|
||||
}
|
||||
}
|
||||
|
||||
const SCOPE_OPTIONS = [
|
||||
'General',
|
||||
'OFFICE',
|
||||
'IT',
|
||||
'Framing',
|
||||
'Insulation',
|
||||
'Hanging',
|
||||
'Scarp & Paper',
|
||||
'Taping',
|
||||
'Sand & Mask',
|
||||
'WT & Cleanup',
|
||||
'Grid & Tile'
|
||||
];
|
||||
|
||||
function employeeScopes(): string[] {
|
||||
const raw = employee?.Scopes;
|
||||
if (Array.isArray(raw)) return raw;
|
||||
if (typeof raw === 'string') return raw ? [raw] : [];
|
||||
return [];
|
||||
}
|
||||
|
||||
function toggleScope(scope: string) {
|
||||
if (!employee) return;
|
||||
const current = employeeScopes();
|
||||
const next = current.includes(scope)
|
||||
? current.filter((s) => s !== scope)
|
||||
: [...current, scope];
|
||||
employee = { ...employee, Scopes: next };
|
||||
patchField('Scopes', next);
|
||||
}
|
||||
|
||||
async function confirmDelete() {
|
||||
deleteSaving = true;
|
||||
deleteError = null;
|
||||
@@ -434,6 +472,37 @@
|
||||
<dd>{employee.Voxer_ID || '—'}</dd>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-col gap-1">
|
||||
<Label class="text-muted-foreground text-sm">Scopes</Label>
|
||||
{#if editMode}
|
||||
<div class="border-input bg-background flex min-h-[80px] flex-wrap gap-2 rounded-md border px-3 py-2">
|
||||
{#each SCOPE_OPTIONS as scope}
|
||||
<label class="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<input
|
||||
type="checkbox"
|
||||
class="border-input size-4 rounded"
|
||||
checked={employeeScopes().includes(scope)}
|
||||
onchange={() => toggleScope(scope)}
|
||||
disabled={patchSaving}
|
||||
/>
|
||||
{scope}
|
||||
</label>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<dd>
|
||||
{#if employeeScopes().length > 0}
|
||||
<div class="flex flex-wrap gap-1">
|
||||
{#each employeeScopes() as scope}
|
||||
<Badge variant="secondary">{scope}</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
—
|
||||
{/if}
|
||||
</dd>
|
||||
{/if}
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<!-- Notes section -->
|
||||
|
||||
Reference in New Issue
Block a user