feat: fix delete validation pending notification at rekam medis, tindakan and obat

This commit is contained in:
yosaphatprs 2025-11-28 16:14:38 +07:00
parent cbcf4ad897
commit 12e190961c
5 changed files with 207 additions and 14 deletions

View File

@ -367,6 +367,14 @@ export class ObatService {
}
async countObat() {
return this.prisma.pemberian_obat.count();
return this.prisma.pemberian_obat.count({
where: {
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
},
});
}
}

View File

@ -563,6 +563,11 @@ export class RekammedisService {
waktu_visit: {
gte: sevenDaysAgo,
},
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
},
_count: {
id_visit: true,
@ -596,6 +601,14 @@ export class RekammedisService {
}
async countRekamMedis() {
return this.prisma.rekam_medis.count();
return this.prisma.rekam_medis.count({
where: {
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
},
});
}
}

View File

@ -102,6 +102,11 @@ export class TindakanDokterService {
kategori_tindakanArray.length > 0
? { in: kategori_tindakanArray }
: undefined,
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
},
});
@ -411,6 +416,14 @@ export class TindakanDokterService {
}
async countTindakanDokter() {
return this.prisma.pemberian_tindakan.count();
return this.prisma.pemberian_tindakan.count({
where: {
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
},
});
}
}

View File

@ -221,19 +221,81 @@ export class ValidationService {
}
}
determineIdType(tableName: string, recordId: string) {
if (tableName === 'rekam_medis') {
return recordId;
} else if (
tableName === 'pemberian_tindakan' ||
tableName === 'pemberian_obat'
) {
return Number(recordId); // numeric ID
} else {
throw new Error('Unsupported table for ID determination');
}
}
async rejectValidation(id: number, user: ActiveUserPayload) {
const validationQueue = await this.getValidationQueueById(id);
if (!validationQueue) {
throw new Error('Validation queue not found');
}
const updated = await this.prisma.validation_queue.update({
where: { id: validationQueue.id },
data: {
status: 'REJECTED',
user_id_process: Number(user.sub),
processed_at: new Date(),
},
});
return updated;
let recordId: number | string = '';
if (
validationQueue.status === 'PENDING' &&
validationQueue.action === 'DELETE'
) {
recordId = this.determineIdType(
validationQueue.table_name,
validationQueue.record_id,
);
}
try {
const rejectedResponse = await this.prisma.$transaction(async (tx) => {
let updatedDeleteStatus = null;
if (validationQueue.action === 'DELETE') {
switch (validationQueue.table_name) {
case 'rekam_medis':
updatedDeleteStatus = await tx.rekam_medis.update({
where: { id_visit: recordId as string },
data: { deleted_status: null },
});
break;
case 'pemberian_tindakan':
updatedDeleteStatus = await tx.pemberian_tindakan.update({
where: { id: recordId as number },
data: { deleted_status: null },
});
break;
case 'pemberian_obat':
updatedDeleteStatus = await tx.pemberian_obat.update({
where: { id: recordId as number },
data: { deleted_status: null },
});
break;
default:
throw new Error('Unsupported table for delete rejection');
}
}
const updatedQueue = await tx.validation_queue.update({
where: { id: validationQueue.id },
data: {
status: 'REJECTED',
user_id_process: Number(user.sub),
processed_at: new Date(),
},
});
return {
...updatedQueue,
updatedDeleteStatus,
};
});
return rejectedResponse;
} catch (error) {
console.error('Error rejecting validation:', (error as Error).message);
throw error;
}
}
}

View File

@ -31,7 +31,6 @@ const hasUserIdProcessColumn = () =>
const formatCellValue = (item: T, columnKey: keyof T) => {
const value = item[columnKey];
if (columnKey === "event" && typeof value === "string") {
const segments = value.split("_");
@ -123,10 +122,12 @@ const handleDeleteCancel = () => {
:key="item.id"
:class="[
'hover:bg-dark hover:text-light transition-colors',
(item as Record<string, any>).isTampered ? 'bg-red-300 text-dark' : ''
(item as Record<string, any>).isTampered ? 'bg-red-300 text-dark' : item.deleted_status ?
item.deleted_status === 'DELETE_VALIDATION' ? 'bg-yellow-100 text-dark' : 'bg-gray-300 text-dark' : '',
]"
>
<td
v-if="item.deleted_status !== 'DELETE_VALIDATION'"
v-for="column in columns"
:key="String(column.key)"
:class="[
@ -147,6 +148,102 @@ const handleDeleteCancel = () => {
Review
</RouterLink>
</td>
<td
v-if="item.deleted_status === 'DELETE_VALIDATION'"
v-for="column in columns"
:key="String(column.key)"
:class="[
column.key === 'txId' || column.key === 'hash'
? 'font-mono overflow-hidden text-ellipsis max-w-10 hover:max-w-150 transition-all duration-500 ease-out text-xs'
: '',
hasStatusColumn() ? 'text-xs' : '',
]"
>
<div
v-if="
column.key === 'id_visit' &&
columns[2]?.key !== 'obat' &&
columns[2]?.key !== 'tindakan'
"
:class="[
column.key === 'id_visit' &&
(columns[2]?.key !== 'obat' || columns[2]?.key !== 'tindakan')
? 'tooltip tooltip-right flex items-center justify-center'
: '',
]"
data-tip="Data ini sedang dalam proses validasi untuk dihapus"
>
<span
:class="[
column.key === 'id_visit' ||
columns[2]?.key !== 'obat' ||
columns[2]?.key !== 'tindakan'
? 'inline-flex items-center gap-1'
: 'hidden',
]"
>
{{ formatCellValue(item, column.key) }}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-3 h-3 mt-1"
>
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 16v-4"></path>
<path d="M12 8h.01"></path>
</svg>
</span>
</div>
<div
v-if="
column.key === 'id' &&
(columns[2]?.key === 'obat' || columns[2]?.key === 'tindakan')
"
:class="[
column.key === 'id' &&
(columns[2]?.key === 'obat' || columns[2]?.key === 'tindakan')
? 'tooltip tooltip-right flex items-center'
: '',
]"
data-tip="Data ini sedang dalam proses validasi untuk dihapus"
>
<span
:class="[
column.key === 'id' ||
columns[2]?.key === 'obat' ||
columns[2]?.key === 'tindakan'
? 'inline-flex items-center gap-1'
: 'hidden',
]"
>
{{ formatCellValue(item, column.key) }}
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
class="w-3 h-3 mt-1"
>
<circle cx="12" cy="12" r="10"></circle>
<path d="M12 16v-4"></path>
<path d="M12 8h.01"></path>
</svg>
</span>
</div>
{{
column.key !== columns[0]?.key
? formatCellValue(item, column.key)
: ""
}}
</td>
<td v-if="!hasStatusColumn()">
<div class="flex gap-2">
<!-- Details Button -->