From f12c901a97026c4817a0a4f9ad3c69db2652d9ac Mon Sep 17 00:00:00 2001 From: grabowski Date: Wed, 15 Apr 2026 10:01:09 +0700 Subject: [PATCH] Show personal/address/emergency on employee detail and in edit modal Detail page now has three new cards beneath the main employee block: - Personal: DOB (with computed age), gender, nationality, marital status - Address: combined one-line address plus a labelled grid for the Thai-specific subdistrict/district/province/postal code parts - Emergency Contact: name, phone, relationship Edit modal extends with matching sections so HR/admin can update all 14 new fields. updateEmployee server action passes the new fields through to the employees table. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../employees/[id]/+page.server.ts | 15 ++ .../[companyId]/employees/[id]/+page.svelte | 203 ++++++++++++++++++ 2 files changed, 218 insertions(+) diff --git a/src/routes/(app)/companies/[companyId]/employees/[id]/+page.server.ts b/src/routes/(app)/companies/[companyId]/employees/[id]/+page.server.ts index 39d89df..00dfa55 100644 --- a/src/routes/(app)/companies/[companyId]/employees/[id]/+page.server.ts +++ b/src/routes/(app)/companies/[companyId]/employees/[id]/+page.server.ts @@ -181,6 +181,21 @@ export const actions: Actions = { taxId: formData.get('taxId')?.toString().trim() || null, bankName: formData.get('bankName')?.toString().trim() || null, bankAccount: formData.get('bankAccount')?.toString().trim() || null, + dateOfBirth: formData.get('dateOfBirth')?.toString().trim() || null, + gender: formData.get('gender')?.toString().trim() || null, + nationality: formData.get('nationality')?.toString().trim() || null, + maritalStatus: formData.get('maritalStatus')?.toString().trim() || null, + addressLine1: formData.get('addressLine1')?.toString().trim() || null, + addressLine2: formData.get('addressLine2')?.toString().trim() || null, + subdistrict: formData.get('subdistrict')?.toString().trim() || null, + district: formData.get('district')?.toString().trim() || null, + province: formData.get('province')?.toString().trim() || null, + postalCode: formData.get('postalCode')?.toString().trim() || null, + country: formData.get('country')?.toString().trim() || null, + emergencyContactName: formData.get('emergencyContactName')?.toString().trim() || null, + emergencyContactPhone: formData.get('emergencyContactPhone')?.toString().trim() || null, + emergencyContactRelationship: + formData.get('emergencyContactRelationship')?.toString().trim() || null, updatedAt: new Date() }) .where(eq(employees.id, params.id)); diff --git a/src/routes/(app)/companies/[companyId]/employees/[id]/+page.svelte b/src/routes/(app)/companies/[companyId]/employees/[id]/+page.svelte index ba10f09..9aa0134 100644 --- a/src/routes/(app)/companies/[companyId]/employees/[id]/+page.svelte +++ b/src/routes/(app)/companies/[companyId]/employees/[id]/+page.svelte @@ -19,6 +19,43 @@ const fullName = $derived( emp.displayName ?? `${emp.firstName} ${emp.lastName}` ); + + const GENDER_LABELS: Record = { + male: 'Male', + female: 'Female', + other: 'Other', + prefer_not_to_say: 'Prefer not to say' + }; + const MARITAL_LABELS: Record = { + single: 'Single', + married: 'Married', + divorced: 'Divorced', + widowed: 'Widowed', + other: 'Other' + }; + + function ageFromDob(dob: string | null | undefined): number | null { + if (!dob) return null; + const d = new Date(dob); + if (isNaN(d.getTime())) return null; + const now = new Date(); + let age = now.getFullYear() - d.getFullYear(); + const m = now.getMonth() - d.getMonth(); + if (m < 0 || (m === 0 && now.getDate() < d.getDate())) age--; + return age; + } + + const fullAddress = $derived( + [ + emp.addressLine1, + emp.addressLine2, + [emp.subdistrict, emp.district].filter(Boolean).join(', '), + [emp.province, emp.postalCode].filter(Boolean).join(' '), + emp.country + ] + .filter((s): s is string => !!s && s.trim().length > 0) + .join(' • ') + ); @@ -146,6 +183,68 @@ + +
+

Personal

+
+
+

Date of Birth

+

+ {#if emp.dateOfBirth} + {formatDate(emp.dateOfBirth)} ({ageFromDob(emp.dateOfBirth)} y) + {:else}—{/if} +

+
+
+

Gender

+

{emp.gender ? GENDER_LABELS[emp.gender] ?? emp.gender : '—'}

+
+
+

Nationality

+

{emp.nationality ?? '—'}

+
+
+

Marital Status

+

{emp.maritalStatus ? MARITAL_LABELS[emp.maritalStatus] ?? emp.maritalStatus : '—'}

+
+
+
+ + +
+

Address

+ {#if fullAddress} +

{fullAddress}

+
+ {#if emp.subdistrict}
Subdistrict{emp.subdistrict}
{/if} + {#if emp.district}
District{emp.district}
{/if} + {#if emp.province}
Province{emp.province}
{/if} + {#if emp.postalCode}
Postal Code{emp.postalCode}
{/if} +
+ {:else} +

+ {/if} +
+ + +
+

Emergency Contact

+
+
+

Name

+

{emp.emergencyContactName ?? '—'}

+
+
+

Phone

+

{emp.emergencyContactPhone ?? '—'}

+
+
+

Relationship

+

{emp.emergencyContactRelationship ?? '—'}

+
+
+
+
@@ -540,6 +639,110 @@
+ +

Personal

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +

Address

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +

Emergency Contact

+
+
+ + +
+
+ + +
+
+ + +
+
+