diff --git a/backend/api/prisma/migrations/20251126085837_add_fk_obat_rekam_medis_tindakan_and_split_record_id_to_str_and_int/migration.sql b/backend/api/prisma/migrations/20251126085837_add_fk_obat_rekam_medis_tindakan_and_split_record_id_to_str_and_int/migration.sql new file mode 100644 index 0000000..fbdc9e0 --- /dev/null +++ b/backend/api/prisma/migrations/20251126085837_add_fk_obat_rekam_medis_tindakan_and_split_record_id_to_str_and_int/migration.sql @@ -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; diff --git a/backend/api/src/modules/rekammedis/rekammedis.service.ts b/backend/api/src/modules/rekammedis/rekammedis.service.ts index a1196a0..fc3d7d9 100644 --- a/backend/api/src/modules/rekammedis/rekammedis.service.ts +++ b/backend/api/src/modules/rekammedis/rekammedis.service.ts @@ -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 }, diff --git a/backend/api/src/modules/tindakandokter/tindakandokter.controller.ts b/backend/api/src/modules/tindakandokter/tindakandokter.controller.ts index eff4049..f308829 100644 --- a/backend/api/src/modules/tindakandokter/tindakandokter.controller.ts +++ b/backend/api/src/modules/tindakandokter/tindakandokter.controller.ts @@ -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); + } } diff --git a/backend/api/src/modules/tindakandokter/tindakandokter.service.ts b/backend/api/src/modules/tindakandokter/tindakandokter.service.ts index 290b760..a90fb8f 100644 --- a/backend/api/src/modules/tindakandokter/tindakandokter.service.ts +++ b/backend/api/src/modules/tindakandokter/tindakandokter.service.ts @@ -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(); } diff --git a/backend/api/src/modules/validation/validation.service.ts b/backend/api/src/modules/validation/validation.service.ts index bc13d4a..c7f6045 100644 --- a/backend/api/src/modules/validation/validation.service.ts +++ b/backend/api/src/modules/validation/validation.service.ts @@ -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) => { diff --git a/frontend/hospital-log/src/views/dashboard/pemberian-tindakan/TindakanView.vue b/frontend/hospital-log/src/views/dashboard/pemberian-tindakan/TindakanView.vue index 68e3332..0f09bf6 100644 --- a/frontend/hospital-log/src/views/dashboard/pemberian-tindakan/TindakanView.vue +++ b/frontend/hospital-log/src/views/dashboard/pemberian-tindakan/TindakanView.vue @@ -29,6 +29,7 @@ interface ApiResponse { const data = ref([]); const searchIdVisit = ref(""); const sortBy = ref("id"); +const isDeleteSuccess = ref(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 () => { + +