feat(sep): modify schema

This commit is contained in:
riefive
2025-10-16 13:02:18 +07:00
parent ba79251d36
commit f4534c25c5
4 changed files with 167 additions and 122 deletions
+89 -84
View File
@@ -1,23 +1,28 @@
<script setup lang="ts">
// helpers
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
import { IntegrationBpjsSchema, type IntegrationBpjsFormData } from '~/schemas/integration-bpjs.schema'
// components (use doc-entry block pattern)
// Components
import Block from '~/components/pub/my-ui/doc-entry/block.vue'
import Cell from '~/components/pub/my-ui/doc-entry/cell.vue'
import Field from '~/components/pub/my-ui/doc-entry/field.vue'
import Label from '~/components/pub/my-ui/doc-entry/label.vue'
import { Button } from '~/components/pub/ui/button'
import { Input } from '~/components/pub/ui/input'
import Select from '~/components/pub/ui/select/Select.vue'
import { RadioGroup, RadioGroupItem } from '~/components/pub/ui/radio-group'
import { Textarea } from '~/components/pub/ui/textarea'
import Select from '~/components/pub/ui/select/Select.vue'
import DatepickerSingle from '~/components/pub/my-ui/datepicker/datepicker-single.vue'
// Types
import { IntegrationBpjsSchema, type IntegrationBpjsFormData } from '~/schemas/integration-bpjs.schema'
import type { PatientEntity } from "~/models/patient"
// Helpers
import { toTypedSchema } from '@vee-validate/zod'
import { useForm } from 'vee-validate'
const props = defineProps<{
isLoading?: boolean
isReadonly?: boolean
patient?: PatientEntity | null
values?: any
}>()
@@ -40,22 +45,22 @@ const { handleSubmit, errors, defineField, meta } = useForm<IntegrationBpjsFormD
})
// Bind fields and extract attrs for consistent Field pattern
const [tanggalSep, tanggalSepAttrs] = defineField('tanggalSep')
const [jalur, jalurAttrs] = defineField('jalur')
const [noBpjs, noBpjsAttrs] = defineField('noBpjs')
const [noKtp, noKtpAttrs] = defineField('noKtp')
const [noRm, noRmAttrs] = defineField('noRm')
const [namaPasien, namaPasienAttrs] = defineField('namaPasien')
const [noTelp, noTelpAttrs] = defineField('noTelp')
const [noSuratKontrol, noSuratKontrolAttrs] = defineField('noSuratKontrol')
const [tglSuratKontrol, tglSuratKontrolAttrs] = defineField('tglSuratKontrol')
const [klinikTujuan, klinikTujuanAttrs] = defineField('klinikTujuan')
const [dpjp, dpjpAttrs] = defineField('dpjp')
const [diagnosaAwal, diagnosaAwalAttrs] = defineField('diagnosaAwal')
const [sepDate, sepDateAttrs] = defineField('sepDate')
const [admissionType, admissionTypeAttrs] = defineField('admissionType')
const [bpjsNumber, bpjsNumberAttrs] = defineField('bpjsNumber')
const [nationalId, nationalIdAttrs] = defineField('nationalId')
const [medicalRecordNumber, medicalRecordNumberAttrs] = defineField('medicalRecordNumber')
const [patientName, patientNameAttrs] = defineField('patientName')
const [phoneNumber, phoneNumberAttrs] = defineField('phoneNumber')
const [referralLetterNumber, referralLetterNumberAttrs] = defineField('referralLetterNumber')
const [referralLetterDate, referralLetterDateAttrs] = defineField('referralLetterDate')
const [destinationClinic, destinationClinicAttrs] = defineField('destinationClinic')
const [attendingDoctor, attendingDoctorAttrs] = defineField('attendingDoctor')
const [initialDiagnosis, initialDiagnosisAttrs] = defineField('initialDiagnosis')
const [cob, cobAttrs] = defineField('cob')
const [katarak, katarakAttrs] = defineField('katarak')
const [jenisProsedur, jenisProsedurAttrs] = defineField('jenisProsedur')
const [kodePenunjang, kodePenunjangAttrs] = defineField('kodePenunjang')
const [cataract, cataractAttrs] = defineField('cataract')
const [procedureType, procedureTypeAttrs] = defineField('procedureType')
const [supportCode, supportCodeAttrs] = defineField('supportCode')
// Submit handler
const onSubmit = handleSubmit((values) => {
@@ -81,11 +86,11 @@ const onSubmit = handleSubmit((values) => {
Tanggal SEP
<span class="ml-1 text-red-500">*</span>
</Label>
<Field :errMessage="errors.tanggalSep">
<Field :errMessage="errors.sepDate">
<DatepickerSingle
id="tanggalSep"
v-model="tanggalSep"
v-bind="tanggalSepAttrs"
id="sepDate"
v-model="sepDate"
v-bind="sepDateAttrs"
:disabled="isLoading || isReadonly"
placeholder="Pilih tanggal sep"
/>
@@ -93,12 +98,12 @@ const onSubmit = handleSubmit((values) => {
</Cell>
<Cell>
<Label height="compact">Jalur</Label>
<Field :errMessage="errors.jalur">
<Field :errMessage="errors.admissionType">
<Select
id="jalur"
id="admissionType"
icon-name="i-lucide-chevron-down"
v-model="jalur"
v-bind="jalurAttrs"
v-model="admissionType"
v-bind="admissionTypeAttrs"
:items="items"
:disabled="isLoading || isReadonly"
placeholder="Pilih jalur"
@@ -136,11 +141,11 @@ const onSubmit = handleSubmit((values) => {
No. Kartu BPJS
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.noBpjs">
<Field :errMessage="errors.bpjsNumber">
<Input
id="noBpjs"
v-model="noBpjs"
v-bind="noBpjsAttrs"
id="bpjsNumber"
v-model="bpjsNumber"
v-bind="bpjsNumberAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -150,11 +155,11 @@ const onSubmit = handleSubmit((values) => {
No. KTP
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.noKtp">
<Field :errMessage="errors.nationalId">
<Input
id="noKtp"
v-model="noKtp"
v-bind="noKtpAttrs"
id="nationalId"
v-model="nationalId"
v-bind="nationalIdAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -164,11 +169,11 @@ const onSubmit = handleSubmit((values) => {
No. RM
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.noRm">
<Field :errMessage="errors.medicalRecordNumber">
<Input
id="noRm"
v-model="noRm"
v-bind="noRmAttrs"
id="medicalRecordNumber"
v-model="medicalRecordNumber"
v-bind="medicalRecordNumberAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -178,11 +183,11 @@ const onSubmit = handleSubmit((values) => {
Nama Pasien
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.namaPasien">
<Field :errMessage="errors.patientName">
<Input
id="namaPasien"
v-model="namaPasien"
v-bind="namaPasienAttrs"
id="patientName"
v-model="patientName"
v-bind="patientNameAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -192,11 +197,11 @@ const onSubmit = handleSubmit((values) => {
No. Telepon
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.noTelp">
<Field :errMessage="errors.phoneNumber">
<Input
id="noTelp"
v-model="noTelp"
v-bind="noTelpAttrs"
id="phoneNumber"
v-model="phoneNumber"
v-bind="phoneNumberAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -218,13 +223,13 @@ const onSubmit = handleSubmit((values) => {
No. Surat Kontrol
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.noSuratKontrol">
<Field :errMessage="errors.referralLetterNumber">
<div class="flex gap-2">
<Input
id="noSuratKontrol"
id="referralLetterNumber"
class="flex-1"
v-model="noSuratKontrol"
v-bind="noSuratKontrolAttrs"
v-model="referralLetterNumber"
v-bind="referralLetterNumberAttrs"
:disabled="isLoading || isReadonly"
/>
<Button
@@ -246,11 +251,11 @@ const onSubmit = handleSubmit((values) => {
Tanggal Surat Kontrol
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.tglSuratKontrol">
<Field :errMessage="errors.referralLetterDate">
<DatepickerSingle
id="tglSuratKontrol"
v-model="tglSuratKontrol"
v-bind="tglSuratKontrolAttrs"
id="referralLetterDate"
v-model="referralLetterDate"
v-bind="referralLetterDateAttrs"
:disabled="isLoading || isReadonly"
placeholder="Pilih tanggal surat kontrol"
/>
@@ -261,12 +266,12 @@ const onSubmit = handleSubmit((values) => {
Klinik Tujuan
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.klinikTujuan">
<Field :errMessage="errors.destinationClinic">
<Select
id="klinikTujuan"
id="destinationClinic"
icon-name="i-lucide-chevron-down"
v-model="klinikTujuan"
v-bind="klinikTujuanAttrs"
v-model="destinationClinic"
v-bind="destinationClinicAttrs"
:items="items"
:disabled="isLoading || isReadonly"
placeholder="Pilih klinik"
@@ -308,12 +313,12 @@ const onSubmit = handleSubmit((values) => {
DPJP
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.dpjp">
<Field :errMessage="errors.attendingDoctor">
<Select
id="dpjp"
id="attendingDoctor"
icon-name="i-lucide-chevron-down"
v-model="dpjp"
v-bind="dpjpAttrs"
v-model="attendingDoctor"
v-bind="attendingDoctorAttrs"
:items="items"
:disabled="isLoading || isReadonly"
placeholder="Pilih DPJP"
@@ -326,11 +331,11 @@ const onSubmit = handleSubmit((values) => {
Diagnosa Awal
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.diagnosaAwal">
<Field :errMessage="errors.initialDiagnosis">
<Input
id="diagnosaAwal"
v-model="diagnosaAwal"
v-bind="diagnosaAwalAttrs"
id="initialDiagnosis"
v-model="initialDiagnosis"
v-bind="initialDiagnosisAttrs"
:disabled="isLoading || isReadonly"
/>
</Field>
@@ -347,7 +352,7 @@ const onSubmit = handleSubmit((values) => {
<Label height="dynamic">Catatan</Label>
<Field>
<Textarea
id="catatan"
id="note"
class="h-[200px] w-full border-gray-400 bg-white"
placeholder="Masukkan catatan opsional"
:disabled="isLoading || isReadonly"
@@ -389,25 +394,25 @@ const onSubmit = handleSubmit((values) => {
Katarak
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.katarak">
<Field :errMessage="errors.cataract">
<RadioGroup
v-model="katarak"
v-model="cataract"
class="flex items-center gap-2"
v-bind="katarakAttrs"
v-bind="cataractAttrs"
>
<div class="flex items-center space-x-2">
<RadioGroupItem
value="Ya"
id="katarak-ya"
id="cataract-yes"
/>
<Label for="katarak-ya">Ya</Label>
<Label for="cataract-yes">Ya</Label>
</div>
<div class="flex items-center space-x-2">
<RadioGroupItem
value="Tidak"
id="katarak-tidak"
id="cataract-no"
/>
<Label for="katarak-tidak">Tidak</Label>
<Label for="cataract-no">Tidak</Label>
</div>
</RadioGroup>
</Field>
@@ -418,11 +423,11 @@ const onSubmit = handleSubmit((values) => {
Jenis Prosedur
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.jenisProsedur">
<Field :errMessage="errors.procedureType">
<RadioGroup
v-model="jenisProsedur"
v-model="procedureType"
class="flex items-center gap-2"
v-bind="jenisProsedurAttrs"
v-bind="procedureTypeAttrs"
>
<div class="flex items-center space-x-2">
<RadioGroupItem
@@ -454,12 +459,12 @@ const onSubmit = handleSubmit((values) => {
Kode Penunjang
<span class="text-red-500">*</span>
</Label>
<Field :errMessage="errors.kodePenunjang">
<Field :errMessage="errors.supportCode">
<Select
id="kodePenunjang"
id="supportCode"
icon-name="i-lucide-chevron-down"
v-model="kodePenunjang"
v-bind="kodePenunjangAttrs"
v-model="supportCode"
v-bind="supportCodeAttrs"
:items="items"
:disabled="isLoading || isReadonly"
placeholder="Pilih Kode Penunjang"
@@ -1,9 +1,6 @@
<script setup lang="ts">
import { ref } from 'vue'
// Helpers
import { refDebounced } from '@vueuse/core'
// Components
import {
Dialog,
@@ -21,15 +18,12 @@ import PaginationView from '~/components/pub/my-ui/pagination/pagination-view.vu
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
// Helpers
import { refDebounced } from '@vueuse/core'
const props = defineProps<{
open: boolean
patients: Array<{
id: string
identity: string
number: string
bpjs: string
name: string
}>
patients: Array<{ id: string; identity: string; number: string; bpjs: string; name: string }>
selected: string
paginationMeta: PaginationMeta
}>()
+11 -12
View File
@@ -1,9 +1,10 @@
<script setup lang="ts">
import { id } from "date-fns/locale"
import { id } from 'date-fns/locale'
import { ref } from 'vue'
// Types
import type { PaginationMeta } from '~/components/pub/my-ui/pagination/pagination.type'
import type { PatientEntity } from '~/models/patient'
// Services
import { getPatientDetail, getPatients } from '~/services/patient.service'
@@ -12,12 +13,11 @@ const openPatient = ref(false)
const openLetter = ref(false)
const openHistory = ref(false)
const selectedPatient = ref('')
const selectedPatientObject = ref<PatientEntity | null>(null)
const selectedLetter = ref('SK22334442')
// patients used by AppSepTableSearchPatient (will be filled from API)
const patients = ref<Array<{ id: string; identity: string; number: string; bpjs: string; name: string }>>([
// fallback empty list until fetched
])
const patients = ref<Array<{ id: string; identity: string; number: string; bpjs: string; name: string }>>([])
const isPatientsLoading = ref(false)
const paginationMeta = ref<PaginationMeta>({
recordCount: 0,
@@ -28,12 +28,12 @@ const paginationMeta = ref<PaginationMeta>({
hasPrev: false,
})
function mapPatientToRow(patient: any) {
function mapPatientToRow(patient: PatientEntity) {
// Defensive mapping: try common field names that might be returned by the API
const identity = patient?.person?.residentIdentityNumber || '-'
const number = patient?.number || patient?.medicalRecordNo || '-'
const bpjs = patient?.person?.refNumber || '-'
const name = patient.name || patient?.person?.name || '-'
const number = patient?.number || '-'
const bpjs = '-'
const name = patient?.person?.name || '-'
return { id: patient.id ? String(patient.id) : '-', identity, number, bpjs, name }
}
@@ -117,13 +117,12 @@ const histories = [
]
function handleSavePatient() {
console.log('Pasien dipilih:', selectedPatient.value)
const getPatient = async () => {
try {
const result = await getPatientDetail(Number(selectedPatient.value))
if (result && result.success && result.body && result.body.data) {
const patient = result.body.data
console.log('Patient:', patient)
const patient = result.body.data || null
selectedPatientObject.value = patient
}
} catch (err) {
console.error('Failed to fetch patient:', err)
@@ -167,7 +166,7 @@ function handleEvent(value: string) {
<span class="font-semibold">Tambah</span>
SEP
</div>
<AppSepEntryForm @event="handleEvent" />
<AppSepEntryForm :patient="selectedPatientObject" @event="handleEvent" />
<AppSepTableSearchPatient
v-model:open="openPatient"
v-model:selected="selectedPatient"
+63 -16
View File
@@ -1,22 +1,69 @@
import { z } from 'zod'
const ERROR_MESSAGES = {
required: {
sepDate: 'Tanggal wajib diisi',
admissionType: 'Jenis Pendaftaran wajib diisi',
bpjsNumber: 'No. Kartu BPJS wajib diisi',
nationalId: 'Nomor ID wajib diisi',
medicalRecordNumber: 'Nomor Rekam Medis wajib diisi',
patientName: 'Nama wajib diisi',
phoneNumber: 'Nomor Telepon wajib diisi',
referralLetterNumber: 'Nomor Surat Kontrol wajib diisi',
referralLetterDate: 'Tanggal Surat Kontrol wajib diisi',
destinationClinic: 'Klinik Tujuan wajib diisi',
attendingDoctor: 'Dokter wajib diisi',
initialDiagnosis: 'Diagnosa Awal wajib diisi',
cob: 'COB wajib diisi',
cataract: 'Katarak wajib diisi',
procedureType: 'Jenis Prosedur wajib diisi',
supportCode: 'Kode Penunjang wajib diisi',
},
}
const IntegrationBpjsSchema = z.object({
tanggalSep: z.string().min(1, 'Tanggal SEP wajib diisi'),
jalur: z.string().min(1, 'Pilih jalur'),
noBpjs: z.string({ required_error: 'No. Kartu BPJS wajib diisi' }).min(1, 'No. Kartu BPJS wajib diisi'),
noKtp: z.string().min(1, 'No. KTP wajib diisi'),
noRm: z.string().min(1, 'No. RM wajib diisi'),
namaPasien: z.string().min(1, 'Nama pasien wajib diisi'),
noTelp: z.string().min(1, 'Nomor telepon wajib diisi'),
noSuratKontrol: z.string().min(1, 'No. Surat Kontrol wajib diisi'),
tglSuratKontrol: z.string().min(1, 'Tanggal Surat Kontrol wajib diisi'),
klinikTujuan: z.string().min(1, 'Klinik tujuan wajib diisi'),
dpjp: z.string().min(1, 'DPJP wajib diisi'),
diagnosaAwal: z.string().min(1, 'Diagnosa awal wajib diisi'),
cob: z.string().min(1, 'COB wajib diisi'),
katarak: z.string().min(1, 'Katarak wajib diisi'),
jenisProsedur: z.string().min(1, 'Jenis prosedur wajib diisi'),
kodePenunjang: z.string().min(1, 'Kode penunjang wajib diisi'),
sepDate: z.string({ required_error: ERROR_MESSAGES.required.sepDate }).min(1, ERROR_MESSAGES.required.sepDate),
admissionType: z
.string({ required_error: ERROR_MESSAGES.required.admissionType })
.min(1, ERROR_MESSAGES.required.admissionType),
bpjsNumber: z
.string({ required_error: ERROR_MESSAGES.required.bpjsNumber })
.min(1, ERROR_MESSAGES.required.bpjsNumber),
nationalId: z
.string({ required_error: ERROR_MESSAGES.required.nationalId })
.min(1, ERROR_MESSAGES.required.nationalId),
medicalRecordNumber: z
.string({ required_error: ERROR_MESSAGES.required.medicalRecordNumber })
.min(1, ERROR_MESSAGES.required.medicalRecordNumber),
patientName: z
.string({ required_error: ERROR_MESSAGES.required.patientName })
.min(1, ERROR_MESSAGES.required.patientName),
phoneNumber: z
.string({ required_error: ERROR_MESSAGES.required.phoneNumber })
.min(1, ERROR_MESSAGES.required.phoneNumber),
referralLetterNumber: z
.string({ required_error: ERROR_MESSAGES.required.referralLetterNumber })
.min(1, ERROR_MESSAGES.required.referralLetterNumber),
referralLetterDate: z
.string({ required_error: ERROR_MESSAGES.required.referralLetterDate })
.min(1, ERROR_MESSAGES.required.referralLetterDate),
destinationClinic: z
.string({ required_error: ERROR_MESSAGES.required.destinationClinic })
.min(1, ERROR_MESSAGES.required.destinationClinic),
attendingDoctor: z
.string({ required_error: ERROR_MESSAGES.required.attendingDoctor })
.min(1, ERROR_MESSAGES.required.attendingDoctor),
initialDiagnosis: z
.string({ required_error: ERROR_MESSAGES.required.initialDiagnosis })
.min(1, ERROR_MESSAGES.required.initialDiagnosis),
cob: z.string({ required_error: ERROR_MESSAGES.required.cob }).min(1, ERROR_MESSAGES.required.cob),
cataract: z.string({ required_error: ERROR_MESSAGES.required.cataract }).min(1, ERROR_MESSAGES.required.cataract),
procedureType: z
.string({ required_error: ERROR_MESSAGES.required.procedureType })
.min(1, ERROR_MESSAGES.required.procedureType),
supportCode: z
.string({ required_error: ERROR_MESSAGES.required.supportCode })
.min(1, ERROR_MESSAGES.required.supportCode),
})
type IntegrationBpjsFormData = z.infer<typeof IntegrationBpjsSchema>