first commit
This commit is contained in:
236
components/apps/patient/form/AlamatSection.vue
Normal file
236
components/apps/patient/form/AlamatSection.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Alamat Identitas -->
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
ALAMAT IDENTITAS
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<!-- Desa/Kelurahan -->
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="localAlamatIdentitas.desa"
|
||||
label="Desa/Kelurahan"
|
||||
placeholder="Cari desa atau kelurahan..."
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Alamat Lengkap -->
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="localAlamatIdentitas.alamatLengkap"
|
||||
label="Alamat Lengkap"
|
||||
placeholder="Alamat"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
rows="3"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- RT -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatIdentitas.rt"
|
||||
label="RT"
|
||||
placeholder="RT"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="3"
|
||||
maxlength="3"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- RW -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatIdentitas.rw"
|
||||
label="RW"
|
||||
placeholder="RW"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="3"
|
||||
maxlength="3"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Kode Pos -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatIdentitas.kodePos"
|
||||
label="Kode Pos"
|
||||
placeholder="Kode Pos"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="5"
|
||||
maxlength="5"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<!-- Alamat Domisili -->
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
ALAMAT DOMISILI
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<!-- Checkbox Samakan dengan alamat identitas -->
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-checkbox
|
||||
v-model="samaDenganIdentitas"
|
||||
label="Samakan dengan alamat identitas"
|
||||
density="compact"
|
||||
@change="handleSamaDenganIdentitas"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row v-if="!samaDenganIdentitas">
|
||||
<!-- Desa/Kelurahan -->
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="localAlamatDomisili.desa"
|
||||
label="Desa/Kelurahan"
|
||||
placeholder="Cari desa atau kelurahan..."
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
prepend-inner-icon="mdi-magnify"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Alamat Lengkap -->
|
||||
<v-col cols="12">
|
||||
<v-textarea
|
||||
v-model="localAlamatDomisili.alamatLengkap"
|
||||
label="Alamat Lengkap"
|
||||
placeholder="Alamat"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
rows="3"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- RT -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatDomisili.rt"
|
||||
label="RT"
|
||||
placeholder="RT"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="3"
|
||||
maxlength="3"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- RW -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatDomisili.rw"
|
||||
label="RW"
|
||||
placeholder="RW"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="3"
|
||||
maxlength="3"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Kode Pos -->
|
||||
<v-col cols="12" md="4">
|
||||
<v-text-field
|
||||
v-model="localAlamatDomisili.kodePos"
|
||||
label="Kode Pos"
|
||||
placeholder="Kode Pos"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
counter="5"
|
||||
maxlength="5"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
alamatIdentitas: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
alamatDomisili: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
samaDenganIdentitas: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits([
|
||||
"update:alamatIdentitas",
|
||||
"update:alamatDomisili",
|
||||
"update:samaDenganIdentitas"
|
||||
]);
|
||||
|
||||
const localAlamatIdentitas = ref({ ...props.alamatIdentitas });
|
||||
const localAlamatDomisili = ref({ ...props.alamatDomisili });
|
||||
const samaDenganIdentitas = ref(props.samaDenganIdentitas);
|
||||
|
||||
const rules = {
|
||||
required: (v) => !!v || "Field ini wajib diisi"
|
||||
};
|
||||
|
||||
const handleSamaDenganIdentitas = () => {
|
||||
if (samaDenganIdentitas.value) {
|
||||
localAlamatDomisili.value = { ...localAlamatIdentitas.value };
|
||||
} else {
|
||||
localAlamatDomisili.value = {
|
||||
desa: "",
|
||||
alamatLengkap: "",
|
||||
rt: "",
|
||||
rw: "",
|
||||
kodePos: ""
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// Watch for changes
|
||||
watch(
|
||||
localAlamatIdentitas,
|
||||
(newVal) => {
|
||||
emit("update:alamatIdentitas", newVal);
|
||||
if (samaDenganIdentitas.value) {
|
||||
localAlamatDomisili.value = { ...newVal };
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(
|
||||
localAlamatDomisili,
|
||||
(newVal) => {
|
||||
emit("update:alamatDomisili", newVal);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(samaDenganIdentitas, (newVal) => {
|
||||
emit("update:samaDenganIdentitas", newVal);
|
||||
});
|
||||
</script>
|
||||
516
components/apps/patient/form/DataDiriSection.vue
Normal file
516
components/apps/patient/form/DataDiriSection.vue
Normal file
@@ -0,0 +1,516 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- Section Header -->
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
DATA DIRI
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<!-- Checkbox Bayi Baru Lahir -->
|
||||
<v-row>
|
||||
<v-col cols="12" class="text-right py-0">
|
||||
<v-checkbox
|
||||
v-model="isBayiBaru"
|
||||
label="Bayi baru lahir"
|
||||
density="compact"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<!-- Photo Upload -->
|
||||
<v-col cols="12" md="2" class="text-center">
|
||||
<v-avatar size="100" color="grey-lighten-3">
|
||||
<v-icon size="50" color="grey">mdi-account</v-icon>
|
||||
</v-avatar>
|
||||
<v-btn
|
||||
variant="text"
|
||||
color="primary"
|
||||
size="small"
|
||||
class="mt-2"
|
||||
@click="uploadPhoto"
|
||||
>
|
||||
Upload Foto
|
||||
</v-btn>
|
||||
</v-col>
|
||||
|
||||
<!-- Form Fields -->
|
||||
<v-col cols="12" md="10">
|
||||
<v-row>
|
||||
<!-- Nama Lengkap -->
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="localData.namaLengkap"
|
||||
label="Nama Lengkap"
|
||||
placeholder="Nama Lengkap"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
@input="debouncedParseNameToFhir"
|
||||
@paste="handlePaste"
|
||||
/>
|
||||
|
||||
<!-- FHIR Name Preview - Auto shows when typing -->
|
||||
<v-expand-transition>
|
||||
<v-alert
|
||||
v-if="fhirName && localData.namaLengkap && isTypingComplete"
|
||||
density="compact"
|
||||
type="info"
|
||||
variant="tonal"
|
||||
class="mt-2"
|
||||
closable
|
||||
@click:close="hideFhirPreview"
|
||||
>
|
||||
<template v-slot:title>
|
||||
<span class="text-body-2">FHIR Name Structure</span>
|
||||
</template>
|
||||
|
||||
<div class="text-caption mt-2">
|
||||
<v-row dense>
|
||||
<v-col
|
||||
v-if="fhirName.prefix && fhirName.prefix.length > 0"
|
||||
cols="12"
|
||||
>
|
||||
<v-chip
|
||||
size="x-small"
|
||||
color="primary"
|
||||
variant="outlined"
|
||||
class="mr-1"
|
||||
>
|
||||
Prefix
|
||||
</v-chip>
|
||||
<span class="font-weight-medium">{{
|
||||
fhirName.prefix.join(", ")
|
||||
}}</span>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
v-if="fhirName.given && fhirName.given.length > 0"
|
||||
cols="12"
|
||||
>
|
||||
<v-chip
|
||||
size="x-small"
|
||||
color="success"
|
||||
variant="outlined"
|
||||
class="mr-1"
|
||||
>
|
||||
Given
|
||||
</v-chip>
|
||||
<span class="font-weight-medium">{{
|
||||
fhirName.given.join(" ")
|
||||
}}</span>
|
||||
</v-col>
|
||||
|
||||
<v-col v-if="fhirName.family" cols="12">
|
||||
<v-chip
|
||||
size="x-small"
|
||||
color="warning"
|
||||
variant="outlined"
|
||||
class="mr-1"
|
||||
>
|
||||
Family
|
||||
</v-chip>
|
||||
<span class="font-weight-medium">{{
|
||||
fhirName.family
|
||||
}}</span>
|
||||
</v-col>
|
||||
|
||||
<v-col
|
||||
v-if="fhirName.suffix && fhirName.suffix.length > 0"
|
||||
cols="12"
|
||||
>
|
||||
<v-chip
|
||||
size="x-small"
|
||||
color="info"
|
||||
variant="outlined"
|
||||
class="mr-1"
|
||||
>
|
||||
Suffix
|
||||
</v-chip>
|
||||
<span class="font-weight-medium">{{
|
||||
fhirName.suffix.join(", ")
|
||||
}}</span>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</div>
|
||||
</v-alert>
|
||||
</v-expand-transition>
|
||||
|
||||
<!-- Optional: Inline chips preview -->
|
||||
<div
|
||||
v-if="
|
||||
fhirName &&
|
||||
localData.namaLengkap &&
|
||||
isTypingComplete &&
|
||||
!showDetailedPreview
|
||||
"
|
||||
class="mt-2"
|
||||
>
|
||||
<v-chip-group>
|
||||
<v-chip
|
||||
v-for="(prefix, index) in fhirName.prefix || []"
|
||||
:key="`prefix-${index}`"
|
||||
size="x-small"
|
||||
color="primary"
|
||||
>
|
||||
{{ prefix }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-for="(given, index) in fhirName.given || []"
|
||||
:key="`given-${index}`"
|
||||
size="x-small"
|
||||
color="success"
|
||||
>
|
||||
{{ given }}
|
||||
</v-chip>
|
||||
<v-chip v-if="fhirName.family" size="x-small" color="warning">
|
||||
{{ fhirName.family }}
|
||||
</v-chip>
|
||||
<v-chip
|
||||
v-for="(suffix, index) in fhirName.suffix || []"
|
||||
:key="`suffix-${index}`"
|
||||
size="x-small"
|
||||
color="info"
|
||||
>
|
||||
{{ suffix }}
|
||||
</v-chip>
|
||||
</v-chip-group>
|
||||
</div>
|
||||
</v-col>
|
||||
|
||||
<!-- Nomor Telepon Selular -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.nomorTeleponSelular"
|
||||
label="Nomor Telepon Selular"
|
||||
placeholder="Nomor Telepon Selular"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required, rules.phone]"
|
||||
required
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
v-bind="props"
|
||||
class="mr-2"
|
||||
>
|
||||
+62
|
||||
<v-icon end>mdi-menu-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item value="+62">+62</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
|
||||
<!-- Nomor Telepon Rumah -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.nomorTeleponRumah"
|
||||
label="Nomor Telepon Rumah"
|
||||
placeholder="Nomor Telepon Rumah"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
v-bind="props"
|
||||
class="mr-2"
|
||||
>
|
||||
+62
|
||||
<v-icon end>mdi-menu-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item value="+62">+62</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
|
||||
<!-- Email -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.email"
|
||||
label="Email"
|
||||
placeholder="Email"
|
||||
type="email"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.email]"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Jenis Kelamin -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.jenisKelamin"
|
||||
label="Jenis Kelamin"
|
||||
:items="jenisKelaminOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Tempat Lahir -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.tempatLahir"
|
||||
label="Tempat Lahir"
|
||||
placeholder="Tempat Lahir"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Tanggal Lahir -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.tanggalLahir"
|
||||
label="Tanggal Lahir"
|
||||
placeholder="Tanggal Lahir (DD/MM/YYYY)"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
>
|
||||
<template v-slot:append-inner>
|
||||
<v-icon>mdi-calendar</v-icon>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
|
||||
<!-- Jenis Identitas -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.jenisIdentitas"
|
||||
label="Jenis Identitas"
|
||||
:items="jenisIdentitasOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Nomor Identitas -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.nomorIdentitas"
|
||||
label="Nomor Identitas"
|
||||
placeholder="Nomor Identitas"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Nama Ibu Kandung -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.namaIbuKandung"
|
||||
label="Nama Ibu Kandung"
|
||||
placeholder="Nama Lengkap Ibu Kandung"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- No. Rekam Medis -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.nomorRekamMedis"
|
||||
label="No. Rekam Medis Lama / Manual"
|
||||
placeholder="No. Rekam medis"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, computed } from "vue";
|
||||
import { debounce } from "lodash-es"; // or use VueUse's useDebounceFn
|
||||
import {
|
||||
parseFhirHumanName,
|
||||
validateFhirHumanName
|
||||
} from "~/utils/module/fhirNameParser";
|
||||
import type { FhirHumanName } from "~/types/fhir/humanName";
|
||||
|
||||
// Types
|
||||
interface DataDiriForm {
|
||||
photo: File | null;
|
||||
namaLengkap: string;
|
||||
nomorTeleponSelular: string;
|
||||
nomorTeleponRumah: string;
|
||||
email: string;
|
||||
jenisKelamin: string;
|
||||
tempatLahir: string;
|
||||
tanggalLahir: string;
|
||||
jenisIdentitas: string;
|
||||
nomorIdentitas: string;
|
||||
namaIbuKandung: string;
|
||||
nomorRekamMedis: string;
|
||||
fhirName?: FhirHumanName | null;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
formData: DataDiriForm;
|
||||
isBayiBaru: boolean;
|
||||
}
|
||||
|
||||
// Props & Emits
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:formData": [value: DataDiriForm];
|
||||
"update:isBayiBaru": [value: boolean];
|
||||
}>();
|
||||
|
||||
// Reactive data
|
||||
const localData = ref<DataDiriForm>({ ...props.formData });
|
||||
const isBayiBaru = ref(props.isBayiBaru);
|
||||
const fhirName = ref<FhirHumanName | null>(null);
|
||||
const isTypingComplete = ref(false);
|
||||
const showDetailedPreview = ref(true); // Toggle between detailed and chip view
|
||||
|
||||
// Rules
|
||||
const rules = {
|
||||
required: (v: string) => !!v || "Field ini wajib diisi",
|
||||
email: (v: string) => !v || /.+@.+\..+/.test(v) || "Format email tidak valid",
|
||||
phone: (v: string) =>
|
||||
!v || /^[0-9+\-() ]+$/.test(v) || "Format nomor telepon tidak valid"
|
||||
};
|
||||
|
||||
// Options
|
||||
interface SelectOption {
|
||||
title: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
const jenisKelaminOptions: SelectOption[] = [
|
||||
{ title: "Laki-laki", value: "L" },
|
||||
{ title: "Perempuan", value: "P" }
|
||||
];
|
||||
|
||||
const jenisIdentitasOptions: SelectOption[] = [
|
||||
{ title: "KTP", value: "KTP" },
|
||||
{ title: "SIM", value: "SIM" },
|
||||
{ title: "Paspor", value: "PASSPORT" }
|
||||
];
|
||||
|
||||
// Methods
|
||||
import { formatFhirName } from "~/utils/module/fhirNameParser";
|
||||
|
||||
const parseNameToFhir = (): void => {
|
||||
if (localData.value.namaLengkap && localData.value.namaLengkap.trim()) {
|
||||
fhirName.value = parseFhirHumanName(localData.value.namaLengkap);
|
||||
|
||||
if (fhirName.value) {
|
||||
const errors = validateFhirHumanName(fhirName.value);
|
||||
if (errors.length > 0) {
|
||||
console.warn("FHIR Name validation errors:", errors);
|
||||
}
|
||||
|
||||
// Store FHIR name in form data
|
||||
localData.value.fhirName = fhirName.value;
|
||||
|
||||
// Format the FHIR name back to a string and update namaLengkap
|
||||
const formattedName = formatFhirName(fhirName.value);
|
||||
if (formattedName !== localData.value.namaLengkap) {
|
||||
localData.value.namaLengkap = formattedName;
|
||||
}
|
||||
|
||||
// Show preview after parsing
|
||||
isTypingComplete.value = true;
|
||||
}
|
||||
} else {
|
||||
// Clear FHIR name if input is empty
|
||||
fhirName.value = null;
|
||||
localData.value.fhirName = null;
|
||||
isTypingComplete.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Debounced version of parseNameToFhir (wait 500ms after user stops typing)
|
||||
const debouncedParseNameToFhir = debounce(() => {
|
||||
parseNameToFhir();
|
||||
}, 500);
|
||||
|
||||
// Handle paste event
|
||||
const handlePaste = (event: ClipboardEvent): void => {
|
||||
// Reset typing complete flag
|
||||
isTypingComplete.value = false;
|
||||
|
||||
// Parse after a short delay to ensure v-model is updated
|
||||
setTimeout(() => {
|
||||
parseNameToFhir();
|
||||
}, 100);
|
||||
};
|
||||
|
||||
// Hide FHIR preview
|
||||
const hideFhirPreview = (): void => {
|
||||
isTypingComplete.value = false;
|
||||
};
|
||||
|
||||
const uploadPhoto = (): void => {
|
||||
// Implementation for photo upload
|
||||
console.log("Upload photo clicked");
|
||||
};
|
||||
|
||||
// Watchers
|
||||
watch(
|
||||
localData,
|
||||
(newVal) => {
|
||||
emit("update:formData", newVal);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(isBayiBaru, (newVal) => {
|
||||
emit("update:isBayiBaru", newVal);
|
||||
});
|
||||
|
||||
// Reset typing complete when name is cleared
|
||||
watch(
|
||||
() => localData.value.namaLengkap,
|
||||
(newVal) => {
|
||||
if (!newVal) {
|
||||
isTypingComplete.value = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Optional: Add smooth transitions */
|
||||
.v-chip-group {
|
||||
gap: 4px;
|
||||
}
|
||||
</style>
|
||||
100
components/apps/patient/form/KesehatanSection.vue
Normal file
100
components/apps/patient/form/KesehatanSection.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
KESEHATAN
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<!-- Golongan Darah -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.golonganDarah"
|
||||
label="Golongan Darah"
|
||||
:items="golonganDarahOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Status Perokok -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.statusPerokok"
|
||||
label="Status Perokok"
|
||||
:items="statusPerokokOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Jenis Penyakit -->
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
v-model="localData.jenisPenyakit"
|
||||
label="Jenis Penyakit"
|
||||
:items="jenisPenyakitOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
multiple
|
||||
chips
|
||||
closable-chips
|
||||
placeholder="Pilih Jenis Penyakit"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:formData"]);
|
||||
|
||||
const localData = ref({ ...props.formData });
|
||||
|
||||
const golonganDarahOptions = [
|
||||
{ title: "A", value: "A" },
|
||||
{ title: "B", value: "B" },
|
||||
{ title: "AB", value: "AB" },
|
||||
{ title: "O", value: "O" }
|
||||
];
|
||||
|
||||
const statusPerokokOptions = [
|
||||
{ title: "Tidak Merokok", value: "tidak" },
|
||||
{ title: "Perokok Aktif", value: "aktif" },
|
||||
{ title: "Perokok Pasif", value: "pasif" },
|
||||
{ title: "Mantan Perokok", value: "mantan" }
|
||||
];
|
||||
|
||||
const jenisPenyakitOptions = [
|
||||
"Diabetes",
|
||||
"Hipertensi",
|
||||
"Jantung",
|
||||
"Asma",
|
||||
"TBC",
|
||||
"Hepatitis",
|
||||
"HIV/AIDS",
|
||||
"Kanker",
|
||||
"Ginjal",
|
||||
"Stroke"
|
||||
];
|
||||
|
||||
watch(
|
||||
localData,
|
||||
(newVal) => {
|
||||
emit("update:formData", newVal);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
53
components/apps/patient/form/PembayaranSection.vue
Normal file
53
components/apps/patient/form/PembayaranSection.vue
Normal file
@@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-subtitle class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2">
|
||||
PEMBAYARAN
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-select
|
||||
v-model="localData.jenisPembayaran"
|
||||
label="Pembayaran"
|
||||
:items="pembayaranOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
:rules="[rules.required]"
|
||||
required
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:formData'])
|
||||
|
||||
const localData = ref({ ...props.formData })
|
||||
|
||||
const rules = {
|
||||
required: v => !!v || 'Field ini wajib diisi'
|
||||
}
|
||||
|
||||
const pembayaranOptions = [
|
||||
{ title: 'BPJS', value: 'BPJS' },
|
||||
{ title: 'Umum', value: 'UMUM' },
|
||||
{ title: 'Asuransi', value: 'ASURANSI' },
|
||||
{ title: 'Perusahaan', value: 'PERUSAHAAN' }
|
||||
]
|
||||
|
||||
watch(localData, (newVal) => {
|
||||
emit('update:formData', newVal)
|
||||
}, { deep: true })
|
||||
</script>
|
||||
167
components/apps/patient/form/PenanggungJawabSection.vue
Normal file
167
components/apps/patient/form/PenanggungJawabSection.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
PENANGGUNG JAWAB / WALI PASIEN
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<!-- Nama Lengkap -->
|
||||
<v-col cols="12">
|
||||
<v-text-field
|
||||
v-model="localData.namaLengkap"
|
||||
label="Nama Lengkap"
|
||||
placeholder="Nama Lengkap"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Jenis Kelamin -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.jenisKelamin"
|
||||
label="Jenis Kelamin"
|
||||
:items="jenisKelaminOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Hubungan dengan Pasien -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.hubunganDenganPasien"
|
||||
label="Hubungan dengan Pasien"
|
||||
:items="hubunganOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Nomor Telepon -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.nomorTelepon"
|
||||
label="Nomor Telepon Selular / Rumah"
|
||||
placeholder="Nomor Telepon"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
>
|
||||
<template v-slot:prepend>
|
||||
<v-menu>
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
v-bind="props"
|
||||
class="mr-2"
|
||||
>
|
||||
+62
|
||||
<v-icon end>mdi-menu-down</v-icon>
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-list density="compact">
|
||||
<v-list-item value="+62">+62</v-list-item>
|
||||
</v-list>
|
||||
</v-menu>
|
||||
</template>
|
||||
</v-text-field>
|
||||
</v-col>
|
||||
|
||||
<!-- Pekerjaan -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.pekerjaan"
|
||||
label="Pekerjaan"
|
||||
placeholder="Pekerjaan"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Alamat -->
|
||||
<v-col cols="12">
|
||||
<label class="text-subtitle-2 font-weight-medium">Alamat</label>
|
||||
<v-checkbox
|
||||
v-model="samaDenganPasien"
|
||||
label="Samakan dengan alamat pasien"
|
||||
density="compact"
|
||||
@change="handleSamakanAlamat"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<v-col cols="12" v-if="!samaDenganPasien">
|
||||
<v-textarea
|
||||
v-model="localData.alamat"
|
||||
placeholder="Alamat"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
rows="3"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
samaDenganPasien: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
alamatPasien: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:formData", "update:samaDenganPasien"]);
|
||||
|
||||
const localData = ref({ ...props.formData });
|
||||
const samaDenganPasien = ref(props.samaDenganPasien);
|
||||
|
||||
const jenisKelaminOptions = [
|
||||
{ title: "Laki-laki", value: "L" },
|
||||
{ title: "Perempuan", value: "P" }
|
||||
];
|
||||
|
||||
const hubunganOptions = [
|
||||
"Suami",
|
||||
"Istri",
|
||||
"Anak",
|
||||
"Orang Tua",
|
||||
"Saudara",
|
||||
"Kerabat",
|
||||
"Lainnya"
|
||||
];
|
||||
|
||||
const handleSamakanAlamat = () => {
|
||||
if (samaDenganPasien.value && props.alamatPasien) {
|
||||
localData.value.alamat = props.alamatPasien.alamatLengkap || "";
|
||||
} else {
|
||||
localData.value.alamat = "";
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
localData,
|
||||
(newVal) => {
|
||||
emit("update:formData", newVal);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
|
||||
watch(samaDenganPasien, (newVal) => {
|
||||
emit("update:samaDenganPasien", newVal);
|
||||
});
|
||||
</script>
|
||||
145
components/apps/patient/form/SosialSection.vue
Normal file
145
components/apps/patient/form/SosialSection.vue
Normal file
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card-subtitle
|
||||
class="bg-grey-lighten-4 text-subtitle-2 font-weight-medium py-2"
|
||||
>
|
||||
SOSIAL
|
||||
</v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-row>
|
||||
<!-- Agama -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.agama"
|
||||
label="Agama"
|
||||
:items="agamaOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Status Pernikahan -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.statusPernikahan"
|
||||
label="Status Pernikahan"
|
||||
:items="statusPernikahanOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Pendidikan Terakhir -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.pendidikanTerakhir"
|
||||
label="Pendidikan Terakhir"
|
||||
:items="pendidikanOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Pekerjaan -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-select
|
||||
v-model="localData.pekerjaan"
|
||||
label="Pekerjaan"
|
||||
:items="pekerjaanOptions"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Bahasa yang Dikuasai -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.bahasaDikuasai"
|
||||
label="Bahasa yang Dikuasai"
|
||||
placeholder="Bahasa yang Dikuasai"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
|
||||
<!-- Suku/Etnis -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-text-field
|
||||
v-model="localData.sukuEtnis"
|
||||
label="Suku/Etnis"
|
||||
placeholder="Suku/Etnis"
|
||||
variant="outlined"
|
||||
density="compact"
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
formData: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:formData"]);
|
||||
|
||||
const localData = ref({ ...props.formData });
|
||||
|
||||
const agamaOptions = [
|
||||
"Islam",
|
||||
"Kristen",
|
||||
"Katolik",
|
||||
"Hindu",
|
||||
"Buddha",
|
||||
"Konghucu",
|
||||
"Lainnya"
|
||||
];
|
||||
|
||||
const statusPernikahanOptions = [
|
||||
{ title: "Belum Menikah", value: "belum_menikah" },
|
||||
{ title: "Menikah", value: "menikah" },
|
||||
{ title: "Cerai Hidup", value: "cerai_hidup" },
|
||||
{ title: "Cerai Mati", value: "cerai_mati" }
|
||||
];
|
||||
|
||||
const pendidikanOptions = [
|
||||
"Tidak Sekolah",
|
||||
"SD",
|
||||
"SMP",
|
||||
"SMA/SMK",
|
||||
"D1",
|
||||
"D3",
|
||||
"D4/S1",
|
||||
"S2",
|
||||
"S3"
|
||||
];
|
||||
|
||||
const pekerjaanOptions = [
|
||||
"PNS",
|
||||
"TNI/Polri",
|
||||
"Pegawai Swasta",
|
||||
"Wiraswasta",
|
||||
"Petani",
|
||||
"Nelayan",
|
||||
"Buruh",
|
||||
"Ibu Rumah Tangga",
|
||||
"Pelajar/Mahasiswa",
|
||||
"Tidak Bekerja",
|
||||
"Lainnya"
|
||||
];
|
||||
|
||||
watch(
|
||||
localData,
|
||||
(newVal) => {
|
||||
emit("update:formData", newVal);
|
||||
},
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
Reference in New Issue
Block a user