Add uncollect feature and security improvements
- Add DELETE /api/samples/collect/:accessnumber/:samplenumber endpoint - Convert SQL queries to parameterized queries for security - Add uncollect() method to SamplesController - Update collect view to support uncollecting samples - Fix checkbox logic to allow toggling collected samples - Update hasChanges() to detect both collect and uncollect actions
This commit is contained in:
parent
ee68d22d15
commit
c2d66d0082
@ -52,6 +52,7 @@ $routes->get('(:any)/audit', 'ApiRequestsAuditController::show/$1');
|
|||||||
// Collect & Show - All Roles
|
// Collect & Show - All Roles
|
||||||
$routes->group('', ['filter' => 'role:0,1,2,3,4'], function ($routes) {
|
$routes->group('', ['filter' => 'role:0,1,2,3,4'], function ($routes) {
|
||||||
$routes->post('collect/(:any)/(:any)', 'SamplesController::collect/$1/$2');
|
$routes->post('collect/(:any)/(:any)', 'SamplesController::collect/$1/$2');
|
||||||
|
$routes->delete('collect/(:any)/(:any)', 'SamplesController::uncollect/$1/$2');
|
||||||
$routes->get('(:any)', 'SamplesController::show/$1');
|
$routes->get('(:any)', 'SamplesController::show/$1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -68,14 +68,26 @@ class SamplesController extends BaseController
|
|||||||
{
|
{
|
||||||
$db = \Config\Database::connect();
|
$db = \Config\Database::connect();
|
||||||
$userid = session('userid');
|
$userid = session('userid');
|
||||||
$sql = "update GDC_CMOD.dbo.TUBES set USERID='$userid',STATUS='1', COLLECTIONDATE=getdate() where ACCESSNUMBER='$accessnumber' and TUBENUMBER='$samplenumber'";
|
$sql = "update GDC_CMOD.dbo.TUBES set USERID=?, STATUS='1', COLLECTIONDATE=getdate() where ACCESSNUMBER=? and TUBENUMBER=?";
|
||||||
$db->query($sql);
|
$db->query($sql, [$userid, $accessnumber, $samplenumber]);
|
||||||
$sql = "INSERT INTO GDC_CMOD.dbo.AUDIT_TUBES(ACCESSNUMBER, TUBENUMBER, USERID, STATUS, LOGDATE)
|
$sql = "INSERT INTO GDC_CMOD.dbo.AUDIT_TUBES(ACCESSNUMBER, TUBENUMBER, USERID, STATUS, LOGDATE)
|
||||||
VALUES ('$accessnumber', '$samplenumber', '$userid', '1', getdate())";
|
VALUES (?, ?, ?, '1', getdate())";
|
||||||
$db->query($sql);
|
$db->query($sql, [$accessnumber, $samplenumber, $userid]);
|
||||||
return $this->respondCreated(['status' => 'success', 'message' => 'Data updated successfully', 'data' => "$accessnumber-$samplenumber"], 201);
|
return $this->respondCreated(['status' => 'success', 'message' => 'Data updated successfully', 'data' => "$accessnumber-$samplenumber"], 201);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function uncollect($accessnumber, $samplenumber)
|
||||||
|
{
|
||||||
|
$db = \Config\Database::connect();
|
||||||
|
$userid = session('userid');
|
||||||
|
$sql = "update GDC_CMOD.dbo.TUBES set STATUS='0', COLLECTIONDATE=NULL where ACCESSNUMBER=? and TUBENUMBER=?";
|
||||||
|
$db->query($sql, [$accessnumber, $samplenumber]);
|
||||||
|
$sql = "INSERT INTO GDC_CMOD.dbo.AUDIT_TUBES(ACCESSNUMBER, TUBENUMBER, USERID, STATUS, LOGDATE)
|
||||||
|
VALUES (?, ?, ?, '0', getdate())";
|
||||||
|
$db->query($sql, [$accessnumber, $samplenumber, $userid]);
|
||||||
|
return $this->respond(['status' => 'success', 'message' => 'Sample uncollected successfully', 'data' => "$accessnumber-$samplenumber"], 200);
|
||||||
|
}
|
||||||
|
|
||||||
public function unreceive($accessnumber, $samplenumber)
|
public function unreceive($accessnumber, $samplenumber)
|
||||||
{
|
{
|
||||||
$db = \Config\Database::connect();
|
$db = \Config\Database::connect();
|
||||||
|
|||||||
@ -100,13 +100,11 @@ $roleConfig = $config['phlebo'];
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="sample in samples" :key="sample.sampcode">
|
<template x-for="sample in samples" :key="sample.sampcode">
|
||||||
<tr :class="sample.colstatus == 1 ? 'bg-success/10' : (sample.selected ? 'bg-warning/10' : '')">
|
<tr :class="sample.selected ? (sample.colstatus == 1 ? 'bg-success/10' : 'bg-warning/10') : ''">
|
||||||
<td class="text-center p-2">
|
<td class="text-center p-2">
|
||||||
<input type="checkbox"
|
<input type="checkbox"
|
||||||
class="checkbox checkbox-xs checkbox-primary"
|
class="checkbox checkbox-xs checkbox-primary"
|
||||||
x-model="sample.selected"
|
x-model="sample.selected">
|
||||||
:checked="sample.colstatus == 1"
|
|
||||||
:disabled="sample.colstatus == 1">
|
|
||||||
</td>
|
</td>
|
||||||
<td class="font-mono text-xs p-2" x-text="sample.sampcode"></td>
|
<td class="font-mono text-xs p-2" x-text="sample.sampcode"></td>
|
||||||
<td class="text-xs p-2" x-text="sample.name"></td>
|
<td class="text-xs p-2" x-text="sample.name"></td>
|
||||||
@ -141,7 +139,7 @@ $roleConfig = $config['phlebo'];
|
|||||||
</button>
|
</button>
|
||||||
<button class="btn btn-sm btn-primary"
|
<button class="btn btn-sm btn-primary"
|
||||||
@click="saveCollection()"
|
@click="saveCollection()"
|
||||||
:disabled="isSaving || !hasSelectedSamples()"
|
:disabled="isSaving || !hasChanges()"
|
||||||
:class="isSaving ? 'loading' : ''">
|
:class="isSaving ? 'loading' : ''">
|
||||||
<i class="fa fa-save mr-1" x-show="!isSaving"></i>
|
<i class="fa fa-save mr-1" x-show="!isSaving"></i>
|
||||||
<span x-text="isSaving ? 'Saving' : 'Save'"></span>
|
<span x-text="isSaving ? 'Saving' : 'Save'"></span>
|
||||||
@ -227,6 +225,12 @@ $roleConfig = $config['phlebo'];
|
|||||||
return this.samples.some(s => s.selected && s.colstatus != 1);
|
return this.samples.some(s => s.selected && s.colstatus != 1);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
hasChanges() {
|
||||||
|
const toCollect = this.samples.some(s => s.selected && s.colstatus != 1);
|
||||||
|
const toUncollect = this.samples.some(s => !s.selected && s.colstatus == 1);
|
||||||
|
return toCollect || toUncollect;
|
||||||
|
},
|
||||||
|
|
||||||
hasUncollectedSamples() {
|
hasUncollectedSamples() {
|
||||||
return this.samples.some(s => s.colstatus != 1 && !s.selected);
|
return this.samples.some(s => s.colstatus != 1 && !s.selected);
|
||||||
},
|
},
|
||||||
@ -271,15 +275,17 @@ $roleConfig = $config['phlebo'];
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const samplesToCollect = this.samples.filter(s => s.selected && s.colstatus != 1);
|
const samplesToCollect = this.samples.filter(s => s.selected && s.colstatus != 1);
|
||||||
|
const samplesToUncollect = this.samples.filter(s => !s.selected && s.colstatus == 1);
|
||||||
|
|
||||||
for (const sample of samplesToCollect) {
|
for (const sample of samplesToCollect) {
|
||||||
await fetch(`${BASEURL}/api/samples/collect/${this.patient.accessnumber}`, {
|
await fetch(`${BASEURL}/api/samples/collect/${this.patient.accessnumber}/${sample.sampcode}`, {
|
||||||
method: 'POST',
|
method: 'POST'
|
||||||
headers: { 'Content-Type': 'application/json' },
|
});
|
||||||
body: JSON.stringify({
|
}
|
||||||
samplenumber: sample.sampcode,
|
|
||||||
userid: this.userid
|
for (const sample of samplesToUncollect) {
|
||||||
})
|
await fetch(`${BASEURL}/api/samples/collect/${this.patient.accessnumber}/${sample.sampcode}`, {
|
||||||
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,10 +293,11 @@ $roleConfig = $config['phlebo'];
|
|||||||
await this.saveComment();
|
await this.saveComment();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showToast(`Collected ${samplesToCollect.length} sample(s)`, 'success');
|
const totalChanged = samplesToCollect.length + samplesToUncollect.length;
|
||||||
|
this.showToast(`Updated ${totalChanged} sample(s)`, 'success');
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.resetForm();
|
this.fetchPatientData();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user