Files
general-template/components/apps/medical/UI/DataManager.vue
Yusron alamsyah 6bb6a1d430 first commit
2026-03-13 10:45:28 +07:00

224 lines
6.3 KiB
Vue

<template>
<v-card class="data-manager">
<v-card-title>
<v-icon left>mdi-database</v-icon>
Manajemen Data
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="store.metadata.patientId"
label="ID Pasien"
prepend-icon="mdi-account"
variant="outlined"
density="compact"
@input="store.saveCurrentData"
/>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="store.metadata.dentist"
label="Dokter Gigi"
prepend-icon="mdi-doctor"
variant="outlined"
density="compact"
@input="store.saveCurrentData"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="4">
<v-btn color="success" variant="elevated" block @click="exportData">
<v-icon left>mdi-download</v-icon>
Simpan Data
</v-btn>
</v-col>
<v-col cols="12" md="4">
<v-file-input
ref="fileInput"
accept=".json"
style="display: none"
@change="handleFileImport"
/>
<v-btn color="info" variant="elevated" block @click="importData">
<v-icon left>mdi-upload</v-icon>
Load Data
</v-btn>
</v-col>
<v-col cols="12" md="4">
<v-btn color="error" variant="elevated" block @click="clearAllData">
<v-icon left>mdi-delete-forever</v-icon>
Hapus Semua
</v-btn>
</v-col>
</v-row>
<v-divider class="my-4" />
<div class="conditions-summary">
<h4>Ringkasan Kondisi Gigi ({{ store.conditions.length }})</h4>
<div class="d-flex flex-wrap align-center gap-3">
<v-chip
v-for="condition in store.conditions"
:key="`${condition.toothNumber}-${condition.surface}`"
size="small"
closable
:link="false"
@click:close="removeCondition(condition)"
:color="getSurfaceColor(condition.surface)"
>
{{ condition.toothNumber
}}{{ condition.surface ? "-" + condition.surface : " " }}
<span v-if="condition.mode === 18">
{{
condition.position &&
condition.position.toLowerCase().includes("start")
? "Start(Bridge)"
: ""
}}
{{
condition.position &&
condition.position.toLowerCase().includes("finish")
? "Finish(Bridge)"
: ""
}}
</span>
<span v-else> ({{ getModeLabel(condition.mode) }}) </span>
<!-- <CircleXIcon class="ml-2" start size="20" /> -->
</v-chip>
</div>
</div>
</v-card-text>
</v-card>
</template>
<script setup lang="ts">
import {
MoodSmileIcon,
ChecksIcon,
UserCircleIcon,
CircleXIcon
} from "vue-tabler-icons";
import type { ToothCondition } from "~/types/apps/medical/odontogram";
import { OdontogramMode } from "~/types/apps/medical/odontogram";
import { useOdontogramStore } from "~/store/apps/medical/odontogram";
import { useDataStorage } from "~/composables/apps/medical/useDataStorage";
const store = useOdontogramStore();
const { exportData: exportDataUtil, importData: importDataUtil } =
useDataStorage();
const fileInput = ref();
const modeLabels = {
[OdontogramMode.DEFAULT]: "Normal",
[OdontogramMode.AMF]: "Amalgam",
[OdontogramMode.COF]: "Composite",
[OdontogramMode.FIS]: "Sealant",
[OdontogramMode.NVT]: "Non-vital",
[OdontogramMode.RCT]: "Saluran Akar",
[OdontogramMode.NON]: "Tidak Ada",
[OdontogramMode.UNE]: "Un-Erupted",
[OdontogramMode.PRE]: "Partial-Erupt",
[OdontogramMode.ANO]: "Anomali",
[OdontogramMode.CARIES]: "Caries",
[OdontogramMode.CFR]: "Fraktur",
[OdontogramMode.FMC]: "Metal Crown",
[OdontogramMode.POC]: "Porcelain Crown",
[OdontogramMode.RRX]: "Sisa Akar",
[OdontogramMode.MIS]: "Hilang",
[OdontogramMode.IPX]: "Implant",
[OdontogramMode.FRM_ACR]: "Denture",
[OdontogramMode.BRIDGE]: "Bridge",
[OdontogramMode.ARROW_TOP_LEFT]: "Top Left Arrow",
[OdontogramMode.ARROW_TOP_RIGHT]: "Top Right Arrow",
[OdontogramMode.ARROW_BOTTOM_LEFT]: "Bottom Left Arrow",
[OdontogramMode.ARROW_BOTTOM_RIGHT]: "Bottom Right Arrow",
[OdontogramMode.ARROW_TOP_TURN_LEFT]: "Top Turn Left Arrow",
[OdontogramMode.ARROW_TOP_TURN_RIGHT]: "Top Turn Right Arrow",
[OdontogramMode.ARROW_BOTTOM_TURN_LEFT]: "Bottom Turn Left Arrow",
[OdontogramMode.ARROW_BOTTOM_TURN_RIGHT]: "Bottom Turn Right Arrow",
[OdontogramMode.HAPUS]: "Hapus"
};
const getModeLabel = (mode: OdontogramMode) => {
return modeLabels[mode] || "Unknown";
};
const exportData = () => {
const data = store.exportCurrentData();
exportDataUtil(data);
};
const importData = () => {
fileInput.value?.$el.querySelector("input").click();
};
const handleFileImport = async (event: any) => {
const file = event.target.files?.[0];
if (!file) return;
const data = await importDataUtil(file);
if (data) {
store.importData(data);
// Clear file input
event.target.value = "";
} else {
alert("Error importing data. Please check the file format.");
}
};
const clearAllData = () => {
if (
confirm("Apakah Anda yakin ingin menghapus semua data termasuk metadata?")
) {
store.clearAllConditions();
store.metadata.patientId = "";
store.metadata.dentist = "";
store.metadata.date = new Date().toISOString().split("T")[0];
}
};
const removeCondition = (condition: ToothCondition) => {
store.removeCondition(condition.toothNumber, condition.surface);
};
const getSurfaceColor = (surface: string | undefined) => {
switch (surface) {
case "T":
return "primary"; // Vuetify blue
case "R":
return "success"; // Vuetify green
case "B":
return "error"; // Vuetify red
case "L":
return "info"; // Vuetify yellow
case "M":
return "secondary"; // Custom purple, will add style below
default:
return "secondary";
}
};
</script>
<style scoped>
.data-manager {
margin-bottom: 16px;
}
.conditions-summary {
margin-top: 16px;
}
.conditions-summary h4 {
margin-bottom: 12px;
color: #666;
}
</style>