feat: auto-select sample when scanning sample number

- Add support for scanning sample numbers (8 digits) in addition to access numbers (10 digits)
- Backend: Detect input type based on length, extract sample code and access suffix
- Frontend: Auto-select matching sample and highlight with primary ring border
- When sample number scanned (e.g., 10135026), extracts sample code (101) and auto-checks it
This commit is contained in:
mahdahar 2026-03-10 15:52:11 +07:00
parent 2b1cccd2be
commit e5eac13036
3 changed files with 42 additions and 16 deletions

View File

@ -125,3 +125,8 @@ language_backend:
# list of regex patterns which, when matched, mark a memory entry as readonly.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:

View File

@ -9,11 +9,32 @@ class SamplesController extends BaseController
{
use ResponseTrait;
public function show($accessnumber)
public function show($input)
{
$db = \Config\Database::connect();
$sql = "SELECT right(p.PATNUMBER,16) as [patnumber], ISNULL(p.FIRSTNAME,'') + ' ' + ISNULL(p.NAME,'') as [Name],
$scannedSampleCode = '';
if (strlen($input) == 10) {
$accessnumber = $input;
} else {
$redacc = substr($input, 3, 5);
$scannedSampleCode = substr($input, 0, 3);
$sql = "SELECT TOP 1 SPR.SP_ACCESSNUMBER
FROM SP_REQUESTS SPR
WHERE SPR.SP_ACCESSNUMBER LIKE '%$redacc' AND YEAR(SPR.REQDATE) = YEAR(GETDATE())
ORDER BY SPR.REQDATE DESC";
$query = $db->query($sql);
$row = $query->getRowArray();
$accessnumber = $row['SP_ACCESSNUMBER'] ?? null;
if (!$accessnumber) {
return $this->response->setJSON(['data' => null]);
}
}
$sql = "SELECT right(p.PATNUMBER,16) as [patnumber], ISNULL(p.FIRSTNAME,'') + ' ' + ISNULL(p.NAME,'') as [Name],
case when format(p.BIRTHDATE,'MMdd')=format(spr.COLLECTIONDATE,'MMdd') then DATEDIFF(YEAR,p.BIRTHDATE, spr.COLLECTIONDATE)
else FLOOR(DATEDIFF(DAY, p.BIRTHDATE, spr.COLLECTIONDATE) / 365.25) end ,
[Gender] = case
@ -38,6 +59,7 @@ class SamplesController extends BaseController
'ktp' => $results['DMG_CKTPNO'] ?? '',
'comment' => $results['COMMENTTEXT'] ?? '',
'accessnumber' => $accessnumber,
'scannedSampleCode' => $scannedSampleCode,
];
$samples = [];

View File

@ -100,7 +100,10 @@ $roleConfig = $config['phlebo'];
</thead>
<tbody>
<template x-for="sample in samples" :key="sample.sampcode">
<tr :class="sample.selected ? (sample.colstatus == 1 ? 'bg-success/10' : 'bg-warning/10') : ''">
<tr :class="[
sample.selected ? (sample.colstatus == 1 ? 'bg-success/10' : 'bg-warning/10') : '',
isScannedSample(sample) ? 'ring-2 ring-primary ring-inset' : ''
]">
<td class="text-center p-2">
<input type="checkbox"
class="checkbox checkbox-xs checkbox-primary"
@ -173,6 +176,7 @@ $roleConfig = $config['phlebo'];
isLoading: false,
isSaving: false,
searched: false,
scannedSampleCode: '',
init() {
this.$nextTick(() => {
@ -203,10 +207,11 @@ $roleConfig = $config['phlebo'];
comment: data.data.comment || ''
};
this.comment = data.data.comment || '';
this.scannedSampleCode = data.data.scannedSampleCode || '';
this.samples = (data.data.samples || []).map(s => ({
...s,
selected: s.colstatus == 1
selected: s.colstatus == 1 || (this.scannedSampleCode && s.sampcode === this.scannedSampleCode)
}));
} else {
this.patient = {};
@ -224,6 +229,10 @@ $roleConfig = $config['phlebo'];
return this.samples.some(s => s.selected && s.colstatus != 1);
},
isScannedSample(sample) {
return this.scannedSampleCode && sample.sampcode === this.scannedSampleCode;
},
hasChanges() {
const toCollect = this.samples.some(s => s.selected && s.colstatus != 1);
const toUncollect = this.samples.some(s => !s.selected && s.colstatus == 1);
@ -294,10 +303,7 @@ $roleConfig = $config['phlebo'];
const totalChanged = samplesToCollect.length + samplesToUncollect.length;
this.showToast(`Updated ${totalChanged} sample(s)`, 'success');
setTimeout(() => {
this.resetForm();
}, 1000);
this.resetForm();
} catch (error) {
console.error('Error:', error);
this.showToast('Save failed', 'error');
@ -322,14 +328,7 @@ $roleConfig = $config['phlebo'];
},
resetForm() {
this.accessnumber = '';
this.patient = {};
this.samples = [];
this.comment = '';
this.searched = false;
this.$nextTick(() => {
this.$refs.accessInput.focus();
});
setTimeout(() => window.location.reload(), 1500);
},
showToast(message, type = 'success') {