Add employee type management and filtering functionality
- Introduced a new `Type` field in the `EmployeeRecord` interface to categorize employees as 'Employee', 'Sub-Contractor', etc. - Updated forms and tables in Svelte to include type selection and display. - Enhanced API endpoints to support filtering employees by type in search queries. - Implemented type filtering buttons and a dropdown in the employee management interface for improved user experience.
This commit is contained in:
@@ -64,6 +64,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:head>
|
<svelte:head>
|
||||||
|
<title>HRM</title>
|
||||||
<link rel="icon" href={favicon} />
|
<link rel="icon" href={favicon} />
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ export interface EmployeeRecord {
|
|||||||
id: string;
|
id: string;
|
||||||
First_Name: string;
|
First_Name: string;
|
||||||
Last_Name: string;
|
Last_Name: string;
|
||||||
|
Type?: string;
|
||||||
Phone_Number: string;
|
Phone_Number: string;
|
||||||
Email_Address: string;
|
Email_Address: string;
|
||||||
Voxer_ID: string;
|
Voxer_ID: string;
|
||||||
|
|||||||
+99
-6
@@ -7,6 +7,8 @@
|
|||||||
import { Button } from '$lib/components/ui/button/index.js';
|
import { Button } from '$lib/components/ui/button/index.js';
|
||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
|
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||||
|
import * as Select from '$lib/components/ui/select/index.js';
|
||||||
import type { EmployeeRecord } from './+page.server';
|
import type { EmployeeRecord } from './+page.server';
|
||||||
import EllipsisVerticalIcon from '@lucide/svelte/icons/ellipsis-vertical';
|
import EllipsisVerticalIcon from '@lucide/svelte/icons/ellipsis-vertical';
|
||||||
import PencilIcon from '@lucide/svelte/icons/pencil';
|
import PencilIcon from '@lucide/svelte/icons/pencil';
|
||||||
@@ -20,19 +22,37 @@
|
|||||||
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);
|
||||||
|
type TypeFilterValue = 'all' | 'Employee' | 'Sub-Contractor';
|
||||||
|
let typeFilter = $state<TypeFilterValue>('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
|
||||||
let searchDebounceId: ReturnType<typeof setTimeout> | null = null;
|
let searchDebounceId: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
function typeParam(): string {
|
||||||
|
return typeFilter === 'all' ? '' : `&type=${encodeURIComponent(typeFilter)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTypeFilter(value: TypeFilterValue) {
|
||||||
|
typeFilter = value;
|
||||||
|
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);
|
||||||
let editingEmployee = $state<EmployeeRecord | null>(null);
|
let editingEmployee = $state<EmployeeRecord | null>(null);
|
||||||
let deletingEmployee = $state<EmployeeRecord | null>(null);
|
let deletingEmployee = $state<EmployeeRecord | null>(null);
|
||||||
|
|
||||||
|
const TYPE_OPTIONS = ['Employee', 'Sub-Contractor', 'Former-Employee', 'Applicant', 'Blacklisted'];
|
||||||
let formFirst = $state('');
|
let formFirst = $state('');
|
||||||
let formLast = $state('');
|
let formLast = $state('');
|
||||||
|
let formType = $state('');
|
||||||
let formPhone = $state('');
|
let formPhone = $state('');
|
||||||
let formEmail = $state('');
|
let formEmail = $state('');
|
||||||
let formVoxer = $state('');
|
let formVoxer = $state('');
|
||||||
@@ -45,7 +65,7 @@
|
|||||||
async function loadEmployees() {
|
async function loadEmployees() {
|
||||||
searchMode = false;
|
searchMode = false;
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/employees?page=1&perPage=200');
|
const res = await fetch(`/api/employees?page=1&perPage=200${typeParam()}`);
|
||||||
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 ?? [];
|
||||||
@@ -69,7 +89,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())}`
|
`/api/employees?page=1&perPage=500&search=${encodeURIComponent(query.trim())}${typeParam()}`
|
||||||
);
|
);
|
||||||
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();
|
||||||
@@ -104,7 +124,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`);
|
const res = await fetch(`/api/employees?page=${nextPage}&perPage=100${typeParam()}`);
|
||||||
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 ?? [];
|
||||||
@@ -120,6 +140,7 @@
|
|||||||
function resetForm() {
|
function resetForm() {
|
||||||
formFirst = '';
|
formFirst = '';
|
||||||
formLast = '';
|
formLast = '';
|
||||||
|
formType = '';
|
||||||
formPhone = '';
|
formPhone = '';
|
||||||
formEmail = '';
|
formEmail = '';
|
||||||
formVoxer = '';
|
formVoxer = '';
|
||||||
@@ -137,6 +158,7 @@
|
|||||||
editingEmployee = emp;
|
editingEmployee = emp;
|
||||||
formFirst = emp.First_Name ?? '';
|
formFirst = emp.First_Name ?? '';
|
||||||
formLast = emp.Last_Name ?? '';
|
formLast = emp.Last_Name ?? '';
|
||||||
|
formType = emp.Type ?? '';
|
||||||
formPhone = emp.Phone_Number ?? '';
|
formPhone = emp.Phone_Number ?? '';
|
||||||
formEmail = emp.Email_Address ?? '';
|
formEmail = emp.Email_Address ?? '';
|
||||||
formVoxer = emp.Voxer_ID ?? '';
|
formVoxer = emp.Voxer_ID ?? '';
|
||||||
@@ -161,6 +183,7 @@
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
First_Name: formFirst,
|
First_Name: formFirst,
|
||||||
Last_Name: formLast,
|
Last_Name: formLast,
|
||||||
|
Type: formType || undefined,
|
||||||
Phone_Number: formPhone,
|
Phone_Number: formPhone,
|
||||||
Email_Address: formEmail,
|
Email_Address: formEmail,
|
||||||
Voxer_ID: formVoxer,
|
Voxer_ID: formVoxer,
|
||||||
@@ -191,6 +214,7 @@
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
First_Name: formFirst,
|
First_Name: formFirst,
|
||||||
Last_Name: formLast,
|
Last_Name: formLast,
|
||||||
|
Type: formType || undefined,
|
||||||
Phone_Number: formPhone,
|
Phone_Number: formPhone,
|
||||||
Email_Address: formEmail,
|
Email_Address: formEmail,
|
||||||
Voxer_ID: formVoxer,
|
Voxer_ID: formVoxer,
|
||||||
@@ -254,6 +278,39 @@
|
|||||||
|
|
||||||
<main class="flex flex-col gap-6 p-6 md:p-8">
|
<main class="flex flex-col gap-6 p-6 md:p-8">
|
||||||
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:gap-4">
|
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:gap-4">
|
||||||
|
<div
|
||||||
|
class="flex shrink-0 rounded-md border border-input bg-transparent p-0.5 shadow-xs"
|
||||||
|
role="group"
|
||||||
|
aria-label="Filter by type"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={typeFilter === 'all' ? 'secondary' : 'ghost'}
|
||||||
|
size="sm"
|
||||||
|
class="rounded-[5px] px-3"
|
||||||
|
onclick={() => setTypeFilter('all')}
|
||||||
|
>
|
||||||
|
All
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={typeFilter === 'Employee' ? 'secondary' : 'ghost'}
|
||||||
|
size="sm"
|
||||||
|
class="rounded-[5px] px-3"
|
||||||
|
onclick={() => setTypeFilter('Employee')}
|
||||||
|
>
|
||||||
|
Employee
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={typeFilter === 'Sub-Contractor' ? 'secondary' : 'ghost'}
|
||||||
|
size="sm"
|
||||||
|
class="rounded-[5px] px-3"
|
||||||
|
onclick={() => setTypeFilter('Sub-Contractor')}
|
||||||
|
>
|
||||||
|
Sub-Contractor
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<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"
|
||||||
@@ -278,6 +335,7 @@
|
|||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Head>First Name</Table.Head>
|
<Table.Head>First Name</Table.Head>
|
||||||
<Table.Head>Last Name</Table.Head>
|
<Table.Head>Last Name</Table.Head>
|
||||||
|
<Table.Head>Type</Table.Head>
|
||||||
<Table.Head>Status</Table.Head>
|
<Table.Head>Status</Table.Head>
|
||||||
<Table.Head>Phone</Table.Head>
|
<Table.Head>Phone</Table.Head>
|
||||||
<Table.Head>Email</Table.Head>
|
<Table.Head>Email</Table.Head>
|
||||||
@@ -288,13 +346,13 @@
|
|||||||
<Table.Body>
|
<Table.Body>
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell colspan={7} class="text-muted-foreground py-8 text-center">
|
<Table.Cell colspan={8} 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={7} class="text-destructive py-8 text-center">
|
<Table.Cell colspan={8} class="text-destructive py-8 text-center">
|
||||||
{error}
|
{error}
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
</Table.Row>
|
</Table.Row>
|
||||||
@@ -309,6 +367,13 @@
|
|||||||
>
|
>
|
||||||
<Table.Cell>{employee.First_Name}</Table.Cell>
|
<Table.Cell>{employee.First_Name}</Table.Cell>
|
||||||
<Table.Cell>{employee.Last_Name}</Table.Cell>
|
<Table.Cell>{employee.Last_Name}</Table.Cell>
|
||||||
|
<Table.Cell>
|
||||||
|
{#if employee.Type}
|
||||||
|
<Badge variant="secondary">{employee.Type}</Badge>
|
||||||
|
{:else}
|
||||||
|
<span class="text-muted-foreground">—</span>
|
||||||
|
{/if}
|
||||||
|
</Table.Cell>
|
||||||
<Table.Cell>{employee.Status}</Table.Cell>
|
<Table.Cell>{employee.Status}</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>
|
||||||
@@ -349,7 +414,7 @@
|
|||||||
</Table.Row>
|
</Table.Row>
|
||||||
{:else}
|
{:else}
|
||||||
<Table.Row>
|
<Table.Row>
|
||||||
<Table.Cell colspan={7} class="text-muted-foreground text-center">
|
<Table.Cell colspan={8} class="text-muted-foreground text-center">
|
||||||
{searchQuery.trim()
|
{searchQuery.trim()
|
||||||
? 'No matching employees.'
|
? 'No matching employees.'
|
||||||
: 'No employee records found.'}
|
: 'No employee records found.'}
|
||||||
@@ -397,6 +462,20 @@
|
|||||||
<Input id="create-last" bind:value={formLast} required />
|
<Input id="create-last" bind:value={formLast} required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="create-type">Type</Label>
|
||||||
|
<Select.Root type="single" bind:value={formType} disabled={formSaving}>
|
||||||
|
<Select.Trigger class="w-full" id="create-type">
|
||||||
|
{formType || 'Select type…'}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Item value="" label="(none)">(none)</Select.Item>
|
||||||
|
{#each TYPE_OPTIONS as opt}
|
||||||
|
<Select.Item value={opt} label={opt}>{opt}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Label for="create-phone">Phone</Label>
|
<Label for="create-phone">Phone</Label>
|
||||||
<Input id="create-phone" bind:value={formPhone} />
|
<Input id="create-phone" bind:value={formPhone} />
|
||||||
@@ -452,6 +531,20 @@
|
|||||||
<Input id="edit-last" bind:value={formLast} required />
|
<Input id="edit-last" bind:value={formLast} required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<Label for="edit-type">Type</Label>
|
||||||
|
<Select.Root type="single" bind:value={formType} disabled={formSaving}>
|
||||||
|
<Select.Trigger class="w-full" id="edit-type">
|
||||||
|
{formType || 'Select type…'}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Item value="" label="(none)">(none)</Select.Item>
|
||||||
|
{#each TYPE_OPTIONS as opt}
|
||||||
|
<Select.Item value={opt} label={opt}>{opt}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<Label for="edit-phone">Phone</Label>
|
<Label for="edit-phone">Phone</Label>
|
||||||
<Input id="edit-phone" bind:value={formPhone} />
|
<Input id="edit-phone" bind:value={formPhone} />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ export interface EmployeeRecord {
|
|||||||
id: string;
|
id: string;
|
||||||
First_Name: string;
|
First_Name: string;
|
||||||
Last_Name: string;
|
Last_Name: string;
|
||||||
|
Type?: string;
|
||||||
Phone_Number: string;
|
Phone_Number: string;
|
||||||
Email_Address: string;
|
Email_Address: string;
|
||||||
Voxer_ID: string;
|
Voxer_ID: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { Label } from '$lib/components/ui/label/index.js';
|
import { Label } from '$lib/components/ui/label/index.js';
|
||||||
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
import * as Dialog from '$lib/components/ui/dialog/index.js';
|
||||||
import * as Card from '$lib/components/ui/card/index.js';
|
import * as Card from '$lib/components/ui/card/index.js';
|
||||||
import { badgeVariants } from '$lib/components/ui/badge/index.js';
|
import { Badge, badgeVariants } from '$lib/components/ui/badge/index.js';
|
||||||
import type {
|
import type {
|
||||||
EmployeeRecord,
|
EmployeeRecord,
|
||||||
EmployeeReportRecord,
|
EmployeeReportRecord,
|
||||||
@@ -270,8 +270,11 @@
|
|||||||
<Button variant="ghost" size="icon" href="/" class="shrink-0">
|
<Button variant="ghost" size="icon" href="/" class="shrink-0">
|
||||||
<ArrowLeftIcon class="size-4" />
|
<ArrowLeftIcon class="size-4" />
|
||||||
</Button>
|
</Button>
|
||||||
<h1 class="text-2xl font-semibold tracking-tight shrink-0">
|
<h1 class="text-2xl font-semibold tracking-tight shrink-0 flex items-center gap-2 flex-wrap">
|
||||||
{employee?.First_Name} {employee?.Last_Name}
|
{employee?.First_Name} {employee?.Last_Name}
|
||||||
|
{#if employee?.Type}
|
||||||
|
<Badge variant="secondary">{employee.Type}</Badge>
|
||||||
|
{/if}
|
||||||
</h1>
|
</h1>
|
||||||
{#if employee}
|
{#if employee}
|
||||||
<div class="flex items-center gap-2 ml-auto">
|
<div class="flex items-center gap-2 ml-auto">
|
||||||
@@ -325,6 +328,38 @@
|
|||||||
<dd>{employee.Last_Name || '—'}</dd>
|
<dd>{employee.Last_Name || '—'}</dd>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<Label for="emp-type" class="text-muted-foreground text-sm">Type</Label>
|
||||||
|
{#if editMode}
|
||||||
|
<Select.Root
|
||||||
|
type="single"
|
||||||
|
value={employee.Type ?? ''}
|
||||||
|
onValueChange={(v) => {
|
||||||
|
employee = { ...employee!, Type: v ?? undefined };
|
||||||
|
if (v !== undefined) patchField('Type', v ?? '');
|
||||||
|
}}
|
||||||
|
disabled={patchSaving}
|
||||||
|
>
|
||||||
|
<Select.Trigger class="w-full" id="emp-type">
|
||||||
|
{employee.Type || 'Select type…'}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<Select.Item value="" label="(none)">(none)</Select.Item>
|
||||||
|
{#each ['Employee', 'Sub-Contractor', 'Former-Employee', 'Applicant', 'Blacklisted'] as opt}
|
||||||
|
<Select.Item value={opt} label={opt}>{opt}</Select.Item>
|
||||||
|
{/each}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
{:else}
|
||||||
|
<dd>
|
||||||
|
{#if employee.Type}
|
||||||
|
<Badge variant="secondary">{employee.Type}</Badge>
|
||||||
|
{:else}
|
||||||
|
—
|
||||||
|
{/if}
|
||||||
|
</dd>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
<div class="flex flex-col gap-1">
|
<div class="flex flex-col gap-1">
|
||||||
<Label for="emp-status" class="text-muted-foreground text-sm">Status</Label>
|
<Label for="emp-status" class="text-muted-foreground text-sm">Status</Label>
|
||||||
{#if editMode}
|
{#if editMode}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export interface EmployeeRecord {
|
|||||||
id: string;
|
id: string;
|
||||||
First_Name: string;
|
First_Name: string;
|
||||||
Last_Name: string;
|
Last_Name: string;
|
||||||
|
Type?: string;
|
||||||
Phone_Number: string;
|
Phone_Number: string;
|
||||||
Email_Address: string;
|
Email_Address: string;
|
||||||
Voxer_ID: string;
|
Voxer_ID: string;
|
||||||
@@ -21,21 +22,26 @@ function escapeFilterValue(s: string): string {
|
|||||||
|
|
||||||
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 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)));
|
||||||
|
|
||||||
let filter = 'delete != true';
|
const typeClause =
|
||||||
|
typeFilter === 'Employee' || typeFilter === 'Sub-Contractor'
|
||||||
|
? `(Type = "${escapeFilterValue(typeFilter)}") && `
|
||||||
|
: '';
|
||||||
|
let filter = `${typeClause}delete != true`;
|
||||||
if (search.length > 0) {
|
if (search.length > 0) {
|
||||||
const escaped = escapeFilterValue(search);
|
const escaped = escapeFilterValue(search);
|
||||||
// Search across main text fields (PocketBase ~ is "contains")
|
const searchFilter = `(${[
|
||||||
filter = `(${[
|
|
||||||
`First_Name ~ "${escaped}"`,
|
`First_Name ~ "${escaped}"`,
|
||||||
`Last_Name ~ "${escaped}"`,
|
`Last_Name ~ "${escaped}"`,
|
||||||
`Email_Address ~ "${escaped}"`,
|
`Email_Address ~ "${escaped}"`,
|
||||||
`Phone_Number ~ "${escaped}"`,
|
`Phone_Number ~ "${escaped}"`,
|
||||||
`Voxer_ID ~ "${escaped}"`,
|
`Voxer_ID ~ "${escaped}"`,
|
||||||
`Status ~ "${escaped}"`
|
`Status ~ "${escaped}"`
|
||||||
].join(' || ')}) && delete != true`;
|
].join(' || ')})`;
|
||||||
|
filter = `${typeClause}${searchFilter} && delete != true`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { items, totalItems } = await locals.pb
|
const { items, totalItems } = await locals.pb
|
||||||
@@ -50,9 +56,10 @@ export const GET: RequestHandler = async ({ locals, url }) => {
|
|||||||
|
|
||||||
export const POST: RequestHandler = async ({ request, locals }) => {
|
export const POST: RequestHandler = async ({ request, locals }) => {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const record = await locals.pb.collection(COLLECTION).create<EmployeeRecord>({
|
const record = await locals.pb.collection(COLLECTION).create<EmployeeRecord>({
|
||||||
First_Name: body.First_Name ?? '',
|
First_Name: body.First_Name ?? '',
|
||||||
Last_Name: body.Last_Name ?? '',
|
Last_Name: body.Last_Name ?? '',
|
||||||
|
Type: body.Type ?? undefined,
|
||||||
Phone_Number: body.Phone_Number ?? '',
|
Phone_Number: body.Phone_Number ?? '',
|
||||||
Email_Address: body.Email_Address ?? '',
|
Email_Address: body.Email_Address ?? '',
|
||||||
Voxer_ID: body.Voxer_ID ?? '',
|
Voxer_ID: body.Voxer_ID ?? '',
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export const PATCH: RequestHandler = async ({ params, request, locals }) => {
|
|||||||
const record = await locals.pb.collection(COLLECTION).update<EmployeeRecord>(id, {
|
const record = await locals.pb.collection(COLLECTION).update<EmployeeRecord>(id, {
|
||||||
...(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.Phone_Number !== undefined && { Phone_Number: body.Phone_Number }),
|
...(body.Phone_Number !== undefined && { Phone_Number: body.Phone_Number }),
|
||||||
...(body.Email_Address !== undefined && { Email_Address: body.Email_Address }),
|
...(body.Email_Address !== undefined && { Email_Address: body.Email_Address }),
|
||||||
...(body.Voxer_ID !== undefined && { Voxer_ID: body.Voxer_ID }),
|
...(body.Voxer_ID !== undefined && { Voxer_ID: body.Voxer_ID }),
|
||||||
|
|||||||
Reference in New Issue
Block a user