mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 09:35:34 +07:00
working on initial testmap
This commit is contained in:
parent
7fcb5d8507
commit
44c9f29f09
@ -46,7 +46,6 @@ export const testCalSchema = z.object({
|
||||
export const refNumSchema = z.object({
|
||||
AgeStart: z.string().optional(),
|
||||
AgeEnd: z.string().optional(),
|
||||
Flag: z.string().min(1, "Required"),
|
||||
Low: z.string().optional(),
|
||||
High: z.string().optional(),
|
||||
LowSign: z.string().optional(),
|
||||
@ -94,6 +93,13 @@ export const refNumSchema = z.object({
|
||||
path: ["LowSign"],
|
||||
});
|
||||
}
|
||||
if (data.High && !data.HighSign) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Math sign for High is required",
|
||||
path: ["HighSign"],
|
||||
});
|
||||
}
|
||||
if (
|
||||
data.LowSign &&
|
||||
data.HighSign &&
|
||||
@ -108,6 +114,29 @@ export const refNumSchema = z.object({
|
||||
}
|
||||
})
|
||||
|
||||
export const refTxtSchema = z.object({
|
||||
AgeStart: z.string().optional(),
|
||||
AgeEnd: z.string().optional(),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const start = toDays(data.AgeStart);
|
||||
const end = toDays(data.AgeEnd);
|
||||
|
||||
// if (start !== null && start > 0 && end !== null && end <= start) {
|
||||
if (start !== null && end !== null && end <= start) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: "Age End must be greater than Age Start",
|
||||
path: ["AgeEnd"],
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
export const testMapSchema = z.object({
|
||||
HostID: z.string().optional(),
|
||||
ClientID: z.string().optional(),
|
||||
})
|
||||
|
||||
export const testInitialForm = {
|
||||
TestSiteID: "",
|
||||
SiteID: "",
|
||||
@ -180,6 +209,19 @@ export const refTxtInitialForm = {
|
||||
Notes: "",
|
||||
};
|
||||
|
||||
export const testMapInitialForm = {
|
||||
TestMapID: "",
|
||||
HostType: "",
|
||||
HostID: "",
|
||||
HostTestCode: "",
|
||||
HostTestName: "",
|
||||
ClientType: "",
|
||||
ClientID: "",
|
||||
ClientTestCode: "",
|
||||
ClientTestName: "",
|
||||
ConDefID: "",
|
||||
}
|
||||
|
||||
export const testDefaultErrors = {
|
||||
TestSiteCode: "Required",
|
||||
TestSiteName: "Required",
|
||||
@ -198,7 +240,15 @@ export const refNumDefaultErrors = {
|
||||
HighSign: null,
|
||||
};
|
||||
|
||||
export const refTxtDefaultErrors = {};
|
||||
export const refTxtDefaultErrors = {
|
||||
AgeStart: null,
|
||||
AgeEnd: null,
|
||||
};
|
||||
|
||||
export const testMapDefaultErrors = {
|
||||
HostID: null,
|
||||
ClientID: null,
|
||||
}
|
||||
|
||||
export const testFormFields = [
|
||||
{
|
||||
@ -699,12 +749,14 @@ export const refTxtFormFields = [
|
||||
label: "Age Start",
|
||||
required: false,
|
||||
type: "agejoin",
|
||||
validateOn: ["input"]
|
||||
},
|
||||
{
|
||||
key: "AgeEnd",
|
||||
label: "Age End",
|
||||
required: false,
|
||||
type: "agejoin",
|
||||
validateOn: ["input"]
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -768,6 +820,99 @@ export const refTxtFormFields = [
|
||||
},
|
||||
];
|
||||
|
||||
export const testMapFormFields = [
|
||||
{
|
||||
title: "Host & Client Information",
|
||||
rows: [
|
||||
{
|
||||
type: "row",
|
||||
columns: [
|
||||
{
|
||||
key: "HostType",
|
||||
label: "Host Type",
|
||||
required: false,
|
||||
type: "select",
|
||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`,
|
||||
},
|
||||
{
|
||||
key: "ClientType",
|
||||
label: "Client Type",
|
||||
required: false,
|
||||
type: "select",
|
||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/entity_type`,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
columns: [
|
||||
{
|
||||
key: "HostID",
|
||||
label: "Host ID",
|
||||
required: false,
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
key: "ClientID",
|
||||
label: "Client ID",
|
||||
required: false,
|
||||
type: "text",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
columns: [
|
||||
{
|
||||
key: "HostTestCode",
|
||||
label: "Host Test Code",
|
||||
required: false,
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
key: "ClientTestCode",
|
||||
label: "Client Test Code",
|
||||
required: false,
|
||||
type: "text",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
columns: [
|
||||
{
|
||||
key: "HostTestName",
|
||||
label: "Host Test Name",
|
||||
required: false,
|
||||
type: "text",
|
||||
},
|
||||
{
|
||||
key: "ClientTestName",
|
||||
label: "Client Test Name",
|
||||
required: false,
|
||||
type: "text",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: "row",
|
||||
columns: [
|
||||
{
|
||||
key: "ConDefID",
|
||||
label: "Container Definition",
|
||||
required: false,
|
||||
type: "select",
|
||||
optionsEndpoint: `${API.BASE_URL}${API.CONTAINER}`,
|
||||
valueKey: "ConDefID",
|
||||
labelKey: (item) => `${item.ConCode} - ${item.ConName}`,
|
||||
fullWidth: false
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
export function getTestFormActions(handlers) {
|
||||
return [
|
||||
{
|
||||
|
||||
@ -6,19 +6,25 @@
|
||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||
import * as Tabs from "$lib/components/ui/tabs/index.js";
|
||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||
import { buildTestPayload, testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields, refNumSchema, refNumDefaultErrors, refNumInitialForm, refNumFormFields, refTxtInitialForm, refTxtFormFields } from "$lib/components/dictionary/test/config/test-form-config";
|
||||
import { buildTestPayload,
|
||||
testCalSchema, testCalInitialForm, testCalDefaultErrors, testCalFormFields,
|
||||
refNumSchema, refNumDefaultErrors, refNumInitialForm, refNumFormFields,
|
||||
refTxtSchema, refTxtDefaultErrors, refTxtInitialForm, refTxtFormFields,
|
||||
testMapSchema, testMapInitialForm, testMapDefaultErrors, testMapFormFields
|
||||
} from "$lib/components/dictionary/test/config/test-form-config";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import RefNum from "./tabs/ref-num.svelte";
|
||||
import RefTxt from "./tabs/ref-txt.svelte";
|
||||
import Calculation from "./tabs/calculation.svelte";
|
||||
import Map from "./tabs/map.svelte";
|
||||
import { API } from "$lib/config/api";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
let props = $props();
|
||||
|
||||
let resetRefNum = $state();
|
||||
|
||||
let resetRefTxt = $state();
|
||||
let resetMap = $state();
|
||||
|
||||
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||
|
||||
@ -36,10 +42,18 @@
|
||||
});
|
||||
|
||||
const refTxtState = useForm({
|
||||
schema: null,
|
||||
schema: refTxtSchema,
|
||||
initialForm: refTxtInitialForm,
|
||||
defaultErrors: refTxtDefaultErrors,
|
||||
});
|
||||
|
||||
const mapFormState = useForm({
|
||||
schema: testMapSchema,
|
||||
initialForm: testMapInitialForm,
|
||||
defaultErrors: testMapDefaultErrors,
|
||||
modeOpt: 'cascade'
|
||||
})
|
||||
|
||||
const helpers = useDictionaryForm(formState);
|
||||
|
||||
const handlers = {
|
||||
@ -329,7 +343,7 @@
|
||||
group
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="map">
|
||||
map
|
||||
<Map {mapFormState} {testMapFormFields} bind:resetMap={resetMap}/>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="reference">
|
||||
<div class="w-full h-full flex items-start">
|
||||
|
||||
@ -0,0 +1,105 @@
|
||||
<script>
|
||||
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
|
||||
let { resetMap = $bindable(), ...props } = $props()
|
||||
|
||||
let tempMap = $state([]);
|
||||
let editingId = $state(null);
|
||||
let idCounter = $state(0);
|
||||
|
||||
resetMap = () => {
|
||||
tempMap = [];
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<div>
|
||||
<DictionaryFormRenderer
|
||||
formState={props.mapFormState}
|
||||
formFields={props.testMapFormFields}
|
||||
/>
|
||||
<div class="flex gap-2 mt-1 ms-2">
|
||||
{#if editingId !== null}
|
||||
<Button size="sm" class="cursor-pointer">
|
||||
Update
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" class="cursor-pointer">
|
||||
Cancel
|
||||
</Button>
|
||||
{:else}
|
||||
<Button size="sm" class="cursor-pointer">
|
||||
Insert
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
<div>
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row class="hover:bg-transparent">
|
||||
<Table.Head>Host Type</Table.Head>
|
||||
<Table.Head>Host ID</Table.Head>
|
||||
<Table.Head>Host Test Code</Table.Head>
|
||||
<Table.Head>Host Test Name</Table.Head>
|
||||
<Table.Head>Client Type</Table.Head>
|
||||
<Table.Head>Client ID</Table.Head>
|
||||
<Table.Head>Client Test Code</Table.Head>
|
||||
<Table.Head>Client Test Name</Table.Head>
|
||||
<Table.Head>Container</Table.Head>
|
||||
<Table.Head class="w-[80px]"></Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#if tempMap.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 tempMap as row (row.id)}
|
||||
<Table.Row
|
||||
class="cursor-pointer hover:bg-muted/50"
|
||||
>
|
||||
<Table.Cell>{row.HostType}</Table.Cell>
|
||||
<Table.Cell>{row.HostID}</Table.Cell>
|
||||
<Table.Cell>{row.HostTestCode}</Table.Cell>
|
||||
<Table.Cell>{row.HostTestName}</Table.Cell>
|
||||
<Table.Cell>{row.ClientType}</Table.Cell>
|
||||
<Table.Cell>{row.ClientID}</Table.Cell>
|
||||
<Table.Cell>{row.ClientTestCode}</Table.Cell>
|
||||
<Table.Cell>{row.ClientTestName}</Table.Cell>
|
||||
<Table.Cell>{row.ConDefID}</Table.Cell>
|
||||
<Table.Cell>
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
>
|
||||
<PencilIcon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
>
|
||||
<Trash2Icon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
{/if}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
</div>
|
||||
@ -8,6 +8,7 @@
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
import { untrack } from "svelte";
|
||||
import { toDays } from "$lib/utils/ageUtils";
|
||||
|
||||
let { resetRefNum = $bindable(), ...props } = $props()
|
||||
|
||||
@ -62,28 +63,28 @@
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function isOverlapping(newLow, newHigh, existingRows) {
|
||||
return existingRows.some(row => {
|
||||
return !(newHigh <= row.Low || newLow >= row.High);
|
||||
});
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
const low = Number(props.refNumState.form.Low);
|
||||
const high = Number(props.refNumState.form.High);
|
||||
console.log(`low: ${low}`);
|
||||
// console.log(props.refNumState.form);
|
||||
// const low = Number(props.refNumState.form.Low);
|
||||
// const high = Number(props.refNumState.form.High);
|
||||
const newStart = toDays(props.refNumState.form.AgeStart);
|
||||
const newEnd = toDays(props.refNumState.form.AgeEnd);
|
||||
// const row = { id: ++idCounter, ...snapshotForm() };
|
||||
// tempNumeric = [...tempNumeric, row];
|
||||
// resetForm();
|
||||
const isOverlap = tempNumeric.some(row => {
|
||||
const existingLow = Number(row.Low);
|
||||
const existingHigh = Number(row.High);
|
||||
|
||||
return !(high < existingLow || low > existingHigh);
|
||||
const isOverlap = tempNumeric.some(row => {
|
||||
const existingStart = toDays(row.AgeStart);
|
||||
const existingEnd = toDays(row.AgeEnd);
|
||||
|
||||
if (existingStart == null || existingEnd == null) return false;
|
||||
|
||||
return !(newEnd < existingStart || newStart > existingEnd);
|
||||
});
|
||||
|
||||
if (isOverlap) {
|
||||
props.refNumState.errors.High = "Range overlaps with existing data";
|
||||
props.refNumState.errors.AgeEnd =
|
||||
"Age range overlaps with existing data";
|
||||
return;
|
||||
}
|
||||
|
||||
@ -194,12 +195,10 @@
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
// Sinkronisasi Low dan LowSign
|
||||
if (!props.refNumState.form.Low || props.refNumState.form.Low === "") {
|
||||
props.refNumState.form.LowSign = "";
|
||||
}
|
||||
|
||||
// Sinkronisasi High dan HighSign
|
||||
if (!props.refNumState.form.High || props.refNumState.form.High === "") {
|
||||
props.refNumState.form.HighSign = "";
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
import { untrack } from "svelte";
|
||||
import { toDays } from "$lib/utils/ageUtils";
|
||||
|
||||
let { resetRefTxt = $bindable(), ...props } = $props()
|
||||
|
||||
@ -48,8 +49,31 @@
|
||||
}
|
||||
|
||||
function handleInsert() {
|
||||
const row = { id: ++idCounter, ...snapshotForm() };
|
||||
const newStart = toDays(props.refTxtState.form.AgeStart);
|
||||
const newEnd = toDays(props.refTxtState.form.AgeEnd);
|
||||
|
||||
const isOverlap = tempTxt.some(row => {
|
||||
const existingStart = toDays(row.AgeStart);
|
||||
const existingEnd = toDays(row.AgeEnd);
|
||||
|
||||
if (existingStart == null || existingEnd == null) return false;
|
||||
|
||||
return !(newEnd < existingStart || newStart > existingEnd);
|
||||
});
|
||||
|
||||
if (isOverlap) {
|
||||
props.refTxtState.errors.AgeEnd =
|
||||
"Age range overlaps with existing data";
|
||||
return;
|
||||
}
|
||||
|
||||
const row = {
|
||||
id: ++idCounter,
|
||||
...snapshotForm()
|
||||
};
|
||||
|
||||
tempTxt = [...tempTxt, row];
|
||||
|
||||
resetForm();
|
||||
}
|
||||
|
||||
@ -105,12 +129,6 @@
|
||||
|
||||
const txtRefTypeBadge = (type) => ({ TEXT: "TX", VSET: "VS" }[type] ?? null);;
|
||||
|
||||
// $effect(() => {
|
||||
// if (props.refType) {
|
||||
// props.refTxtState.form.TxtRefType = props.refType;
|
||||
// }
|
||||
// });
|
||||
|
||||
$effect(() => {
|
||||
for (const key of ["AgeStart", "AgeEnd"]) {
|
||||
props.refTxtState.form[key] =
|
||||
|
||||
@ -236,6 +236,7 @@
|
||||
formState.validateField("Low", formState.form[txtKey]);
|
||||
formState.validateField("LowSign");
|
||||
formState.validateField("High", formState.form[txtKey]);
|
||||
formState.validateField("HighSign");
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -304,6 +305,7 @@
|
||||
<InputGroup.Input type="number" bind:value={joinFields[key].YY}
|
||||
oninput={() => {
|
||||
if (validateOn?.includes("input")) {
|
||||
console.log('object');
|
||||
// formState.validateField(key, formState.form[key], false);
|
||||
formState.validateField("AgeStart");
|
||||
formState.validateField("AgeEnd");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user