fix for location and patient list

location :
tambah rule loccode max 6
fix handlesave if result.status success

patient list :
fix emailaddress1&2 kosong masih trigger rule
add rule for phone
change deceased to type toggle isDead
fix rule timeOfDeath
fix derived isDeathDateDisabled
This commit is contained in:
faiztyanirh 2026-04-13 13:42:46 +07:00
parent 3ebea51ee7
commit ffb57539e8
9 changed files with 68 additions and 36 deletions

View File

@ -28,7 +28,7 @@ export function usePatientForm(formState, patientSchema) {
),
EmailAddress1: patientSchema.shape.EmailAddress1.refine(
async (value) => {
if (!value) return false;
if (!value) return true;
const res = await fetch(`${API.BASE_URL}${API.CHECK}?EmailAddress1=${value}`);
const { status, data } = await res.json();
return status === "success" && data === false ? false : true;
@ -37,13 +37,22 @@ export function usePatientForm(formState, patientSchema) {
),
EmailAddress2: patientSchema.shape.EmailAddress2.refine(
async (value) => {
if (!value) return false;
if (!value) return true;
const res = await fetch(`${API.BASE_URL}${API.CHECK}?EmailAddress1=${value}`);
const { status, data } = await res.json();
return status === "success" && data === false ? false : true;
},
{ message: "Email address already used" }
),
Phone: patientSchema.shape.Phone.refine(
async (value) => {
if (!value) return true;
const res = await fetch(`${API.BASE_URL}${API.CHECK}?Phone=${value}`);
const { status, data } = await res.json();
return status === "success" && data === false ? false : true;
},
{ message: "Phone number already used" }
),
});
const partial = asyncSchema.pick({ [field]: true });

View File

@ -113,10 +113,10 @@
tempDetailContact,
});
console.log(payload)
// const result = await formState.save(masterDetail.mode);
const result = await formState.save(masterDetail.mode);
// toast('Contact Created!');
// masterDetail?.exitForm(true);
toast('Contact Created!');
masterDetail?.exitForm(true);
}
const primaryAction = $derived({

View File

@ -3,7 +3,7 @@ import EraserIcon from "@lucide/svelte/icons/eraser";
import { z } from "zod";
export const locationSchema = z.object({
LocCode: z.string().min(1, "Required"),
LocCode: z.string().min(1, "Required").max(6, "Max 6 chars"),
LocFull: z.string().min(1, "Required"),
Email: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
Phone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),

View File

@ -26,8 +26,14 @@
async function handleSave() {
const result = await formState.save(masterDetail.mode);
toast('Location Created!');
masterDetail?.exitForm(true);
if (result.status === 'success') {
toast('Location Created!');
masterDetail?.exitForm(true);
} else {
console.error('Failed to save location');
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save location';
toast.error(errorMessages)
}
}
const primaryAction = $derived({

View File

@ -63,7 +63,6 @@
}
function handleInsertDetail() {
console.log('object');
const row = {
id: ++idCounter,
...snapshotForm()

View File

@ -79,7 +79,7 @@ export const detailSections = [
},
{ key: "isDeadLabel", label: "Deceased" },
{ key: "CreateDate", label: "Create Date", isUTCDate: true },
{ key: "DelDate", label: "Disabled Date" },
{ key: "DelDate", label: "Delete Date" },
{ key: "TimeOfDeath", label: "Time of Death", isUTCDate: true },
]
},

View File

@ -12,12 +12,13 @@ export const patientSchema = z.object({
),
EmailAddress1: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
EmailAddress2: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
Phone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
Phone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers").optional(),
MobilePhone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
TimeOfDeath: z.string().optional().refine(
(date) => new Date(date) <= new Date(),
"Cannot exceed today's date"
),
ZIP: z.string().max(7, "Max 7 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
});
export const patientInitialForm = {
@ -55,7 +56,7 @@ export const patientInitialForm = {
Phone: "",
EmailAddress2: "",
MobilePhone: "",
DeathIndicator: "",
isDead: "",
TimeOfDeath: "",
LinkTo: [],
PatCom: "",
@ -73,6 +74,7 @@ export const patientDefaultErrors = {
Phone: null,
MobilePhone: null,
TimeOfDeath: null,
ZIP: null,
};
export const patientFormFields = [
@ -236,7 +238,7 @@ export const patientFormFields = [
{
type: "group",
columns: [
{ key: "ZIP", label: "ZIP", required: false, type: "number" },
{ key: "ZIP", label: "ZIP", required: false, type: "text", validateOn: ["input"] },
{
key: "Country",
label: "Country",
@ -257,7 +259,7 @@ export const patientFormFields = [
type: "row",
columns: [
{ key: "EmailAddress1", label: "Email Address 1", required: false, type: "email", validateOn: ["input", "blur"] },
{ key: "Phone", label: "Phone", required: false, type: "text", validateOn: ["input"] },
{ key: "Phone", label: "Phone", required: false, type: "text", validateOn: ["input", "blur"] },
]
},
{
@ -273,13 +275,23 @@ export const patientFormFields = [
{
type: "group",
columns: [
// {
// key: "isDead",
// label: "Deceased",
// required: false,
// type: "select",
// optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/death_indicator`,
// defaultValue: 'N'
// },
{
key: "isDead",
label: "Deceased",
required: false,
type: "select",
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/death_indicator`,
defaultValue: 'N'
type: "toggle",
optionsToggle: [
{ value: 0, label: 'No' },
{ value: 1, label: 'Yes' }
]
},
{ key: "TimeOfDeath", label: "Time of Death", required: false, type: "datetime", validateOn: ["input"] }
]

View File

@ -68,7 +68,7 @@
disabled: formState.isSaving.current
}
];
// $inspect(formState.selectOptions)
$inspect(formState.errors)
</script>
<FormPageContainer title="Create Patient" {primaryAction} {secondaryActions} {actions}>

View File

@ -68,17 +68,18 @@
}
let isDeathDateDisabled = $derived(
formState.form.isDead !== 'Y'
formState.form.isDead !== 1 && formState.form.isDead !== "1"
);
$effect(() => {
if (isDeathDateDisabled && formState.form.TimeOfDeath) {
formState.form.TimeOfDeath = "";
formState.errors.TimeOfDeath = null;
}
});
</script>
{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey, valueKey, labelKey })}
{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, optionsToggle, validateOn, dependsOn, endpointParamKey, valueKey, labelKey })}
<div class="flex w-full flex-col gap-1.5">
<div class="flex justify-between items-center w-full">
<Label>{label}</Label>
@ -283,22 +284,27 @@
{/if}
</div>
{:else if type === "toggle"}
<div class="flex items-center w-full">
<Toggle
aria-label="Toggle discharge"
variant="outline"
class="w-full transition-all data-[state=on]:text-primary"
bind:pressed={formState.form.isDischarge}
>
{#if formState.form.isDischarge}
<XIcon class="mr-2 h-4 w-4" />
{:else}
<CheckIcon class="mr-2 h-4 w-4" />
{/if}
{formState.form.isDischarge ? "Discharged" : "Active"}
</Toggle>
</div>
{@const toggleOff = optionsToggle?.[0] ?? { value: false, label: 'Off' }}
{@const toggleOn = optionsToggle?.[1] ?? { value: true, label: 'On' }}
{@const isOn = String(formState.form[key]) === String(toggleOn.value)}
<div class="flex items-center w-full">
<Toggle
aria-label="Toggle"
variant="outline"
class="w-full transition-all data-[state=on]:text-primary"
pressed={isOn}
onPressedChange={(pressed) => {
formState.form[key] = pressed ? toggleOn.value : toggleOff.value;
}}
>
{#if isOn}
<CheckIcon class="mr-2 h-4 w-4" />
{:else}
<XIcon class="mr-2 h-4 w-4" />
{/if}
{isOn ? toggleOn.label : toggleOff.label}
</Toggle>
</div>
{:else}
<Input
type="text"