feat: validation delete for admin on tindakan

This commit is contained in:
yosaphatprs 2025-11-26 16:33:19 +07:00
parent 3b209ca566
commit e1a539325f
6 changed files with 130 additions and 13 deletions

View File

@ -0,0 +1,12 @@
-- AlterTable
ALTER TABLE "validation_queue" ADD COLUMN "integer_record_id" INTEGER DEFAULT 0,
ADD COLUMN "string_record_id" VARCHAR(25) DEFAULT '';
-- AddForeignKey
ALTER TABLE "validation_queue" ADD CONSTRAINT "fk_validation_rekam_medis" FOREIGN KEY ("string_record_id") REFERENCES "rekam_medis"("id_visit") ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE "validation_queue" ADD CONSTRAINT "fk_validation_pemberian_obat" FOREIGN KEY ("integer_record_id") REFERENCES "pemberian_obat"("id") ON DELETE CASCADE ON UPDATE NO ACTION;
-- AddForeignKey
ALTER TABLE "validation_queue" ADD CONSTRAINT "fk_validation_pemberian_tindakan" FOREIGN KEY ("integer_record_id") REFERENCES "pemberian_tindakan"("id") ON DELETE CASCADE ON UPDATE NO ACTION;

View File

@ -486,7 +486,7 @@ export class RekammedisService {
}
}
async deleteRekamMedisFromDB(id_visit: string, user: ActiveUserPayload) {
async deleteRekamMedisFromDB(id_visit: string) {
try {
const deletedRekamMedis = await this.prisma.rekam_medis.delete({
where: { id_visit },

View File

@ -1,6 +1,7 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
@ -79,4 +80,13 @@ export class TindakanDokterController {
async getTindakanLog(@Param('id') id: string) {
return await this.tindakanDokterService.getTindakanLogById(id);
}
@Delete('/:id')
@UseGuards(AuthGuard)
async deleteTindakanDokter(
@Param('id') id: number,
@CurrentUser() user: ActiveUserPayload,
) {
return await this.tindakanDokterService.deleteTindakanDokter(id, user);
}
}

View File

@ -316,6 +316,74 @@ export class TindakanDokterService {
};
}
async deleteTindakanDokter(id: number, user: ActiveUserPayload) {
const tindakanId = Number(id);
if (Number.isNaN(tindakanId)) {
throw new BadRequestException('ID tindakan tidak valid');
}
const existingTindakan = await this.getTindakanDokterById(tindakanId);
if (!existingTindakan) {
throw new BadRequestException(
`Tindakan dokter dengan ID ${id} tidak ditemukan`,
);
}
return this.prisma.validation_queue.create({
data: {
table_name: 'pemberian_tindakan',
action: 'DELETE',
dataPayload: existingTindakan,
record_id: tindakanId.toString(),
user_id_request: user.sub,
status: 'PENDING',
},
});
}
async deleteTindakanDokterFromDBAndBlockchain(id: number, userId: number) {
const tindakanId = Number(id);
if (Number.isNaN(tindakanId)) {
throw new BadRequestException('ID tindakan tidak valid');
}
const existingTindakan = await this.getTindakanDokterById(tindakanId);
if (!existingTindakan) {
throw new BadRequestException(
`Tindakan dokter dengan ID ${tindakanId} tidak ditemukan`,
);
}
try {
const deletedTindakan = await this.prisma.$transaction(async (tx) => {
const deleted = await tx.pemberian_tindakan.delete({
where: { id: tindakanId },
});
const logPayload = JSON.stringify(deleted);
const payloadHash = sha256(logPayload);
const data = {
id: `TINDAKAN_${deleted.id}`,
event: 'tindakan_dokter_deleted',
user_id: userId.toString(),
payload: payloadHash,
};
const createdLog = await this.logService.storeLog(data);
return {
...deleted,
log: createdLog,
};
});
return deletedTindakan;
} catch (error) {
console.error('Error deleting Tindakan Dokter:', error);
throw error;
}
}
async countTindakanDokter() {
return this.prisma.pemberian_tindakan.count();
}

View File

@ -46,9 +46,7 @@ export class ValidationService {
);
},
approveDelete: async (queue: any) => {
return this.rekamMedisService.deleteRekamMedisFromDB(queue.record_id, {
sub: queue.user_id_request.toString(),
} as ActiveUserPayload);
return this.rekamMedisService.deleteRekamMedisFromDB(queue.record_id);
},
},
pemberian_tindakan: {
@ -72,6 +70,12 @@ export class ValidationService {
Number(queue.user_id_request),
);
},
approveDelete: async (queue: any) => {
return this.tindakanDokterService.deleteTindakanDokterFromDBAndBlockchain(
Number(queue.record_id),
queue.user_id_request,
);
},
},
pemberian_obat: {
approveCreate: async (queue: any) => {

View File

@ -29,6 +29,7 @@ interface ApiResponse {
const data = ref<TindakanDokter[]>([]);
const searchIdVisit = ref("");
const sortBy = ref("id");
const isDeleteSuccess = ref<boolean>(false);
const router = useRouter();
const route = useRoute();
@ -182,16 +183,15 @@ const handleUpdate = (item: TindakanDokter) => {
};
const handleDelete = async (item: TindakanDokter) => {
if (
confirm(`Apakah Anda yakin ingin menghapus tindakan "${item.tindakan}"?`)
) {
try {
await api.delete(`/tindakan/${item.id}`);
await fetchData();
} catch (error) {
console.error("Error deleting tindakan:", error);
alert("Gagal menghapus data tindakan");
try {
const result = await api.delete(`/tindakan/${item.id}`);
if (result) {
isDeleteSuccess.value = true;
}
await fetchData();
} catch (error) {
console.error("Error deleting tindakan:", error);
alert("Gagal menghapus data tindakan");
}
};
@ -373,6 +373,29 @@ onMounted(async () => {
</RouterLink>
</div>
<div
role="alert"
class="alert alert-success m-4 shadow-md"
v-if="isDeleteSuccess"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6 shrink-0 stroke-current"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
<span
>Data tindakan berhasil dikirim untuk validasi penghapusan</span
>
</div>
<!-- Data Table -->
<DataTable
:data="data"