first commit

This commit is contained in:
2025-11-26 07:49:54 +00:00
commit d8685ccf10
468 changed files with 41346 additions and 0 deletions
@@ -0,0 +1,227 @@
<template>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<span class="text-h6">NAKES & JADWAL</span>
<v-btn variant="text" color="primary" size="small" @click="resetSection">
Atur Ulang
</v-btn>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" md="6">
<v-select
v-model="localData.paymentMethod"
:items="paymentMethods"
label="Pembiayaan"
variant="outlined"
density="compact"
:rules="[rules.required]"
append-inner-icon="mdi-chevron-down"
>
<template v-slot:label>
Pembiayaan <span class="text-red">*</span>
</template>
</v-select>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="localData.referralNumber"
label="Nomor Rujukan (Opsional)"
variant="outlined"
density="compact"
placeholder="Nomor Rujukan"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-select
v-model="localData.polyclinicName"
:items="polyclinics"
label="Nama Poliklinik"
variant="outlined"
density="compact"
placeholder="Pilih"
:rules="[rules.required]"
@update:modelValue="onPolyclinicChange"
append-inner-icon="mdi-chevron-down"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-select
v-model="localData.doctorName"
:items="doctors"
label="Nama Nakes"
variant="outlined"
density="compact"
placeholder="Pilih"
:rules="[rules.required]"
:disabled="!localData.polyclinicName"
@update:modelValue="onDoctorChange"
append-inner-icon="mdi-chevron-down"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="localData.consultationDate"
label="Tanggal Konsultasi"
variant="outlined"
density="compact"
type="date"
:rules="[rules.required]"
:min="minDate"
>
<template v-slot:label>
Tanggal Konsultasi <span class="text-red">*</span>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="localData.consultationTime"
:items="timeSlots"
label="Jam Konsultasi"
variant="outlined"
density="compact"
placeholder="Pilih Jam Konsultasi"
:rules="[rules.required]"
:disabled="!localData.consultationDate || !localData.doctorName"
append-inner-icon="mdi-clock-outline"
>
<template v-slot:label>
Jam Konsultasi <span class="text-red">*</span>
</template>
</v-select>
</v-col>
</v-row>
<v-row v-if="localData.doctorName && localData.consultationDate">
<v-col cols="12">
<v-alert type="info" variant="tonal" density="compact">
Slot Nakes: {{ availableSlots }} slot tersedia
</v-alert>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
<script setup>
import { ref, watch, computed } from "vue";
const props = defineProps({
scheduleData: {
type: Object,
required: true
}
});
const emit = defineEmits(["update:scheduleData", "update"]);
const localData = ref({ ...props.scheduleData });
const paymentMethods = ["Pribadi", "BPJS", "Asuransi Swasta", "Perusahaan"];
const polyclinics = [
"Poli Umum",
"Poli Gigi",
"Poli Anak",
"Poli Kandungan",
"Poli Penyakit Dalam",
"Poli THT"
];
const doctors = ref([]);
const timeSlots = ref([]);
const availableSlots = ref(0);
const minDate = computed(() => {
const today = new Date();
return today.toISOString().split("T")[0];
});
const rules = {
required: (value) => !!value || "Field ini wajib diisi"
};
const onPolyclinicChange = (value) => {
// Reset doctor and time when polyclinic changes
localData.value.doctorName = "";
localData.value.consultationTime = "";
// Simulate loading doctors based on polyclinic
if (value) {
doctors.value = [
"dr. Ahmad Subhan, Sp.A",
"dr. Siti Nurhaliza",
"dr. Budi Santoso, Sp.PD",
"dr. Maria Christina"
];
}
};
const onDoctorChange = (value) => {
// Reset time when doctor changes
localData.value.consultationTime = "";
// Simulate loading time slots
if (value && localData.value.consultationDate) {
loadTimeSlots();
}
};
const loadTimeSlots = () => {
// Simulate available time slots
timeSlots.value = [
{ title: "08:00 - 08:30", value: "08:00" },
{ title: "08:30 - 09:00", value: "08:30" },
{ title: "09:00 - 09:30", value: "09:00" },
{ title: "09:30 - 10:00", value: "09:30" },
{ title: "10:00 - 10:30", value: "10:00" },
{ title: "10:30 - 11:00", value: "10:30" }
];
availableSlots.value = 6;
};
const resetSection = () => {
localData.value = {
paymentMethod: "Pribadi",
referralNumber: "",
polyclinicName: "",
doctorName: "",
consultationDate: "",
consultationTime: "",
slotAvailable: true
};
doctors.value = [];
timeSlots.value = [];
};
watch(
localData,
(newVal) => {
emit("update:scheduleData", newVal);
emit("update");
},
{ deep: true }
);
watch(
() => localData.value.consultationDate,
(newVal) => {
if (newVal && localData.value.doctorName) {
loadTimeSlots();
}
}
);
</script>
@@ -0,0 +1,249 @@
<template>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<span class="text-h6">DATA PASIEN</span>
<v-btn variant="text" color="primary" size="small" @click="resetSection">
Atur Ulang
</v-btn>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12">
<v-text-field
v-model="localData.name"
label="Nama Pasien"
variant="outlined"
density="compact"
placeholder="Masukkan Nama Pasien"
:rules="[rules.required]"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="localData.medicalRecordNumber"
label="No. Rekam medis"
variant="outlined"
density="compact"
placeholder="MRN Pasien"
:rules="[rules.required]"
/>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="localData.category"
:items="patientCategories"
label="Kategori Pasien"
variant="outlined"
density="compact"
placeholder="Pilih Kategori Pasien"
append-inner-icon="mdi-chevron-down"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-text-field
v-model="localData.birthDate"
label="Tanggal Lahir"
variant="outlined"
density="compact"
type="date"
placeholder="Tanggal Lahir (DD/MM/YYYY)"
:rules="[rules.required]"
>
<template v-slot:label>
Tanggal Lahir <span class="text-red">*</span>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="localData.gender"
:items="genderOptions"
label="Jenis Kelamin"
variant="outlined"
density="compact"
placeholder="Pilih"
:rules="[rules.required]"
append-inner-icon="mdi-chevron-down"
>
<template v-slot:label>
Jenis Kelamin <span class="text-red">*</span>
</template>
</v-select>
</v-col>
</v-row>
<v-row>
<v-col cols="12">
<v-textarea
v-model="localData.address"
label="Alamat"
variant="outlined"
density="compact"
rows="3"
placeholder="Alamat"
:rules="[rules.required]"
counter="100"
>
<template v-slot:label>
Alamat <span class="text-red">*</span>
</template>
</v-textarea>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="2">
<v-select
v-model="localData.phoneCode"
:items="phoneCodes"
label="Kode"
variant="outlined"
density="compact"
:rules="[rules.required]"
>
<template v-slot:label>
Kode <span class="text-red">*</span>
</template>
</v-select>
</v-col>
<v-col cols="12" md="5">
<v-text-field
v-model="localData.phoneNumber"
label="Nomor Telepon"
variant="outlined"
density="compact"
placeholder="Nomor Telepon"
:rules="[rules.required, rules.phone]"
>
<template v-slot:label>
Nomor Telepon <span class="text-red">*</span>
</template>
</v-text-field>
</v-col>
<v-col cols="12" md="5">
<v-text-field
v-model="localData.email"
label="Email"
variant="outlined"
density="compact"
placeholder="Email"
:rules="[rules.email]"
/>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-select
v-model="localData.identityType"
:items="identityTypes"
label="Jenis Identitas"
variant="outlined"
density="compact"
placeholder="Pilih"
:rules="[rules.required]"
append-inner-icon="mdi-chevron-down"
>
<template v-slot:label>
Jenis Identitas <span class="text-red">*</span>
</template>
</v-select>
</v-col>
<v-col cols="12" md="6">
<v-text-field
v-model="localData.identityNumber"
label="Nomor Identitas"
variant="outlined"
density="compact"
placeholder="Nomor Identitas"
:rules="[rules.required]"
>
<template v-slot:label>
Nomor Identitas <span class="text-red">*</span>
</template>
</v-text-field>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
<script setup>
import { ref, watch } from "vue";
const props = defineProps({
patientData: {
type: Object,
required: true
}
});
const emit = defineEmits(["update:patientData", "update"]);
const localData = ref({ ...props.patientData });
const patientCategories = ["Umum", "BPJS", "Asuransi Lain"];
const genderOptions = [
{ title: "Laki-laki", value: "L" },
{ title: "Perempuan", value: "P" }
];
const phoneCodes = ["+62", "+65", "+60", "+1"];
const identityTypes = [
{ title: "KTP", value: "ktp" },
{ title: "SIM", value: "sim" },
{ title: "Paspor", value: "paspor" },
{ title: "Kartu Pelajar", value: "kartu_pelajar" }
];
const rules = {
required: (value) => !!value || "Field ini wajib diisi",
email: (value) => {
const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return !value || pattern.test(value) || "Email tidak valid";
},
phone: (value) => {
const pattern = /^[0-9]{8,15}$/;
return pattern.test(value) || "Nomor telepon tidak valid";
}
};
const resetSection = () => {
localData.value = {
name: "",
medicalRecordNumber: "",
category: "",
birthDate: "",
gender: "",
address: "",
phoneCode: "+62",
phoneNumber: "",
email: "",
identityType: "",
identityNumber: ""
};
};
watch(
localData,
(newVal) => {
emit("update:patientData", newVal);
emit("update");
},
{ deep: true }
);
</script>
@@ -0,0 +1,106 @@
<template>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<span class="text-h6">JENIS PENDAFTARAN</span>
<v-btn variant="text" color="primary" size="small" @click="resetSection">
Atur Ulang
</v-btn>
</v-card-title>
<v-card-text>
<v-row>
<v-col cols="12" md="6">
<div class="mb-2">
<span class="text-subtitle-2">
Jenis Kunjungan <span class="text-red">*</span>
</span>
</div>
<v-radio-group
v-model="localData.visitType"
:rules="[rules.required]"
inline
>
<v-radio label="Kunjungan Sakit" value="sakit" />
<v-radio label="Kunjungan Sehat" value="sehat" />
</v-radio-group>
</v-col>
</v-row>
<v-row>
<v-col cols="12" md="6">
<v-select
v-model="localData.treatmentType"
:items="treatmentTypes"
label="Jenis Perawatan"
variant="outlined"
density="compact"
:rules="[rules.required]"
append-inner-icon="mdi-chevron-down"
>
<template v-slot:label>
Jenis Perawatan <span class="text-red">*</span>
</template>
</v-select>
</v-col>
<v-col cols="12" md="6">
<v-select
v-model="localData.triage"
:items="triageOptions"
label="Triase"
variant="outlined"
density="compact"
placeholder="Pilih"
clearable
append-inner-icon="mdi-chevron-down"
/>
</v-col>
</v-row>
</v-card-text>
</v-card>
</template>
<script setup>
import { ref, watch } from "vue";
const props = defineProps({
registrationData: {
type: Object,
required: true
}
});
const emit = defineEmits(["update:registrationData", "update"]);
const localData = ref({ ...props.registrationData });
const treatmentTypes = ["Rawat Jalan", "Rawat Inap", "IGD"];
const triageOptions = [
{ title: "Merah (Emergency)", value: "merah" },
{ title: "Kuning (Urgent)", value: "kuning" },
{ title: "Hijau (Less Urgent)", value: "hijau" },
{ title: "Hitam (Non Urgent)", value: "hitam" }
];
const rules = {
required: (value) => !!value || "Field ini wajib diisi"
};
const resetSection = () => {
localData.value = {
visitType: "",
treatmentType: "Rawat Jalan",
triage: ""
};
};
watch(
localData,
(newVal) => {
emit("update:registrationData", newVal);
emit("update");
},
{ deep: true }
);
</script>
@@ -0,0 +1,159 @@
<template>
<v-card>
<v-card-title class="d-flex justify-space-between align-center">
<div class="d-flex align-center gap-2">
<span class="text-h6">SKALA RISIKO JATUH (OPSIONAL)</span>
<v-icon color="warning" size="small">mdi-alert-box</v-icon>
</div>
<v-btn variant="text" color="primary" size="small" @click="resetSection">
Atur Ulang
</v-btn>
</v-card-title>
<v-expansion-panels v-model="panel" class="mt-2">
<v-expansion-panel>
<v-expansion-panel-title>
<span class="font-weight-medium"
>SKALA RISIKO JATUH - GET UP AND GO</span
>
</v-expansion-panel-title>
<v-expansion-panel-text>
<v-card flat>
<v-card-text>
<!-- Walking Ability Question -->
<div class="mb-6">
<div class="mb-3">
<p class="text-subtitle-1 font-weight-medium mb-1">
Cara berjalan pasien:
</p>
<ul class="ml-4">
<li>Jalan tidak seimbang, sempoyongan, atau limbung</li>
<li>
Menggunakan alat bantu (kruk, tripot, kursi roda, orang
lain)
</li>
</ul>
</div>
<v-radio-group
v-model="localData.walkingAbility"
inline
@update:modelValue="calculateScore"
>
<v-radio label="Ya" :value="true" />
<v-radio label="Tidak" :value="false" />
</v-radio-group>
</div>
<!-- Sitting Support Question -->
<div class="mb-6">
<div class="mb-3">
<p class="text-subtitle-1 font-weight-medium mb-1">
Menopang saat akan duduk
</p>
<p class="text-body-2 text-grey-darken-1">
Tampak memegang sandaran kursi atau meja / benda lain
sebagai penopang saat akan duduk
</p>
</div>
<v-radio-group
v-model="localData.supportWhileSitting"
inline
@update:modelValue="calculateScore"
>
<v-radio label="Ya" :value="true" />
<v-radio label="Tidak" :value="false" />
</v-radio-group>
</div>
<!-- Score Display -->
<v-divider class="my-4" />
<div class="d-flex justify-space-between align-center">
<span class="text-subtitle-1 font-weight-medium">Skor</span>
<v-chip :color="scoreColor" variant="tonal" size="large">
{{ localData.score }}
</v-chip>
</div>
<!-- Risk Level Alert -->
<v-alert
v-if="riskLevel"
:type="riskLevel.type"
variant="tonal"
density="compact"
class="mt-4"
>
{{ riskLevel.message }}
</v-alert>
</v-card-text>
</v-card>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</v-card>
</template>
<script setup>
import { ref, watch, computed } from "vue";
const props = defineProps({
riskData: {
type: Object,
required: true
}
});
const emit = defineEmits(["update:riskData", "update"]);
const localData = ref({ ...props.riskData });
const panel = ref(0);
const calculateScore = () => {
let score = 0;
if (localData.value.walkingAbility === true) score += 1;
if (localData.value.supportWhileSitting === true) score += 1;
localData.value.score = score;
};
const scoreColor = computed(() => {
if (localData.value.score === 0) return "success";
if (localData.value.score === 1) return "warning";
return "error";
});
const riskLevel = computed(() => {
if (localData.value.score === 0) {
return {
type: "success",
message: "Risiko Rendah: Tidak berisiko jatuh"
};
} else if (localData.value.score === 1) {
return {
type: "warning",
message: "Risiko Sedang: Memerlukan pengawasan"
};
} else if (localData.value.score === 2) {
return {
type: "error",
message: "Risiko Tinggi: Memerlukan bantuan dan pengawasan ketat"
};
}
return null;
});
const resetSection = () => {
localData.value = {
walkingAbility: null,
supportWhileSitting: null,
score: 0
};
};
watch(
localData,
(newVal) => {
emit("update:riskData", newVal);
emit("update");
},
{ deep: true }
);
</script>