mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 09:35:34 +07:00
fix multiple
fix tab selalu muncul di payload fix rule overlap sekarang berdasarkan kondisi rangetype ubah calc input param not required, nambahin result button
This commit is contained in:
parent
0c0bbd6e26
commit
5cab6097a9
@ -23,7 +23,18 @@ export const searchFields = [
|
||||
|
||||
];
|
||||
|
||||
export const detailSections = [];
|
||||
export const detailSections = [
|
||||
{
|
||||
class: "grid grid-cols-2 gap-4 items-center",
|
||||
fields: [
|
||||
{ key: "SiteID", label: "Site" },
|
||||
{ key: "TestSiteCode", label: "Test Code" },
|
||||
{ key: "TestSiteName", label: "Test Name" },
|
||||
{ key: "TestType", label: "Test Type" },
|
||||
{ key: "Description", label: "Description" },
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export function testActions(masterDetail) {
|
||||
return [
|
||||
|
||||
@ -41,7 +41,7 @@ export const testCalSchema = z
|
||||
level: z.preprocess((val) => (val ? Number(val) : 0), z.number())
|
||||
})
|
||||
)
|
||||
.min(1, 'Required'),
|
||||
.optional(),
|
||||
FormulaCode: z.string().optional()
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
@ -303,7 +303,7 @@ export const testDefaultErrors = {
|
||||
};
|
||||
|
||||
export const testCalDefaultErrors = {
|
||||
FormulaInput: 'Required',
|
||||
FormulaInput: null,
|
||||
FormulaCode: null,
|
||||
};
|
||||
|
||||
@ -611,7 +611,7 @@ export const testCalFormFields = [
|
||||
{
|
||||
key: 'FormulaInput',
|
||||
label: 'Input Parameter',
|
||||
required: true,
|
||||
required: false,
|
||||
type: 'selectmultiple',
|
||||
optionsEndpoint: `${API.BASE_URL}${API.TEST}`,
|
||||
valueKey: 'TestSiteCode',
|
||||
@ -726,19 +726,18 @@ export const refNumFormFields = [
|
||||
{
|
||||
type: 'row',
|
||||
columns: [
|
||||
// {
|
||||
// key: 'NumRefType',
|
||||
// label: 'Reference Type',
|
||||
// required: false,
|
||||
// type: 'text'
|
||||
// },
|
||||
{
|
||||
key: 'NumRefType',
|
||||
label: 'Reference Type',
|
||||
required: false,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
key: 'RangeType',
|
||||
label: 'Range Type',
|
||||
required: false,
|
||||
type: 'select',
|
||||
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/range_type`,
|
||||
fullWidth: false
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1035,18 +1034,23 @@ export function buildTestPayload({ mainForm, activeFormStates, testType, refNumD
|
||||
payload[key] = refNumData;
|
||||
} else if (key === 'refTxt' && refTxtData?.length > 0) {
|
||||
payload[key] = refTxtData;
|
||||
} else if(key === 'group') {
|
||||
} else if(key === 'group' && state.form?.Members?.length > 0) {
|
||||
payload[key] = {
|
||||
...state.form,
|
||||
Members: state.form?.Members?.map((m) => m.value).filter(Boolean) ?? []
|
||||
};
|
||||
} else if (key === 'map' && mapData?.length > 0) {
|
||||
payload[key] = mapData;
|
||||
} else if (state?.form) {
|
||||
} else if (key === 'cal') {
|
||||
payload[key] = {
|
||||
...state.form
|
||||
};
|
||||
}
|
||||
}
|
||||
// else if ((key === 'cal' || key === 'map') && state?.form) {
|
||||
// payload[key] = {
|
||||
// ...state.form
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
return cleanEmptyStrings(payload);
|
||||
|
||||
@ -148,7 +148,7 @@
|
||||
});
|
||||
console.log(payload);
|
||||
|
||||
// const result = await formState.save(masterDetail.mode);
|
||||
// const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
// toast('Test Created!');
|
||||
// masterDetail?.exitForm(true);
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
let props = $props();
|
||||
|
||||
const operators = ['+', '-', '*', '/', '^', '(', ')'];
|
||||
const logicalop = ['IF', 'THEN', 'ELSE', 'END', 'AND', 'OR', 'NOT', 'MIN', 'MAX'];
|
||||
const logicalop = ['IF', 'THEN', 'ELSE', 'END', 'AND', 'OR', 'NOT', 'MIN', 'MAX', 'RESULT'];
|
||||
const comparisonop = ['=', '!=', '<', '>', '<=', '>='];
|
||||
|
||||
let tokens = $state([]);
|
||||
|
||||
@ -33,6 +33,15 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
let disabledFlag = $derived.by(() => {
|
||||
const refType = props.refNumState.form.NumRefType;
|
||||
|
||||
if (refType === 'RANGE') return true;
|
||||
if (refType === 'THOLD') return false;
|
||||
|
||||
return false;
|
||||
})
|
||||
|
||||
function snapshotForm() {
|
||||
const f = props.refNumState.form;
|
||||
return {
|
||||
@ -85,6 +94,8 @@
|
||||
|
||||
if (existingStart == null || existingEnd == null) return false;
|
||||
|
||||
if (row.RangeType !== props.refNumState.form.RangeType) return false;
|
||||
|
||||
return !(newEnd < existingStart || newStart > existingEnd);
|
||||
});
|
||||
|
||||
@ -213,6 +224,7 @@
|
||||
formState={props.refNumState}
|
||||
formFields={props.refNumFormFields}
|
||||
{disabledSign}
|
||||
{disabledFlag}
|
||||
bind:joinFields
|
||||
/>
|
||||
<div class="flex gap-2 mt-1 ms-2">
|
||||
@ -276,7 +288,7 @@
|
||||
>{numRefTypeBadge(row.NumRefType)}</Badge
|
||||
>
|
||||
</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Flag}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.NumRefType === "RANGE" ? "AUTO" : row.Flag}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Interpretation}</Table.Cell>
|
||||
<Table.Cell class="font-medium">{row.Notes}</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
@ -39,7 +39,14 @@
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
const currentRefType = props.refTxtState.form.TxtRefType;
|
||||
|
||||
props.refTxtState.reset?.();
|
||||
|
||||
if (currentRefType) {
|
||||
props.refTxtState.form.TxtRefType = currentRefType;
|
||||
}
|
||||
|
||||
joinFields = {
|
||||
AgeStart: { DD: "", MM: "", YY: "" },
|
||||
AgeEnd: { DD: "", MM: "", YY: "" },
|
||||
|
||||
@ -1,112 +0,0 @@
|
||||
<script>
|
||||
import * as Select from '$lib/components/ui/select/index.js';
|
||||
import { fruits } from '$lib/components/multiselect/multiselect-form-config';
|
||||
import { Input } from '$lib/components/ui/input/index.js';
|
||||
import { z } from 'zod';
|
||||
import { Badge } from '$lib/components/ui/badge/index.js';
|
||||
|
||||
let value = $state([]);
|
||||
let inputValue = $state('');
|
||||
|
||||
function hasExactKeyword(input, keyword) {
|
||||
const regex = new RegExp(`\\b${keyword}\\b`, 'i');
|
||||
return regex.test(input);
|
||||
}
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
selected: z.array(z.string()),
|
||||
input: z.string()
|
||||
})
|
||||
.refine(
|
||||
(data) => {
|
||||
return data.selected.every((v) => hasExactKeyword(data.input, v));
|
||||
},
|
||||
{
|
||||
message: 'Input must contain all selected values',
|
||||
path: ['input']
|
||||
}
|
||||
);
|
||||
|
||||
let errors = $state({});
|
||||
|
||||
$effect(() => {
|
||||
const result = schema.safeParse({ selected: value, input: inputValue });
|
||||
if (!result.success) {
|
||||
const fieldErrors = {};
|
||||
for (const issue of result.error.issues) {
|
||||
fieldErrors[issue.path[0]] = issue.message;
|
||||
}
|
||||
errors = fieldErrors;
|
||||
} else {
|
||||
errors = {};
|
||||
}
|
||||
});
|
||||
|
||||
const selectedLabel = $derived(
|
||||
value.length === 0
|
||||
? 'Select a fruit'
|
||||
: value.map((v) => fruits.find((f) => f.value === v)?.value).join(', ')
|
||||
);
|
||||
|
||||
const hasErrors = $derived(Object.keys(errors).length > 0);
|
||||
|
||||
const inputStatus = $derived(
|
||||
value
|
||||
.filter((v) => v)
|
||||
.map((v) => ({
|
||||
value: v,
|
||||
label: fruits.find((f) => f.value === v)?.label,
|
||||
done: hasExactKeyword(inputValue, v)
|
||||
}))
|
||||
);
|
||||
|
||||
function unselectAll() {
|
||||
value = [];
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Select.Root type="multiple" name="favoriteFruit" bind:value>
|
||||
<Select.Trigger class="w-full">
|
||||
{selectedLabel}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Group>
|
||||
<Select.Label>Fruits</Select.Label>
|
||||
{#each fruits as fruit (fruit.value)}
|
||||
<Select.Item value={fruit.value} label={fruit.label}>
|
||||
{fruit.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Group>
|
||||
{#if value.length > 0}
|
||||
<Select.Separator />
|
||||
<button
|
||||
class="w-full px-2 py-1.5 text-left text-sm hover:bg-accent hover:text-accent-foreground"
|
||||
onclick={unselectAll}
|
||||
>
|
||||
Unselect All
|
||||
</button>
|
||||
{/if}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
|
||||
<Input bind:value={inputValue} />
|
||||
{#if errors.input}
|
||||
<div class="flex items-center gap-2 text-sm text-red-500">
|
||||
<span>{errors.input}:</span>
|
||||
<div class="flex gap-1">
|
||||
{#each inputStatus as item (item.value)}
|
||||
<Badge variant={item.done ? 'default' : 'destructive'}>
|
||||
{item.value}
|
||||
</Badge>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button disabled={hasErrors} class="rounded bg-blue-500 px-4 py-2 text-white disabled:opacity-50">
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
@ -9,6 +9,8 @@
|
||||
|
||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
||||
|
||||
let test = $derived(masterDetail?.selectedItem?.data);
|
||||
|
||||
const handlers = {
|
||||
editTest: () => masterDetail.enterEdit("data"),
|
||||
};
|
||||
@ -47,7 +49,7 @@
|
||||
{#if masterDetail.selectedItem}
|
||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.TestSiteName}
|
||||
title={masterDetail.selectedItem?.data?.TestSiteName}
|
||||
{actions}
|
||||
/>
|
||||
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
disabledResultTypes = [],
|
||||
disabledReferenceTypes = [],
|
||||
disabledSign = false,
|
||||
disabledFlag = false,
|
||||
joinFields = $bindable(),
|
||||
hiddenFields,
|
||||
handleTestTypeChange,
|
||||
@ -158,6 +159,7 @@
|
||||
}
|
||||
}}
|
||||
readonly={key === 'NumRefType' || key === 'TxtRefType' || key === 'Level'}
|
||||
disabled={key === 'Flag' && disabledFlag}
|
||||
/>
|
||||
{:else if type === 'email'}
|
||||
<Input
|
||||
@ -545,37 +547,39 @@
|
||||
{/if}
|
||||
</div>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onMoveCursorRight}>
|
||||
<MoveRightIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onDeleteChar}>
|
||||
<DeleteIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onClearExpression}>
|
||||
<BrushCleaningIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onAddNewline} title="New line">
|
||||
<CornerDownLeftIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
{#if formState.form.FormulaInput?.length > 0}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Selected Tests</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each formState.form.FormulaInput as item (item)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
class="h-auto w-auto p-2"
|
||||
onclick={() => onAddValue?.(item.value)}
|
||||
>
|
||||
{item.value}
|
||||
</Button>
|
||||
{/each}
|
||||
<MoveRightIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onDeleteChar}>
|
||||
<DeleteIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onClearExpression}>
|
||||
<BrushCleaningIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="icon" onclick={onAddNewline} title="New line">
|
||||
<CornerDownLeftIcon class="w-4 h-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div class="flex flex-col gap-4">
|
||||
{#if formState.form.FormulaInput?.length > 0}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Selected Tests</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
{#each formState.form.FormulaInput as item (item)}
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
class="h-auto w-auto p-2"
|
||||
onclick={() => onAddValue?.(item.value)}
|
||||
>
|
||||
{item.value}
|
||||
</Button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Literal Values -->
|
||||
{/if}
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Custom Values</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@ -624,7 +628,7 @@
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
|
||||
|
||||
<Popover.Root bind:open={numberPopoverOpen}>
|
||||
<Popover.Trigger>
|
||||
{#snippet child({ props: triggerProps })}
|
||||
@ -673,7 +677,6 @@
|
||||
</Popover.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Logical Operators</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@ -689,8 +692,6 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Comparison Operators -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Comparison Operators</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@ -706,8 +707,6 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Math Operators -->
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Math Operators</Label>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@ -723,15 +722,15 @@
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if tokens.length > 0}
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Preview</Label>
|
||||
<div class="border-2 border-dashed border-muted-foreground/30 rounded-lg">
|
||||
<pre class="font-mono text-sm bg-muted/50 p-2 rounded">{expressionString}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if tokens.length > 0}
|
||||
<div class="flex flex-col gap-2">
|
||||
<Label class="uppercase tracking-wide">Preview</Label>
|
||||
<div class="border-2 border-dashed border-muted-foreground/30 rounded-lg">
|
||||
<pre class="font-mono text-sm bg-muted/50 p-2 rounded">{expressionString}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user