mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-23 01:29:27 +07:00
continue refnum threshold rule checking
This commit is contained in:
parent
0f4dd0d522
commit
b546db4a01
@ -146,6 +146,13 @@ export const refNumSchema = z
|
||||
path: ['LowSign']
|
||||
});
|
||||
}
|
||||
if (data.LowSign && !['>', '>=', '='].includes(data.LowSign)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'Low sign must be =, > or >=',
|
||||
path: ['LowSign'],
|
||||
});
|
||||
}
|
||||
if (data.High && !data.HighSign) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
@ -153,6 +160,13 @@ export const refNumSchema = z
|
||||
path: ['HighSign']
|
||||
});
|
||||
}
|
||||
if (data.HighSign && !['<', '<=', '='].includes(data.HighSign)) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
message: 'High sign must be =, < or <=',
|
||||
path: ['HighSign'],
|
||||
});
|
||||
}
|
||||
if (data.LowSign && data.HighSign && data.LowSign === data.HighSign) {
|
||||
ctx.addIssue({
|
||||
code: z.ZodIssueCode.custom,
|
||||
|
||||
@ -432,6 +432,16 @@
|
||||
refTxtState.form.TxtRefType = value;
|
||||
}
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
const refType = formState.form.RefType;
|
||||
if (refType === 'RANGE' || refType === 'THOLD') {
|
||||
refNumState.form.NumRefType = refType;
|
||||
}
|
||||
if (refType === 'TEXT' || refType === 'VSET') {
|
||||
refTxtState.form.TxtRefType = refType;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<FormPageContainer title="Edit Test" {primaryAction} {secondaryActions}>
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
|
||||
return false;
|
||||
});
|
||||
// $inspect(props.refNumState.form.NumRefType)
|
||||
|
||||
function snapshotForm() {
|
||||
const f = props.refNumState.form;
|
||||
return {
|
||||
@ -91,10 +91,6 @@
|
||||
row.RangeType === form.RangeType
|
||||
);
|
||||
|
||||
console.log('peers:', JSON.stringify(peers));
|
||||
console.log('newLow:', form.Low, 'newLowSign:', form.LowSign);
|
||||
console.log('newHigh:', form.High, 'newHighSign:', form.HighSign);
|
||||
|
||||
if (peers.length === 0) return null;
|
||||
|
||||
const newLow = form.Low !== '' && form.Low != null ? Number(form.Low) : null;
|
||||
@ -102,46 +98,36 @@
|
||||
const newLowSign = form.LowSign ?? '';
|
||||
const newHighSign = form.HighSign ?? '';
|
||||
|
||||
const excludeIds = new Set();
|
||||
|
||||
// Cari row yang high-nya paling dekat di bawah newLow
|
||||
const closestBelow = peers
|
||||
.filter(r => r.High !== '' && r.High != null)
|
||||
.map(r => ({ ...r, highNum: Number(r.High) }))
|
||||
.filter(r => r.highNum <= newLow)
|
||||
.sort((a, b) => b.highNum - a.highNum)[0];
|
||||
|
||||
if (closestBelow) {
|
||||
excludeIds.add(closestBelow.id)
|
||||
}
|
||||
|
||||
const closestAbove = peers
|
||||
.filter(r => r.Low !== '' && r.Low != null)
|
||||
.filter(r => !excludeIds.has(r.id))
|
||||
.map(r => ({ ...r, lowNum: Number(r.Low) }))
|
||||
.filter(r => r.lowNum <= newHigh)
|
||||
.sort((a, b) => a.lowNum - b.lowNum)[0];
|
||||
|
||||
if (closestBelow) {
|
||||
const prevHigh = closestBelow.highNum;
|
||||
const prevHighSign = closestBelow.HighSign ?? '';
|
||||
|
||||
if (prevHigh !== newLow) {
|
||||
// Ada celah
|
||||
if (newLow > prevHigh + 1) {
|
||||
return { field: 'Low', message: `Gap between intervals. Previous interval ends at ${prevHigh}, new interval starts at ${newLow}` };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
for (const row of peers) {
|
||||
const rowHigh = row.High !== '' && row.High != null ? Number(row.High) : null;
|
||||
const rowLow = row.Low !== '' && row.Low != null ? Number(row.Low) : null;
|
||||
const rowHighSign = row.HighSign ?? '';
|
||||
const rowLowSign = row.LowSign ?? '';
|
||||
|
||||
console.log('rowHigh:', rowHigh, typeof rowHigh);
|
||||
console.log('newLow:', newLow, typeof newLow);
|
||||
console.log('equal?:', rowHigh === newLow);
|
||||
|
||||
// No. 4 & 6 — tanpa celah & tanpa sign kontradiktif
|
||||
// Cek apakah interval baru harus dimulai tepat setelah interval sebelumnya berakhir
|
||||
if (rowHigh != null && newLow != null && rowHigh === newLow) {
|
||||
console.log('boundary check:', rowHighSign, newLowSign);
|
||||
|
||||
// rowHighSign <= 200, newLowSign harus > 200
|
||||
// rowHighSign < 200, newLowSign harus >= 200
|
||||
const prevEndInclusive = rowHighSign === '<=';
|
||||
if (prevHigh === newLow) {
|
||||
const prevEndInclusive = prevHighSign === '<=';
|
||||
const newStartInclusive = newLowSign === '>=';
|
||||
console.log('prevEndInclusive:', prevEndInclusive, 'newStartInclusive:', newStartInclusive);
|
||||
|
||||
if (prevEndInclusive && newStartInclusive) {
|
||||
return { field: 'LowSign', message: 'Sign contradicts previous interval (overlap at boundary). Use > instead of >=' };
|
||||
}
|
||||
@ -149,68 +135,45 @@
|
||||
return { field: 'LowSign', message: 'Gap between intervals. Use >= instead of >' };
|
||||
}
|
||||
}
|
||||
if (newLow < prevHigh) {
|
||||
return { field: 'Low', message: 'This interval overlaps with an existing interval' };
|
||||
}
|
||||
}
|
||||
|
||||
if (rowLow != null && newHigh != null && rowLow === newHigh) {
|
||||
const prevStartInclusive = rowLowSign === '>=';
|
||||
if (closestAbove) {
|
||||
const nextLow = closestAbove.lowNum;
|
||||
const nextLowSign = closestAbove.LowSign ?? '';
|
||||
|
||||
if (newHigh < nextLow - 1) {
|
||||
return { field: 'High', message: `Gap between intervals. Next interval starts at ${nextLow}, new interval ends at ${newHigh}` };
|
||||
}
|
||||
if (nextLow === newHigh) {
|
||||
const nextStartInclusive = nextLowSign === '>=';
|
||||
const newEndInclusive = newHighSign === '<=';
|
||||
|
||||
if (prevStartInclusive && newEndInclusive) {
|
||||
if (nextStartInclusive && newEndInclusive) {
|
||||
return { field: 'HighSign', message: 'Sign contradicts next interval (overlap at boundary). Use < instead of <=' };
|
||||
}
|
||||
if (!prevStartInclusive && !newEndInclusive) {
|
||||
if (!nextStartInclusive && !newEndInclusive) {
|
||||
return { field: 'HighSign', message: 'Gap between intervals. Use <= instead of <' };
|
||||
}
|
||||
}
|
||||
|
||||
// No. 5 — overlap: cek apakah ada nilai yang bisa memenuhi kedua interval
|
||||
// Contoh: row adalah "< 100", new adalah "> 90" → overlap di 91–99
|
||||
if (rowHigh != null && newLow != null) {
|
||||
const prevEnd = rowHigh;
|
||||
const newStart = newLow;
|
||||
|
||||
const prevInclusive = rowHighSign === '<=';
|
||||
const newInclusive = newLowSign === '>=';
|
||||
|
||||
const isOverlap =
|
||||
newStart < prevEnd ||
|
||||
(newStart === prevEnd && prevInclusive && newInclusive);
|
||||
|
||||
if (isOverlap) {
|
||||
return { field: 'Low', message: 'This interval overlaps with an existing interval' };
|
||||
}
|
||||
if (newHigh > nextLow) {
|
||||
return { field: 'High', message: 'This interval overlaps with an existing interval' };
|
||||
}
|
||||
|
||||
if (rowLow != null && newHigh != null) {
|
||||
const prevStart = rowLow;
|
||||
const newEnd = newHigh;
|
||||
|
||||
const prevInclusive = rowLowSign === '>=';
|
||||
const newInclusive = newHighSign === '<=';
|
||||
|
||||
const isOverlap =
|
||||
newEnd > prevStart ||
|
||||
(newEnd === prevStart && prevInclusive && newInclusive);
|
||||
|
||||
if (isOverlap) {
|
||||
return { field: 'High', message: 'This interval overlaps with an existing interval' };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
// $inspect(tempNumeric)
|
||||
|
||||
function handleInsert() {
|
||||
const newStart = toDays(props.refNumState.form.AgeStart);
|
||||
const newEnd = toDays(props.refNumState.form.AgeEnd);
|
||||
|
||||
const isOverlap = tempNumeric.some((row) => {
|
||||
if (row.SpcType !== props.refNumState.form.SpcType) return false;
|
||||
if (row.Sex !== props.refNumState.form.Sex) return false;
|
||||
if (row.NumRefType !== props.refNumState.form.NumRefType) return false;
|
||||
if (row.RangeType !== props.refNumState.form.RangeType) return false;
|
||||
const isOverlap = props.refNumState.form.NumRefType === 'THOLD' ? false : tempNumeric.some((row) => {
|
||||
if ((row.SpcType ?? '') !== (props.refNumState.form.SpcType ?? '')) return false;
|
||||
if ((row.Sex ?? '') !== (props.refNumState.form.Sex ?? '')) return false;
|
||||
if ((row.NumRefType ?? '') !== (props.refNumState.form.NumRefType ?? '')) return false;
|
||||
if ((row.RangeType ?? '') !== (props.refNumState.form.RangeType ?? '')) return false;
|
||||
|
||||
if (row.id === editingId) return false;
|
||||
|
||||
@ -222,12 +185,13 @@
|
||||
return (newStart <= existingEnd && newEnd >= existingStart);
|
||||
});
|
||||
|
||||
console.log(`isoverlap: ${isOverlap}`)
|
||||
if (isOverlap) {
|
||||
props.refNumState.errors.AgeEnd = 'Age range overlaps with existing data';
|
||||
return;
|
||||
}
|
||||
|
||||
const tholdError = validateTholdContinuity();
|
||||
const tholdError = validateTholdContinuity(editingId);
|
||||
if (tholdError) {
|
||||
props.refNumState.errors[tholdError.field] = tholdError.message;
|
||||
return;
|
||||
@ -276,6 +240,37 @@
|
||||
}
|
||||
|
||||
function handleUpdate() {
|
||||
const newStart = toDays(props.refNumState.form.AgeStart);
|
||||
const newEnd = toDays(props.refNumState.form.AgeEnd);
|
||||
|
||||
const isOverlap = props.refNumState.form.NumRefType === 'THOLD' ? false : tempNumeric.some((row) => {
|
||||
if ((row.SpcType ?? '') !== (props.refNumState.form.SpcType ?? '')) return false;
|
||||
if ((row.Sex ?? '') !== (props.refNumState.form.Sex ?? '')) return false;
|
||||
if ((row.NumRefType ?? '') !== (props.refNumState.form.NumRefType ?? '')) return false;
|
||||
if ((row.RangeType ?? '') !== (props.refNumState.form.RangeType ?? '')) return false;
|
||||
|
||||
if (row.id === editingId) return false;
|
||||
|
||||
const existingStart = toDays(row.AgeStart);
|
||||
const existingEnd = toDays(row.AgeEnd);
|
||||
|
||||
if (existingStart == null || existingEnd == null) return false;
|
||||
|
||||
return (newStart <= existingEnd && newEnd >= existingStart);
|
||||
});
|
||||
|
||||
console.log(`isoverlap: ${isOverlap}`)
|
||||
if (isOverlap) {
|
||||
props.refNumState.errors.AgeEnd = 'Age range overlaps with existing data';
|
||||
return;
|
||||
}
|
||||
|
||||
const tholdError = validateTholdContinuity(editingId);
|
||||
if (tholdError) {
|
||||
props.refNumState.errors[tholdError.field] = tholdError.message;
|
||||
return;
|
||||
}
|
||||
|
||||
tempNumeric = tempNumeric.map((row) =>
|
||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||
);
|
||||
@ -396,7 +391,7 @@
|
||||
Cancel
|
||||
</Button>
|
||||
{:else}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsert}>Insert</Button>
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsert} disabled={Object.values(props.refNumState.errors).some(error => error !== null)}>Insert</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@ -439,17 +434,6 @@
|
||||
-
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
<!-- <Table.Cell class="font-medium flex justify-between">
|
||||
<div>
|
||||
{row.LowSign ? row.LowSign : ''}
|
||||
{row.Low || 'null'} –
|
||||
{row.HighSign ? row.HighSign : ''}
|
||||
{row.High || 'null'}
|
||||
</div>
|
||||
<Badge variant="outline" class="border-dashed border-primary border-2"
|
||||
>{numRefTypeBadge(row.NumRefType)}</Badge
|
||||
>
|
||||
</Table.Cell> -->
|
||||
<Table.Cell class="font-medium flex justify-between">
|
||||
<div>
|
||||
{#if row.Low && row.High}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user