mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-23 09:39:27 +07:00
continue dict test refnum
This commit is contained in:
parent
ea43e44809
commit
580328ad6b
@ -550,6 +550,7 @@ export function getTestFormActions(handlers) {
|
||||
export function buildTestPayload({
|
||||
mainForm,
|
||||
calForm,
|
||||
refForm,
|
||||
testType
|
||||
}) {
|
||||
let payload = {
|
||||
@ -561,7 +562,12 @@ export function buildTestPayload({
|
||||
...payload,
|
||||
...calForm
|
||||
};
|
||||
}
|
||||
} else if (testType === 'TEST' || 'PARAM') {
|
||||
payload = {
|
||||
...payload,
|
||||
...refForm
|
||||
}
|
||||
}
|
||||
|
||||
return cleanEmptyStrings(payload);
|
||||
}
|
||||
@ -48,10 +48,12 @@
|
||||
async function handleSave() {
|
||||
const mainForm = masterDetail.formState.form;
|
||||
const calForm = calFormState.form;
|
||||
const refForm = refNumState.form;
|
||||
|
||||
const payload = buildTestPayload({
|
||||
mainForm,
|
||||
calForm,
|
||||
refForm,
|
||||
testType: mainForm.TestType
|
||||
});
|
||||
console.log(payload);
|
||||
|
||||
@ -27,7 +27,6 @@
|
||||
try {
|
||||
const res = await fetch(`${API.BASE_URL}${API.TEST}`);
|
||||
const data = await res.json();
|
||||
console.log(data);
|
||||
|
||||
options = data.data.map((item) => ({
|
||||
value: item.TestSiteCode,
|
||||
|
||||
@ -4,9 +4,22 @@
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||
import { buildAgeText } from "$lib/utils/ageUtils";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
let props = $props();
|
||||
|
||||
let tempNumeric = $state([]);
|
||||
let editingId = $state(null);
|
||||
let idCounter = $state(0);
|
||||
$inspect(tempNumeric)
|
||||
let joinFields = $state({
|
||||
AgeStart: { DD: "", MM: "", YY: "" },
|
||||
AgeEnd: { DD: "", MM: "", YY: "" },
|
||||
});
|
||||
|
||||
let disabledSign = $derived.by(() => {
|
||||
const refType = props.refType;
|
||||
|
||||
@ -16,11 +29,133 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
function snapshotForm() {
|
||||
const f = props.refNumState.form;
|
||||
return {
|
||||
SpcType: f.SpcType ?? "",
|
||||
Sex: f.Sex ?? "",
|
||||
AgeStart: f.AgeStart ?? "",
|
||||
AgeEnd: f.AgeEnd ?? "",
|
||||
NumRefType: f.NumRefType ?? "",
|
||||
RangeType: f.RangeType ?? "",
|
||||
LowSign: f.LowSign ?? "",
|
||||
Low: f.Low ?? "",
|
||||
HighSign: f.HighSign ?? "",
|
||||
High: f.High ?? "",
|
||||
Display: f.Display ?? "",
|
||||
Flag: f.Flag ?? "",
|
||||
Interpretation: f.Interpretation ?? "",
|
||||
Notes: f.Notes ?? "",
|
||||
};
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
props.refNumState.reset?.();
|
||||
joinFields = {
|
||||
AgeStart: { DD: "", MM: "", YY: "" },
|
||||
AgeEnd: { DD: "", MM: "", YY: "" },
|
||||
};
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
const row = { id: ++idCounter, ...snapshotForm() };
|
||||
tempNumeric = [...tempNumeric, row];
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function handleEdit(row) {
|
||||
editingId = row.id;
|
||||
|
||||
const f = props.refNumState.form;
|
||||
f.SpcType = row.SpcType;
|
||||
f.Sex = row.Sex;
|
||||
f.AgeStart = row.AgeStart;
|
||||
f.AgeEnd = row.AgeEnd;
|
||||
f.NumRefType = row.NumRefType;
|
||||
f.RangeType = row.RangeType;
|
||||
f.LowSign = row.LowSign;
|
||||
f.Low = row.Low;
|
||||
f.HighSign = row.HighSign;
|
||||
f.High = row.High;
|
||||
f.Display = row.Display;
|
||||
f.Flag = row.Flag;
|
||||
f.Interpretation = row.Interpretation;
|
||||
f.Notes = row.Notes;
|
||||
}
|
||||
|
||||
function handleUpdate() {
|
||||
tempNumeric = tempNumeric.map((row) =>
|
||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||
);
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function handleCancelEdit() {
|
||||
resetForm();
|
||||
}
|
||||
|
||||
function handleRemove(id) {
|
||||
tempNumeric = tempNumeric.filter((row) => row.id !== id);
|
||||
if (editingId === id) resetForm();
|
||||
}
|
||||
|
||||
function getLabel(fieldKey, value) {
|
||||
const opts = props.refNumState.selectOptions[fieldKey] ?? [];
|
||||
const found = opts.find((o) => o.value == value);
|
||||
return found ? found.label : value;
|
||||
}
|
||||
|
||||
function getCode(fieldKey, value) {
|
||||
const opts = props.refNumState.selectOptions[fieldKey] ?? [];
|
||||
const found = opts.find((o) => o.value == value);
|
||||
return found ? found.code : value;
|
||||
}
|
||||
|
||||
const rangeTypeBadge = (type) => ({ REF: "REF", CRTC: "CRTC" }[type] ?? type);
|
||||
|
||||
const rangeDisplay = (row) => {
|
||||
if (row.NumRefType === "RANGE") return `${row.LowValue} - ${row.HighValue}`;
|
||||
if (row.NumRefType === "THOLD") return row.TholdValue;
|
||||
return "-";
|
||||
};
|
||||
|
||||
$effect(() => {
|
||||
if (props.refType) {
|
||||
props.refNumState.form.NumRefType = props.refType;
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
for (const key of ["AgeStart", "AgeEnd"]) {
|
||||
props.refNumState.form[key] =
|
||||
buildAgeText(joinFields[key]);
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
const allColumns = props.refNumFormFields.flatMap(
|
||||
(section) => section.rows.flatMap(
|
||||
(row) => row.columns ?? []
|
||||
)
|
||||
);
|
||||
|
||||
untrack(() => {
|
||||
for (const col of allColumns) {
|
||||
if (!col.optionsEndpoint) continue;
|
||||
|
||||
props.refNumState.fetchOptions?.(
|
||||
{
|
||||
key: col.key,
|
||||
optionsEndpoint: col.optionsEndpoint,
|
||||
valueKey: col.valueKey,
|
||||
labelKey: col.labelKey,
|
||||
},
|
||||
props.refNumState.form
|
||||
);
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
@ -29,54 +164,90 @@
|
||||
formState={props.refNumState}
|
||||
formFields={props.refNumFormFields}
|
||||
{disabledSign}
|
||||
bind:joinFields
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
class="cursor-pointer mt-1 ms-2"
|
||||
>
|
||||
Insert
|
||||
</Button>
|
||||
<div class="flex gap-2 mt-1 ms-2">
|
||||
{#if editingId !== null}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleUpdate}>
|
||||
Update
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEdit}>
|
||||
Cancel
|
||||
</Button>
|
||||
{:else}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsert}>
|
||||
Insert
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row class="hover:bg-transparent">
|
||||
<Table.Head class="">Specimen Type</Table.Head>
|
||||
<Table.Head class="">Sex</Table.Head>
|
||||
<Table.Head class="">Age Range</Table.Head>
|
||||
<Table.Head class="">Type</Table.Head>
|
||||
<Table.Head class="">Range/Threshold</Table.Head>
|
||||
<Table.Head class="">Flag</Table.Head>
|
||||
<Table.Head class="">Interpretation</Table.Head>
|
||||
<Table.Head class="">Notes</Table.Head>
|
||||
<Table.Head>Specimen Type</Table.Head>
|
||||
<Table.Head>Sex</Table.Head>
|
||||
<Table.Head>Age Range</Table.Head>
|
||||
<Table.Head>Type</Table.Head>
|
||||
<Table.Head>Range/Threshold</Table.Head>
|
||||
<Table.Head>Flag</Table.Head>
|
||||
<Table.Head>Interpretation</Table.Head>
|
||||
<Table.Head>Notes</Table.Head>
|
||||
<Table.Head class="w-[80px]"></Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
>
|
||||
<Table.Cell>Whole Blood</Table.Cell>
|
||||
<Table.Cell class="font-medium">Female</Table.Cell>
|
||||
<Table.Cell class="">1Y 0M 0D - 10Y 0M 0D</Table.Cell>
|
||||
<Table.Cell class="text-muted-foreground"><Badge>Range</Badge></Table.Cell>
|
||||
<Table.Cell class="font-medium">4 - 10</Table.Cell>
|
||||
<Table.Cell class="font-medium">L</Table.Cell>
|
||||
<Table.Cell class="font-medium">Interpretation</Table.Cell>
|
||||
<Table.Cell class="font-medium">Testing</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
>
|
||||
<Table.Cell>Whole Blood</Table.Cell>
|
||||
<Table.Cell class="font-medium">Male</Table.Cell>
|
||||
<Table.Cell class="">1Y 0M 0D - 10Y 0M 0D</Table.Cell>
|
||||
<Table.Cell class="text-muted-foreground"><Badge>Threshold</Badge></Table.Cell>
|
||||
<Table.Cell class="font-medium">10</Table.Cell>
|
||||
<Table.Cell class="font-medium">L</Table.Cell>
|
||||
<Table.Cell class="font-medium">Interpretation</Table.Cell>
|
||||
<Table.Cell class="font-medium">Testing</Table.Cell>
|
||||
</Table.Row>
|
||||
{#if tempNumeric.length === 0}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={9} class="text-center text-muted-foreground py-6">
|
||||
No data. Fill the form above and click Insert.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#each tempNumeric as row (row.id)}
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
>
|
||||
<Table.Cell>{row.SpcType ? getLabel("SpcType", row.SpcType) : "-"}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Sex ? getLabel("Sex", row.Sex) : "-"}</Table.Cell>
|
||||
<Table.Cell>{row.AgeStart} - {row.AgeEnd}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Badge>{rangeTypeBadge(row.RangeType)}</Badge>
|
||||
</Table.Cell>
|
||||
<!-- <Table.Cell class="font-medium">{rangeDisplay(row)}</Table.Cell> -->
|
||||
<Table.Cell class="font-medium">
|
||||
{row.LowSign ? row.LowSign : ""} {row.Low || ""} -
|
||||
{row.HighSign ? row.HighSign : ""} {row.High || ""}
|
||||
</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Flag}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Interpretation}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Notes}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleEdit(row)}
|
||||
>
|
||||
<PencilIcon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleRemove(row.id)}
|
||||
>
|
||||
<Trash2Icon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
{/if}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
|
||||
@ -19,8 +19,9 @@
|
||||
disabledResultTypes = [],
|
||||
disabledReferenceTypes = [],
|
||||
disabledSign = false,
|
||||
joinFields = $bindable()
|
||||
} = $props();
|
||||
|
||||
|
||||
let searchQuery = $state({});
|
||||
let dropdownOpen = $state({});
|
||||
|
||||
@ -225,7 +226,7 @@
|
||||
{/if}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
{:else if type === "selectmultiple"}
|
||||
<!-- {:else if type === "selectmultiple"}
|
||||
{@const filteredOptions = getFilteredOptions(key)}
|
||||
{@const selectedValues = Array.isArray(formState.form[key]) ? formState.form[key] : []}
|
||||
{@const selectedOptions = selectedValues
|
||||
@ -297,7 +298,7 @@
|
||||
Unselect All
|
||||
</button>
|
||||
{/if}
|
||||
</Select.Root>
|
||||
</Select.Root> -->
|
||||
{:else if type === "date"}
|
||||
<ReusableCalendar
|
||||
bind:value={formState.form[key]}
|
||||
@ -363,19 +364,19 @@
|
||||
{:else if type === "agejoin"}
|
||||
<div class="flex items-center gap-2 w-full">
|
||||
<InputGroup.Root>
|
||||
<InputGroup.Input type="number" />
|
||||
<InputGroup.Input type="number" bind:value={joinFields[key].YY}/>
|
||||
<InputGroup.Addon align="inline-end">
|
||||
<InputGroup.Text>Year</InputGroup.Text>
|
||||
</InputGroup.Addon>
|
||||
</InputGroup.Root>
|
||||
<InputGroup.Root>
|
||||
<InputGroup.Input type="number" />
|
||||
<InputGroup.Input type="number" bind:value={joinFields[key].MM}/>
|
||||
<InputGroup.Addon align="inline-end">
|
||||
<InputGroup.Text>Month</InputGroup.Text>
|
||||
</InputGroup.Addon>
|
||||
</InputGroup.Root>
|
||||
<InputGroup.Root>
|
||||
<InputGroup.Input type="number" />
|
||||
<InputGroup.Input type="number" bind:value={joinFields[key].DD}/>
|
||||
<InputGroup.Addon align="inline-end">
|
||||
<InputGroup.Text>Day</InputGroup.Text>
|
||||
</InputGroup.Addon>
|
||||
@ -388,35 +389,16 @@
|
||||
placeholder="Custom field type: {type}"
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class={`absolute min-h-[1rem] w-full ${
|
||||
key === 'FormulaCode' ? 'top-20' : 'top-8'
|
||||
}`}
|
||||
>
|
||||
<!-- {#if formState.errors[key]}
|
||||
<span class="text-destructive text-sm leading-none">
|
||||
{formState.errors[key]}
|
||||
</span>
|
||||
{/if} -->
|
||||
{#if key === 'FormulaCode' && formState.errors[key]}
|
||||
<div class="flex items-center gap-2 text-sm text-destructive">
|
||||
<span>{formState.errors[key]}:</span>
|
||||
<div class="flex gap-1">
|
||||
{#each formState.form.FormulaInput || [] as item}
|
||||
{@const hasItem = hasExactKeyword(formState.form.FormulaCode, item)}
|
||||
<Badge variant={hasItem ? 'default' : 'destructive'}>
|
||||
{item}
|
||||
</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else if formState.errors[key]}
|
||||
{#if formState.errors[key]}
|
||||
<span class="text-destructive text-sm leading-none">
|
||||
{formState.errors[key]}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
34
src/lib/utils/ageUtils.js
Normal file
34
src/lib/utils/ageUtils.js
Normal file
@ -0,0 +1,34 @@
|
||||
export function normalizeAge(age) {
|
||||
if (!age) return { YY: 0, MM: 0, DD: 0 };
|
||||
|
||||
return {
|
||||
YY: Number(age.YY) || 0,
|
||||
MM: Number(age.MM) || 0,
|
||||
DD: Number(age.DD) || 0
|
||||
};
|
||||
}
|
||||
|
||||
export function ageToDays(age) {
|
||||
const { YY, MM, DD } = normalizeAge(age);
|
||||
return YY * 365 + MM * 30 + DD;
|
||||
}
|
||||
|
||||
export function buildAgeText(age) {
|
||||
const { YY, MM, DD } = normalizeAge(age);
|
||||
|
||||
if (!YY && !MM && !DD) return "";
|
||||
|
||||
return `${YY}Y ${MM}M ${DD}D`;
|
||||
}
|
||||
|
||||
export function daysToAge(totalDays) {
|
||||
const days = Number(totalDays) || 0;
|
||||
|
||||
const YY = Math.floor(days / 365);
|
||||
const remainingAfterYear = days % 365;
|
||||
|
||||
const MM = Math.floor(remainingAfterYear / 30);
|
||||
const DD = remainingAfterYear % 30;
|
||||
|
||||
return { YY, MM, DD };
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user