update report view and remove backup files
This commit is contained in:
parent
448186bea7
commit
d787522519
@ -69,62 +69,70 @@
|
|||||||
<!-- Report Content -->
|
<!-- Report Content -->
|
||||||
<div x-show="!loading && selectedTest && controls.length > 0" class="space-y-6 animate-in fade-in duration-500">
|
<div x-show="!loading && selectedTest && controls.length > 0" class="space-y-6 animate-in fade-in duration-500">
|
||||||
|
|
||||||
<!-- Report Header (Print only) -->
|
<!-- Report Header -->
|
||||||
<div class="hidden print:block mb-8 border-b-2 border-base-content/20 pb-4">
|
<div class="mb-6 print:mb-4">
|
||||||
<div class="flex justify-between items-end">
|
<!-- Main Header -->
|
||||||
|
<div class="text-center border-b-2 border-black pb-3 mb-3">
|
||||||
|
<h1 class="text-2xl font-black uppercase tracking-widest mb-1">Internal QC</h1>
|
||||||
|
<h2 class="text-lg font-bold" x-text="departmentDisplayName"></h2>
|
||||||
|
<h3 class="text-sm font-bold uppercase tracking-wide">Trisensa Diagnostic Centre</h3>
|
||||||
|
</div>
|
||||||
|
<!-- Sub Header -->
|
||||||
|
<div class="flex justify-between items-center border-b border-gray-400 pb-2">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-3xl font-black uppercase tracking-tighter" x-text="testName"></h2>
|
<span class="text-xs uppercase font-bold opacity-60">Test:</span>
|
||||||
<p class="text-sm font-bold opacity-60" x-text="departmentName"></p>
|
<span class="text-base font-bold ml-1" x-text="testName"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div>
|
||||||
<p class="text-xl font-bold" x-text="monthDisplay"></p>
|
<span class="text-xs uppercase font-bold opacity-60">Period:</span>
|
||||||
<p class="text-xs opacity-50">Report Generated: <?= date('d M Y H:i') ?></p>
|
<span class="text-base font-bold ml-1" x-text="monthDisplay"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Summary Stats -->
|
<!-- Summary Stats - Compact Horizontal Table -->
|
||||||
<div class="flex flex-wrap gap-3 mb-6 no-print-gap">
|
<div class="mb-4 print:mb-2">
|
||||||
<template x-for="control in processedControls" :key="control.controlId">
|
<div class="bg-base-100 rounded-xl border border-base-300 shadow-sm overflow-hidden">
|
||||||
<div class="bg-base-100 rounded-xl border border-base-300 shadow-sm overflow-hidden flex flex-col w-full sm:w-[calc(50%-0.75rem)] lg:w-[calc(33.333%-0.75rem)] xl:w-[calc(25%-0.75rem)] print:w-[calc(33.333%-0.5rem)] print:text-[11px]">
|
<div class="overflow-x-auto">
|
||||||
<div class="bg-base-200/50 p-2 border-b border-base-300">
|
<table class="table table-xs w-full">
|
||||||
<div class="flex justify-between items-center gap-2">
|
<thead class="bg-base-200/50">
|
||||||
<h3 class="font-bold truncate text-xs" x-text="control.controlName"></h3>
|
<tr>
|
||||||
<span class="badge badge-xs badge-neutral shrink-0 print:scale-75" x-text="'Lot: ' + (control.lot || 'N/A')"></span>
|
<th class="text-left whitespace-nowrap">Control</th>
|
||||||
</div>
|
<th class="text-center">Lot</th>
|
||||||
</div>
|
<th class="text-center">N</th>
|
||||||
<div class="p-2 flex-1 grid grid-cols-2 gap-2">
|
<th class="text-center">Mean</th>
|
||||||
<div class="flex flex-col">
|
<th class="text-center">SD</th>
|
||||||
<span class="text-[9px] uppercase font-bold opacity-50">N</span>
|
<th class="text-center">CV%</th>
|
||||||
<span class="text-sm font-mono font-bold" x-text="control.stats.n || 0"></span>
|
<th class="text-center">Target</th>
|
||||||
</div>
|
<th class="text-center">Bias%</th>
|
||||||
<div class="flex flex-col">
|
</tr>
|
||||||
<span class="text-[9px] uppercase font-bold opacity-50">Mean</span>
|
</thead>
|
||||||
<span class="text-sm font-mono font-bold text-primary"
|
<tbody>
|
||||||
x-text="formatNum(control.stats.mean)"></span>
|
<template x-for="control in processedControls" :key="control.controlId">
|
||||||
</div>
|
<tr class="hover:bg-base-200/30">
|
||||||
<div class="flex flex-col">
|
<td class="font-bold whitespace-nowrap" x-text="control.controlName"></td>
|
||||||
<span class="text-[9px] uppercase font-bold opacity-50">SD</span>
|
<td class="text-center font-mono text-[10px]" x-text="control.lot || 'N/A'"></td>
|
||||||
<span class="text-sm font-mono font-bold" x-text="formatNum(control.stats.sd)"></span>
|
<td class="text-center font-mono" x-text="control.stats.n || 0"></td>
|
||||||
</div>
|
<td class="text-center font-mono" x-text="formatNum(control.stats.mean)"></td>
|
||||||
<div class="flex flex-col">
|
<td class="text-center font-mono" x-text="formatNum(control.stats.sd)"></td>
|
||||||
<span class="text-[9px] uppercase font-bold opacity-50">% CV</span>
|
<td class="text-center font-mono"
|
||||||
<span class="text-sm font-mono font-bold"
|
:class="control.stats.cv > 5 ? 'text-warning' : 'text-success'"
|
||||||
:class="control.stats.cv > 5 ? 'text-warning' : 'text-success'"
|
x-text="formatNum(control.stats.cv, 1) + '%'"></td>
|
||||||
x-text="formatNum(control.stats.cv, 1) + '%'"></span>
|
<td class="text-center font-mono text-[10px]"
|
||||||
</div>
|
x-text="formatNum(control.mean) + '±' + formatNum(2*control.sd)"></td>
|
||||||
</div>
|
<td class="text-center font-mono"
|
||||||
<div class="px-2 py-1 bg-base-200/30 text-[9px] flex justify-between border-t border-base-300 font-medium">
|
:class="getBiasClass(control)"
|
||||||
<span>Tgt: <span x-text="formatNum(control.mean)"></span>±<span x-text="formatNum(2*control.sd)"></span></span>
|
x-text="control.stats.mean ? getBias(control) + '%' : '-'"></td>
|
||||||
<span x-show="control.stats.mean" :class="getBiasClass(control)"
|
</tr>
|
||||||
x-text="'B: ' + getBias(control) + '%'"></span>
|
</template>
|
||||||
</div>
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Table and Chart Grid -->
|
<!-- Table and Chart Grid -->
|
||||||
<div class="flex flex-col lg:flex-row gap-6">
|
<div class="flex gap-6">
|
||||||
<!-- Monthly Table (Left) -->
|
<!-- Monthly Table (Left) -->
|
||||||
<div class="w-full lg:w-1/3">
|
<div class="w-full lg:w-1/3">
|
||||||
<div class="bg-base-100 rounded-xl border border-base-300 shadow-sm overflow-hidden h-full">
|
<div class="bg-base-100 rounded-xl border border-base-300 shadow-sm overflow-hidden h-full">
|
||||||
@ -132,7 +140,7 @@
|
|||||||
<h3 class="font-bold text-sm uppercase tracking-widest opacity-70 print:text-[10px]">Daily Results Log</h3>
|
<h3 class="font-bold text-sm uppercase tracking-widest opacity-70 print:text-[10px]">Daily Results Log</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="overflow-x-auto max-h-[600px] overflow-y-auto">
|
<div class="overflow-x-auto max-h-[600px] overflow-y-auto">
|
||||||
<table class="table table-xs w-full print:table-xs">
|
<table class="table table-xs w-full">
|
||||||
<thead class="bg-base-200/50 sticky top-0">
|
<thead class="bg-base-200/50 sticky top-0">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="w-10 text-center print:px-1">Day</th>
|
<th class="w-10 text-center print:px-1">Day</th>
|
||||||
@ -209,32 +217,94 @@
|
|||||||
body {
|
body {
|
||||||
-webkit-print-color-adjust: exact;
|
-webkit-print-color-adjust: exact;
|
||||||
print-color-adjust: exact;
|
print-color-adjust: exact;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force black and white except canvas */
|
||||||
|
* {
|
||||||
|
color: #000 !important;
|
||||||
|
border-color: #333 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas, canvas * {
|
||||||
|
color: initial !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force white backgrounds */
|
||||||
|
.bg-base-100, .bg-base-200, .bg-base-300, [class*="bg-base-"],
|
||||||
|
.table thead, .table tbody, .table tr, .table td, .table th {
|
||||||
|
background-color: #fff !important;
|
||||||
|
background: #fff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure header is visible in print */
|
||||||
|
[class*="print:mb-4"] {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Header styling for print */
|
||||||
|
.border-b-2.border-black {
|
||||||
|
border-bottom: 2px solid #000 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.border-b.border-gray-400 {
|
||||||
|
border-bottom: 1px solid #666 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact control summary table */
|
||||||
|
.table-compact th, .table-compact td {
|
||||||
|
padding: 4px 3px !important;
|
||||||
|
font-size: 7px !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
border: 1px solid #ccc !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-compact th {
|
||||||
|
font-weight: bold !important;
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove card styling in print */
|
||||||
|
.rounded-xl, .rounded-2xl {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shadow-sm, .shadow {
|
||||||
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-print,
|
.no-print,
|
||||||
.navbar,
|
.navbar,
|
||||||
footer,
|
footer,
|
||||||
.drawer-side,
|
.drawer-side,
|
||||||
.divider,
|
.divider {
|
||||||
.mb-6:first-child,
|
|
||||||
.flex.justify-between:first-child {
|
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.space-y-6 > div:first-child {
|
/* Remove hiding of first child - we need the header visible */
|
||||||
display: none !important;
|
/* .space-y-6 > div:first-child was hiding the header */
|
||||||
}
|
|
||||||
|
|
||||||
.space-y-6 > div:nth-child(2) {
|
.space-y-6 > div:nth-child(2) {
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
margin: 0 !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Report content - natural height */
|
||||||
|
.space-y-6 {
|
||||||
|
display: block !important;
|
||||||
|
gap: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.shadow-sm,
|
.shadow-sm,
|
||||||
.shadow,
|
.shadow,
|
||||||
.shadow-md,
|
.shadow-md,
|
||||||
@ -258,18 +328,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.table-xs th, .table-xs td {
|
.table-xs th, .table-xs td {
|
||||||
padding: 1px !important;
|
padding: 4px 3px !important;
|
||||||
font-size: 6px !important;
|
font-size: 7px !important;
|
||||||
line-height: 1.2 !important;
|
line-height: 1.4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
font-size: 6px !important;
|
font-size: 7px !important;
|
||||||
line-height: 1.2 !important;
|
line-height: 1.4 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table tr {
|
.table tr {
|
||||||
height: 15px !important;
|
height: auto !important;
|
||||||
|
min-height: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table tbody tr {
|
||||||
|
padding: 4px 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.overflow-x-auto {
|
.overflow-x-auto {
|
||||||
@ -277,17 +352,20 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.max-h-\[600px\] {
|
.max-h-\[600px\] {
|
||||||
max-height: 500px !important;
|
max-height: none !important;
|
||||||
|
overflow-y: visible !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.flex-col.lg\:flex-row {
|
/* Force 2-column layout in print */
|
||||||
|
.space-y-6 > .flex.gap-6 {
|
||||||
flex-direction: row !important;
|
flex-direction: row !important;
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
|
gap: 8px !important;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-full.lg\:w-1\/3 {
|
[class*="lg:w-1/3"] {
|
||||||
width: 35% !important;
|
width: 35% !important;
|
||||||
flex: 0 0 35% !important;
|
flex: 0 0 35% !important;
|
||||||
max-width: 35% !important;
|
max-width: 35% !important;
|
||||||
@ -295,7 +373,7 @@
|
|||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.w-full.lg\:w-2\/3 {
|
[class*="lg:w-2/3"] {
|
||||||
width: 65% !important;
|
width: 65% !important;
|
||||||
flex: 0 0 65% !important;
|
flex: 0 0 65% !important;
|
||||||
max-width: 65% !important;
|
max-width: 65% !important;
|
||||||
@ -327,11 +405,11 @@
|
|||||||
gap: 2px !important;
|
gap: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-[8px] {
|
[class*="text-[8px]"] {
|
||||||
font-size: 6px !important;
|
font-size: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-[9px] {
|
[class*="text-[9px]"] {
|
||||||
font-size: 7px !important;
|
font-size: 7px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -344,14 +422,20 @@
|
|||||||
animation: none !important;
|
animation: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-\[560px\],
|
/* Chart fills available height */
|
||||||
.h-\[560px\] canvas,
|
.h-\[560px\] {
|
||||||
|
flex: 1 !important;
|
||||||
|
height: auto !important;
|
||||||
|
min-height: 400px !important;
|
||||||
|
}
|
||||||
|
|
||||||
canvas#mergedChart {
|
canvas#mergedChart {
|
||||||
height: 500px !important;
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.h-full {
|
.h-full {
|
||||||
height: auto !important;
|
height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print\:break-inside-avoid {
|
.print\:break-inside-avoid {
|
||||||
@ -363,36 +447,32 @@
|
|||||||
padding: 2px !important;
|
padding: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print\:text-\[10px\] {
|
[class*="print:text-[10px]"] {
|
||||||
font-size: 8px !important;
|
font-size: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print\:text-\[11px\] {
|
[class*="print:text-[11px]"] {
|
||||||
font-size: 8px !important;
|
font-size: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print\:scale-75 {
|
.print\\:scale-75 {
|
||||||
transform: none !important;
|
transform: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.print:text-\[11px\] {
|
|
||||||
font-size: 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-xs {
|
.badge-xs {
|
||||||
font-size: 6px !important;
|
font-size: 6px !important;
|
||||||
padding: 1px 3px !important;
|
padding: 1px 3px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-\[9px\] {
|
[class*="text-[9px]"] {
|
||||||
font-size: 7px !important;
|
font-size: 7px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-\[10px\] {
|
[class*="text-[10px]"] {
|
||||||
font-size: 8px !important;
|
font-size: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text-\[11px\] {
|
[class*="text-[11px]"] {
|
||||||
font-size: 8px !important;
|
font-size: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,6 +541,29 @@
|
|||||||
.p-2 {
|
.p-2 {
|
||||||
padding: 2px !important;
|
padding: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Remove chart legend in print to save space */
|
||||||
|
.chart-legend,
|
||||||
|
.legend,
|
||||||
|
canvas + div,
|
||||||
|
[class*="legend"] {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Minimize chart title area */
|
||||||
|
[class*="print:mb-1"] {
|
||||||
|
margin-bottom: 2px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ensure chart title is tiny */
|
||||||
|
[class*="print:text-[10px]"] {
|
||||||
|
font-size: 7px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove extra padding from chart container */
|
||||||
|
.p-4.h-full {
|
||||||
|
padding: 4px !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<?= $this->endSection(); ?>
|
<?= $this->endSection(); ?>
|
||||||
@ -589,7 +692,9 @@
|
|||||||
|
|
||||||
const labels = days;
|
const labels = days;
|
||||||
const datasets = [];
|
const datasets = [];
|
||||||
let allValues = [];
|
|
||||||
|
// Store actual values for tooltips
|
||||||
|
const actualValues = {};
|
||||||
|
|
||||||
controls.forEach((control, index) => {
|
controls.forEach((control, index) => {
|
||||||
const mean = parseFloat(control.mean) || 0;
|
const mean = parseFloat(control.mean) || 0;
|
||||||
@ -598,19 +703,26 @@
|
|||||||
const bgColor = bgColors[index % bgColors.length];
|
const bgColor = bgColors[index % bgColors.length];
|
||||||
|
|
||||||
const results = control.results || {};
|
const results = control.results || {};
|
||||||
|
const controlKey = control.controlId || index;
|
||||||
|
actualValues[controlKey] = {};
|
||||||
|
|
||||||
const dataPoints = days.map(day => {
|
const dataPoints = days.map(day => {
|
||||||
const res = results[day];
|
const res = results[day];
|
||||||
if (res && res.resValue !== null) {
|
if (res && res.resValue !== null && mean !== 0 && sd !== 0) {
|
||||||
const val = parseFloat(res.resValue);
|
const val = parseFloat(res.resValue);
|
||||||
allValues.push(val);
|
// Store actual value for tooltip
|
||||||
return val;
|
actualValues[controlKey][day] = val;
|
||||||
|
// Convert to deviation from mean in SD units
|
||||||
|
return (val - mean) / sd;
|
||||||
}
|
}
|
||||||
|
actualValues[controlKey][day] = null;
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
const pointColors = dataPoints.map(v => {
|
const pointColors = dataPoints.map(v => {
|
||||||
if (v === null || mean === 0 || sd === 0) return color;
|
if (v === null) return color;
|
||||||
return Math.abs(v - mean) > 2 * sd ? '#ef4444' : color;
|
// Red if outside ±2SD, otherwise use control color
|
||||||
|
return Math.abs(v) > 2 ? '#ef4444' : color;
|
||||||
});
|
});
|
||||||
|
|
||||||
datasets.push({
|
datasets.push({
|
||||||
@ -624,24 +736,14 @@
|
|||||||
pointBorderColor: pointColors,
|
pointBorderColor: pointColors,
|
||||||
pointHoverRadius: 6,
|
pointHoverRadius: 6,
|
||||||
spanGaps: true,
|
spanGaps: true,
|
||||||
tension: 0.1
|
tension: 0.1,
|
||||||
|
// Store reference data for tooltip
|
||||||
|
controlMean: mean,
|
||||||
|
controlSd: sd,
|
||||||
|
controlKey: controlKey
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Calculate scale
|
|
||||||
const targets = controls.map(c => parseFloat(c.mean) || 0).filter(m => m !== 0);
|
|
||||||
const sds = controls.map(c => parseFloat(c.sd) || 0).filter(s => s !== 0);
|
|
||||||
|
|
||||||
let minScale = 0, maxScale = 100;
|
|
||||||
if (allValues.length > 0 && targets.length > 0 && sds.length > 0) {
|
|
||||||
const minVal = Math.min(...allValues);
|
|
||||||
const maxVal = Math.max(...allValues);
|
|
||||||
const minTgt = Math.min(...targets) - 3.5 * Math.max(...sds);
|
|
||||||
const maxTgt = Math.max(...targets) + 3.5 * Math.max(...sds);
|
|
||||||
minScale = Math.min(minVal, minTgt);
|
|
||||||
maxScale = Math.max(maxVal, maxTgt);
|
|
||||||
}
|
|
||||||
|
|
||||||
const chartData = {
|
const chartData = {
|
||||||
labels: labels,
|
labels: labels,
|
||||||
datasets: datasets
|
datasets: datasets
|
||||||
@ -668,26 +770,44 @@
|
|||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function(context) {
|
||||||
return context.dataset.label + ': ' + context.parsed.x;
|
const dataset = context.dataset;
|
||||||
|
const dayIndex = context.dataIndex;
|
||||||
|
const day = days[dayIndex];
|
||||||
|
const deviation = context.parsed.x;
|
||||||
|
const actualVal = actualValues[dataset.controlKey][day];
|
||||||
|
|
||||||
|
if (actualVal === null) {
|
||||||
|
return dataset.label + ': No data';
|
||||||
|
}
|
||||||
|
|
||||||
|
const mean = dataset.controlMean;
|
||||||
|
const sd = dataset.controlSd;
|
||||||
|
return `${dataset.label}: ${actualVal.toFixed(2)} (${deviation > 0 ? '+' : ''}${deviation.toFixed(2)}σ)`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
min: minScale,
|
min: -3,
|
||||||
max: maxScale,
|
max: 3,
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Value',
|
text: 'Deviation from Mean (SD)',
|
||||||
font: { size: 11, weight: 'bold' }
|
font: { size: 11, weight: 'bold' }
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
display: true,
|
display: true,
|
||||||
color: '#e5e7eb'
|
color: '#e5e7eb',
|
||||||
|
drawBorder: false
|
||||||
},
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
font: { family: 'monospace', size: 10 }
|
font: { family: 'monospace', size: 10 },
|
||||||
|
stepSize: 1,
|
||||||
|
callback: function(value) {
|
||||||
|
if (value === 0) return '0';
|
||||||
|
return (value > 0 ? '+' : '') + value + 'σ';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
@ -705,6 +825,18 @@
|
|||||||
stepSize: 1
|
stepSize: 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
annotation: {
|
||||||
|
annotations: {
|
||||||
|
line1: {
|
||||||
|
type: 'line',
|
||||||
|
xMin: 0,
|
||||||
|
xMax: 0,
|
||||||
|
borderColor: 'rgba(0,0,0,0.5)',
|
||||||
|
borderWidth: 2,
|
||||||
|
borderDash: [5, 5]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -725,6 +857,19 @@
|
|||||||
return test && test.deptName ? test.deptName : 'Laboratory Department';
|
return test && test.deptName ? test.deptName : 'Laboratory Department';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get departmentDisplayName() {
|
||||||
|
const dept = this.departmentName;
|
||||||
|
const mapping = {
|
||||||
|
'Kimia': 'Kimia Klinik',
|
||||||
|
'Imun': 'Imunologi',
|
||||||
|
'Hema': 'Hematologi',
|
||||||
|
'Mikro': 'Mikrobiologi',
|
||||||
|
'Urinal': 'Urinalisis',
|
||||||
|
'Serolo': 'Serologi'
|
||||||
|
};
|
||||||
|
return mapping[dept] || dept;
|
||||||
|
},
|
||||||
|
|
||||||
get monthDisplay() {
|
get monthDisplay() {
|
||||||
if (!this.month) return '';
|
if (!this.month) return '';
|
||||||
const [year, month] = this.month.split('-');
|
const [year, month] = this.month.split('-');
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -1,170 +0,0 @@
|
|||||||
USE [master]
|
|
||||||
GO
|
|
||||||
/****** Object: Database [cmod_qc] Script Date: 17/01/2026 18:26:32 ******/
|
|
||||||
CREATE DATABASE [cmod_qc]
|
|
||||||
CONTAINMENT = NONE
|
|
||||||
ON PRIMARY
|
|
||||||
( NAME = N'cmod_qc', FILENAME = N'C:\db\cmod_qc.mdf' , SIZE = 8192KB , MAXSIZE = UNLIMITED, FILEGROWTH = 1024KB )
|
|
||||||
LOG ON
|
|
||||||
( NAME = N'cmod_qc_log', FILENAME = N'C:\db\cmod_qc_log.ldf' , SIZE = 32448KB , MAXSIZE = 2048GB , FILEGROWTH = 10%)
|
|
||||||
WITH CATALOG_COLLATION = DATABASE_DEFAULT, LEDGER = OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET COMPATIBILITY_LEVEL = 110
|
|
||||||
GO
|
|
||||||
IF (1 = FULLTEXTSERVICEPROPERTY('IsFullTextInstalled'))
|
|
||||||
begin
|
|
||||||
EXEC [cmod_qc].[dbo].[sp_fulltext_database] @action = 'enable'
|
|
||||||
end
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ANSI_NULL_DEFAULT OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ANSI_NULLS OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ANSI_PADDING OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ANSI_WARNINGS OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ARITHABORT OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET AUTO_CLOSE OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET AUTO_SHRINK OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET AUTO_UPDATE_STATISTICS ON
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET CURSOR_CLOSE_ON_COMMIT OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET CURSOR_DEFAULT GLOBAL
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET CONCAT_NULL_YIELDS_NULL OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET NUMERIC_ROUNDABORT OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET QUOTED_IDENTIFIER OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET RECURSIVE_TRIGGERS OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET DISABLE_BROKER
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET AUTO_UPDATE_STATISTICS_ASYNC OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET DATE_CORRELATION_OPTIMIZATION OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET TRUSTWORTHY OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ALLOW_SNAPSHOT_ISOLATION OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET PARAMETERIZATION SIMPLE
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET READ_COMMITTED_SNAPSHOT OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET HONOR_BROKER_PRIORITY OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET RECOVERY SIMPLE
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET MULTI_USER
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET PAGE_VERIFY CHECKSUM
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET DB_CHAINING OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET FILESTREAM( NON_TRANSACTED_ACCESS = OFF )
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET TARGET_RECOVERY_TIME = 0 SECONDS
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET DELAYED_DURABILITY = DISABLED
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET ACCELERATED_DATABASE_RECOVERY = OFF
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET QUERY_STORE = OFF
|
|
||||||
GO
|
|
||||||
USE [cmod_qc]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[CONTROL_TEST] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[CONTROL_TEST](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[controlid] [int] NULL,
|
|
||||||
[testid] [int] NULL,
|
|
||||||
[mean] [float] NULL,
|
|
||||||
[sd] [float] NULL
|
|
||||||
) ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[DAILY_RESULT] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[DAILY_RESULT](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[controlid] [int] NULL,
|
|
||||||
[testid] [int] NULL,
|
|
||||||
[resdate] [datetime] NULL,
|
|
||||||
[resvalue] [varchar](50) NULL,
|
|
||||||
[rescomment] [text] NULL
|
|
||||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[DICT_CONTROL] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[DICT_CONTROL](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[deptid] [int] NULL,
|
|
||||||
[name] [varchar](50) NULL,
|
|
||||||
[lot] [varchar](50) NULL,
|
|
||||||
[producer] [text] NULL,
|
|
||||||
[expdate] [date] NULL
|
|
||||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[DICT_DEPT] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[DICT_DEPT](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[name] [varchar](50) NULL,
|
|
||||||
CONSTRAINT [PK_DICT_DEPT] PRIMARY KEY CLUSTERED
|
|
||||||
(
|
|
||||||
[id] ASC
|
|
||||||
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
|
|
||||||
) ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[DICT_TEST] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[DICT_TEST](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[deptid] [int] NULL,
|
|
||||||
[name] [varchar](50) NULL,
|
|
||||||
[unit] [varchar](50) NULL,
|
|
||||||
[method] [varchar](50) NULL,
|
|
||||||
[cva] [varchar](50) NULL,
|
|
||||||
[ba] [varchar](50) NULL,
|
|
||||||
[tea] [varchar](50) NULL
|
|
||||||
) ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
/****** Object: Table [dbo].[MONTHLY_COMMENT] Script Date: 17/01/2026 18:26:33 ******/
|
|
||||||
SET ANSI_NULLS ON
|
|
||||||
GO
|
|
||||||
SET QUOTED_IDENTIFIER ON
|
|
||||||
GO
|
|
||||||
CREATE TABLE [dbo].[MONTHLY_COMMENT](
|
|
||||||
[id] [int] IDENTITY(1,1) NOT NULL,
|
|
||||||
[controlid] [int] NOT NULL,
|
|
||||||
[testid] [int] NOT NULL,
|
|
||||||
[commonth] [varchar](7) NOT NULL,
|
|
||||||
[comtext] [text] NULL
|
|
||||||
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
|
|
||||||
GO
|
|
||||||
USE [master]
|
|
||||||
GO
|
|
||||||
ALTER DATABASE [cmod_qc] SET READ_WRITE
|
|
||||||
GO
|
|
||||||
Loading…
x
Reference in New Issue
Block a user