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:
+74
-6
@@ -24,8 +24,23 @@
|
|||||||
let loading = $state(true);
|
let loading = $state(true);
|
||||||
let loadingMore = $state(false);
|
let loadingMore = $state(false);
|
||||||
let error = $state<string | null>(null);
|
let error = $state<string | null>(null);
|
||||||
|
const SCOPE_OPTIONS = [
|
||||||
|
'General',
|
||||||
|
'OFFICE',
|
||||||
|
'IT',
|
||||||
|
'Framing',
|
||||||
|
'Insulation',
|
||||||
|
'Hanging',
|
||||||
|
'Scarp & Paper',
|
||||||
|
'Taping',
|
||||||
|
'Sand & Mask',
|
||||||
|
'WT & Cleanup',
|
||||||
|
'Grid & Tile'
|
||||||
|
];
|
||||||
|
|
||||||
type TypeFilterValue = 'all' | 'Employee' | 'Sub-Contractor';
|
type TypeFilterValue = 'all' | 'Employee' | 'Sub-Contractor';
|
||||||
let typeFilter = $state<TypeFilterValue>('all');
|
let typeFilter = $state<TypeFilterValue>('all');
|
||||||
|
let scopeFilter = $state<string>('all');
|
||||||
let searchQuery = $state('');
|
let searchQuery = $state('');
|
||||||
let nextPage = $state(2);
|
let nextPage = $state(2);
|
||||||
let searchMode = $state(false); // true when showing server-side search results
|
let searchMode = $state(false); // true when showing server-side search results
|
||||||
@@ -35,9 +50,14 @@
|
|||||||
return typeFilter === 'all' ? '' : `&type=${encodeURIComponent(typeFilter)}`;
|
return typeFilter === 'all' ? '' : `&type=${encodeURIComponent(typeFilter)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function scopeParam(): string {
|
||||||
|
return scopeFilter === 'all' || !scopeFilter ? '' : `&scope=${encodeURIComponent(scopeFilter)}`;
|
||||||
|
}
|
||||||
|
|
||||||
function listUrl(): string {
|
function listUrl(): string {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (typeFilter !== 'all') params.set('type', typeFilter);
|
if (typeFilter !== 'all') params.set('type', typeFilter);
|
||||||
|
if (scopeFilter !== 'all' && scopeFilter) params.set('scope', scopeFilter);
|
||||||
const q = searchQuery.trim();
|
const q = searchQuery.trim();
|
||||||
if (q) params.set('search', q);
|
if (q) params.set('search', q);
|
||||||
const s = params.toString();
|
const s = params.toString();
|
||||||
@@ -48,6 +68,8 @@
|
|||||||
const params = get(page).url.searchParams;
|
const params = get(page).url.searchParams;
|
||||||
const type = params.get('type');
|
const type = params.get('type');
|
||||||
typeFilter = type === 'Sub-Contractor' ? 'Sub-Contractor' : type === 'Employee' ? 'Employee' : 'all';
|
typeFilter = type === 'Sub-Contractor' ? 'Sub-Contractor' : type === 'Employee' ? 'Employee' : 'all';
|
||||||
|
const scope = params.get('scope') ?? 'all';
|
||||||
|
scopeFilter = SCOPE_OPTIONS.includes(scope) ? scope : 'all';
|
||||||
searchQuery = params.get('search') ?? '';
|
searchQuery = params.get('search') ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,6 +84,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setScopeFilter(value: string) {
|
||||||
|
scopeFilter = value;
|
||||||
|
goto(listUrl(), { replaceState: true });
|
||||||
|
loading = true;
|
||||||
|
if (searchQuery.trim()) {
|
||||||
|
searchEmployees(searchQuery);
|
||||||
|
} else {
|
||||||
|
loadEmployees();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let openCreate = $state(false);
|
let openCreate = $state(false);
|
||||||
let openEdit = $state(false);
|
let openEdit = $state(false);
|
||||||
let openDelete = $state(false);
|
let openDelete = $state(false);
|
||||||
@@ -84,7 +117,7 @@
|
|||||||
async function loadEmployees() {
|
async function loadEmployees() {
|
||||||
searchMode = false;
|
searchMode = false;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/employees?page=1&perPage=200${typeParam()}`);
|
const res = await fetch(`/api/employees?page=1&perPage=200${typeParam()}${scopeParam()}`);
|
||||||
if (!res.ok) throw new Error(res.statusText);
|
if (!res.ok) throw new Error(res.statusText);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
employees = json.employees ?? [];
|
employees = json.employees ?? [];
|
||||||
@@ -108,7 +141,7 @@
|
|||||||
error = null;
|
error = null;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(
|
const res = await fetch(
|
||||||
`/api/employees?page=1&perPage=500&search=${encodeURIComponent(query.trim())}${typeParam()}`
|
`/api/employees?page=1&perPage=500&search=${encodeURIComponent(query.trim())}${typeParam()}${scopeParam()}`
|
||||||
);
|
);
|
||||||
if (!res.ok) throw new Error(res.statusText);
|
if (!res.ok) throw new Error(res.statusText);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
@@ -144,7 +177,7 @@
|
|||||||
if (loadingMore || employees.length >= total) return;
|
if (loadingMore || employees.length >= total) return;
|
||||||
loadingMore = true;
|
loadingMore = true;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/employees?page=${nextPage}&perPage=100${typeParam()}`);
|
const res = await fetch(`/api/employees?page=${nextPage}&perPage=100${typeParam()}${scopeParam()}`);
|
||||||
if (!res.ok) throw new Error(res.statusText);
|
if (!res.ok) throw new Error(res.statusText);
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
const list = json.employees ?? [];
|
const list = json.employees ?? [];
|
||||||
@@ -338,6 +371,29 @@
|
|||||||
Sub-Contractor
|
Sub-Contractor
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
bind:value={scopeFilter}
|
||||||
|
onValueChange={() => {
|
||||||
|
goto(listUrl(), { replaceState: true });
|
||||||
|
loading = true;
|
||||||
|
if (searchQuery.trim()) {
|
||||||
|
searchEmployees(searchQuery);
|
||||||
|
} else {
|
||||||
|
loadEmployees();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Trigger class="w-[180px]" aria-label="Filter by scope">
|
||||||
|
{scopeFilter === 'all' ? 'All scopes' : scopeFilter}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Item value="all" label="All scopes">All scopes</Select.Item>
|
||||||
|
{#each SCOPE_OPTIONS as scope}
|
||||||
|
<Select.Item value={scope} label={scope}>{scope}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
<div class="relative min-w-0 flex-1 sm:max-w-sm">
|
<div class="relative min-w-0 flex-1 sm:max-w-sm">
|
||||||
<SearchIcon
|
<SearchIcon
|
||||||
class="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2"
|
class="text-muted-foreground pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2"
|
||||||
@@ -364,6 +420,7 @@
|
|||||||
<Table.Head>Last Name</Table.Head>
|
<Table.Head>Last Name</Table.Head>
|
||||||
<Table.Head>Type</Table.Head>
|
<Table.Head>Type</Table.Head>
|
||||||
<Table.Head>Status</Table.Head>
|
<Table.Head>Status</Table.Head>
|
||||||
|
<Table.Head>Scopes</Table.Head>
|
||||||
<Table.Head>Phone</Table.Head>
|
<Table.Head>Phone</Table.Head>
|
||||||
<Table.Head>Email</Table.Head>
|
<Table.Head>Email</Table.Head>
|
||||||
<Table.Head>Voxer ID</Table.Head>
|
<Table.Head>Voxer ID</Table.Head>
|
||||||
@@ -373,13 +430,13 @@
|
|||||||
<Table.Body>
|
<Table.Body>
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell colspan={8} class="text-muted-foreground py-8 text-center">
|
<Table.Cell colspan={9} class="text-muted-foreground py-8 text-center">
|
||||||
Loading…
|
Loading…
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
{:else if error}
|
{:else if error}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell colspan={8} class="text-destructive py-8 text-center">
|
<Table.Cell colspan={9} class="text-destructive py-8 text-center">
|
||||||
{error}
|
{error}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
@@ -402,6 +459,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
<Table.Cell>{employee.Status}</Table.Cell>
|
<Table.Cell>{employee.Status}</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
{#if (employee.Scopes ?? []).length > 0}
|
||||||
|
<div class="flex flex-wrap gap-1">
|
||||||
|
{#each (Array.isArray(employee.Scopes) ? employee.Scopes : []) as scope}
|
||||||
|
<Badge variant="secondary" class="text-xs">{scope}</Badge>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<span class="text-muted-foreground">—</span>
|
||||||
|
{/if}
|
||||||
|
</Table.Cell>
|
||||||
<Table.Cell>{employee.Phone_Number}</Table.Cell>
|
<Table.Cell>{employee.Phone_Number}</Table.Cell>
|
||||||
<Table.Cell>{employee.Email_Address}</Table.Cell>
|
<Table.Cell>{employee.Email_Address}</Table.Cell>
|
||||||
<Table.Cell>{employee.Voxer_ID}</Table.Cell>
|
<Table.Cell>{employee.Voxer_ID}</Table.Cell>
|
||||||
@@ -441,7 +509,7 @@
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell colspan={8} class="text-muted-foreground text-center">
|
<Table.Cell colspan={9} class="text-muted-foreground text-center">
|
||||||
{searchQuery.trim()
|
{searchQuery.trim()
|
||||||
? 'No matching employees.'
|
? 'No matching employees.'
|
||||||
: 'No employee records found.'}
|
: 'No employee records found.'}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export interface EmployeeRecord {
|
|||||||
Voxer_ID: string;
|
Voxer_ID: string;
|
||||||
Status: string;
|
Status: string;
|
||||||
Notes?: string;
|
Notes?: string;
|
||||||
|
Scopes?: string[];
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
if (!employee?.id) return;
|
||||||
patchSaving = true;
|
patchSaving = true;
|
||||||
patchError = null;
|
patchError = null;
|
||||||
@@ -125,8 +125,15 @@
|
|||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ [field]: value })
|
body: JSON.stringify({ [field]: value })
|
||||||
});
|
});
|
||||||
if (!res.ok) throw new Error('Update failed');
|
const json = await res.json().catch(() => ({}));
|
||||||
const json = await res.json();
|
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;
|
employee = (json.employee ?? employee) as EmployeeRecord;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
patchError = e instanceof Error ? e.message : 'Update failed';
|
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() {
|
async function confirmDelete() {
|
||||||
deleteSaving = true;
|
deleteSaving = true;
|
||||||
deleteError = null;
|
deleteError = null;
|
||||||
@@ -434,6 +472,37 @@
|
|||||||
<dd>{employee.Voxer_ID || '—'}</dd>
|
<dd>{employee.Voxer_ID || '—'}</dd>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</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>
|
</dl>
|
||||||
|
|
||||||
<!-- Notes section -->
|
<!-- Notes section -->
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface EmployeeRecord {
|
|||||||
Email_Address: string;
|
Email_Address: string;
|
||||||
Voxer_ID: string;
|
Voxer_ID: string;
|
||||||
Status: string;
|
Status: string;
|
||||||
|
Scopes?: string[];
|
||||||
[key: string]: unknown;
|
[key: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,9 +21,24 @@ function escapeFilterValue(s: string): string {
|
|||||||
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
return s.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SCOPE_OPTIONS = [
|
||||||
|
'General',
|
||||||
|
'OFFICE',
|
||||||
|
'IT',
|
||||||
|
'Framing',
|
||||||
|
'Insulation',
|
||||||
|
'Hanging',
|
||||||
|
'Scarp & Paper',
|
||||||
|
'Taping',
|
||||||
|
'Sand & Mask',
|
||||||
|
'WT & Cleanup',
|
||||||
|
'Grid & Tile'
|
||||||
|
];
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ locals, url }) => {
|
export const GET: RequestHandler = async ({ locals, url }) => {
|
||||||
const search = url.searchParams.get('search')?.trim() ?? '';
|
const search = url.searchParams.get('search')?.trim() ?? '';
|
||||||
const typeFilter = url.searchParams.get('type')?.trim() ?? '';
|
const typeFilter = url.searchParams.get('type')?.trim() ?? '';
|
||||||
|
const scopeFilter = url.searchParams.get('scope')?.trim() ?? '';
|
||||||
const page = Math.max(1, parseInt(url.searchParams.get('page') ?? '1', 10));
|
const page = Math.max(1, parseInt(url.searchParams.get('page') ?? '1', 10));
|
||||||
const perPage = Math.min(500, Math.max(1, parseInt(url.searchParams.get('perPage') ?? '200', 10)));
|
const perPage = Math.min(500, Math.max(1, parseInt(url.searchParams.get('perPage') ?? '200', 10)));
|
||||||
|
|
||||||
@@ -30,7 +46,11 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
typeFilter === 'Employee' || typeFilter === 'Sub-Contractor'
|
typeFilter === 'Employee' || typeFilter === 'Sub-Contractor'
|
||||||
? `(Type = "${escapeFilterValue(typeFilter)}") && `
|
? `(Type = "${escapeFilterValue(typeFilter)}") && `
|
||||||
: '';
|
: '';
|
||||||
let filter = `${typeClause}delete != true`;
|
const scopeClause =
|
||||||
|
scopeFilter.length > 0 && SCOPE_OPTIONS.includes(scopeFilter)
|
||||||
|
? `(Scopes ?~ "${escapeFilterValue(scopeFilter)}") && `
|
||||||
|
: '';
|
||||||
|
let filter = `${typeClause}${scopeClause}delete != true`;
|
||||||
if (search.length > 0) {
|
if (search.length > 0) {
|
||||||
const escaped = escapeFilterValue(search);
|
const escaped = escapeFilterValue(search);
|
||||||
const searchFilter = `(${[
|
const searchFilter = `(${[
|
||||||
@@ -41,7 +61,7 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
`Voxer_ID ~ "${escaped}"`,
|
`Voxer_ID ~ "${escaped}"`,
|
||||||
`Status ~ "${escaped}"`
|
`Status ~ "${escaped}"`
|
||||||
].join(' || ')})`;
|
].join(' || ')})`;
|
||||||
filter = `${typeClause}${searchFilter} && delete != true`;
|
filter = `${typeClause}${scopeClause}${searchFilter} && delete != true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { items, totalItems } = await locals.pb
|
const { items, totalItems } = await locals.pb
|
||||||
@@ -64,6 +84,7 @@ export const POST: RequestHandler = async ({ request, locals }) => {
|
|||||||
Email_Address: body.Email_Address ?? '',
|
Email_Address: body.Email_Address ?? '',
|
||||||
Voxer_ID: body.Voxer_ID ?? '',
|
Voxer_ID: body.Voxer_ID ?? '',
|
||||||
Status: body.Status ?? '',
|
Status: body.Status ?? '',
|
||||||
|
Scopes: Array.isArray(body.Scopes) ? body.Scopes : undefined,
|
||||||
delete: false,
|
delete: false,
|
||||||
...(locals.user?.id && { entered_by: locals.user.id })
|
...(locals.user?.id && { entered_by: locals.user.id })
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export const GET: RequestHandler = async ({ params, locals }) => {
|
|||||||
export const PATCH: RequestHandler = async ({ params, request, locals }) => {
|
export const PATCH: RequestHandler = async ({ params, request, locals }) => {
|
||||||
const id = params.id;
|
const id = params.id;
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const record = await locals.pb.collection(COLLECTION).update<EmployeeRecord>(id, {
|
|
||||||
|
const payload: Record<string, unknown> = {
|
||||||
...(body.First_Name !== undefined && { First_Name: body.First_Name }),
|
...(body.First_Name !== undefined && { First_Name: body.First_Name }),
|
||||||
...(body.Last_Name !== undefined && { Last_Name: body.Last_Name }),
|
...(body.Last_Name !== undefined && { Last_Name: body.Last_Name }),
|
||||||
...(body.Type !== undefined && { Type: body.Type }),
|
...(body.Type !== undefined && { Type: body.Type }),
|
||||||
@@ -37,8 +38,28 @@ export const PATCH: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
...(body.Status !== undefined && { Status: body.Status }),
|
...(body.Status !== undefined && { Status: body.Status }),
|
||||||
...(body.Notes !== undefined && { Notes: body.Notes }),
|
...(body.Notes !== undefined && { Notes: body.Notes }),
|
||||||
...(locals.user?.id && { updated_by: locals.user.id })
|
...(locals.user?.id && { updated_by: locals.user.id })
|
||||||
});
|
};
|
||||||
return json({ employee: record });
|
|
||||||
|
if (body.Scopes !== undefined) {
|
||||||
|
payload.Scopes = Array.isArray(body.Scopes) ? body.Scopes : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const record = await locals.pb.collection(COLLECTION).update<EmployeeRecord>(id, payload);
|
||||||
|
return json({ employee: record });
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const status = e && typeof e === 'object' && 'status' in e ? (e as { status: number }).status : 500;
|
||||||
|
const response = e && typeof e === 'object' && 'response' in e ? (e as { response: unknown }).response : null;
|
||||||
|
const message =
|
||||||
|
response && typeof response === 'object' && response !== null && 'message' in response
|
||||||
|
? (response as { message: string }).message
|
||||||
|
: 'Failed to update record';
|
||||||
|
const data =
|
||||||
|
response && typeof response === 'object' && response !== null && 'data' in response
|
||||||
|
? (response as { data: unknown }).data
|
||||||
|
: undefined;
|
||||||
|
return json({ error: message, data }, { status });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DELETE: RequestHandler = async ({ params, locals }) => {
|
export const DELETE: RequestHandler = async ({ params, locals }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user