wip: data masih dead pixel upload sukses

This commit is contained in:
Khafid Prayoga
2025-10-15 16:37:41 +07:00
parent 4976916780
commit 20b96ab7e4
7 changed files with 86 additions and 27 deletions
+5 -3
View File
@@ -3,7 +3,7 @@ import type { FormErrors } from '~/types/error'
import { toTypedSchema } from '@vee-validate/zod'
import { Form } from '~/components/pub/ui/form'
import InputBase from '~/components/pub/my-ui/form/input-base.vue'
import FileUpload from '~/components/pub/my-ui/form/file-upload.vue'
import FileUpload from '~/components/pub/my-ui/form/file-field.vue'
import InputName from './_common/input-name.vue'
import RadioCommunicationBarrier from './_common/radio-communication-barrier.vue'
import RadioDisability from './_common/radio-disability.vue'
@@ -144,20 +144,22 @@ defineExpose({
</div>
<div class="grid grid-cols-1 md:grid-cols-2">
<FileUpload
field-name="identityCardFile"
field-name="residentIdentityFile"
label="Dokumen KTP"
placeholder="Unggah scan dokumen KTP"
:errors="errors"
:accept="['pdf', 'jpg', 'png']"
:max-size-mb="1"
@update:model-value="values.residentIdentityFile = $event"
/>
<FileUpload
field-name="familyCardFile"
field-name="familyIdentityFile"
label="Dokumen KK"
placeholder="Unggah scan dokumen KK"
:errors="errors"
:accept="['pdf', 'jpg', 'png']"
:max-size-mb="1"
@update:model-value="values.familyIdentityFile = $event"
/>
</div>
</div>
-13
View File
@@ -184,19 +184,6 @@ function onClick(type: string) {
<DetailRow label="Hubungan">
{{ relationshipOptions.find((item) => item.code === relative.relationship_code)?.label || '-' }}
</DetailRow>
<DetailRow label="Jenis Kelamin">
{{ genderOptions.find((item) => item.code === relative.gender_code)?.label || '-' }}
</DetailRow>
<DetailRow label="Pendidikan">
{{ educationOptions.find((item) => item.code === relative.education_code)?.label || '-' }}
</DetailRow>
<DetailRow label="Pekerjaan">
{{
occupationOptions.find((item) => item.code === relative.occupation_code)?.label ||
relative.occupation_name ||
'-'
}}
</DetailRow>
<DetailRow label="Alamat">{{ relative.address || '-' }}</DetailRow>
<DetailRow label="Nomor HP">{{ relative.phoneNumber || '-' }}</DetailRow>
</template>
+17 -4
View File
@@ -10,7 +10,8 @@ import { PersonAddressSchema } from '~/schemas/person-address.schema'
import { PersonContactListSchema } from '~/schemas/person-contact.schema'
import { PersonFamiliesSchema } from '~/schemas/person-family.schema'
import { ResponsiblePersonSchema } from '~/schemas/person-relative.schema'
import { postPatient } from '~/services/patient.service'
import { postPatient, uploadAttachment } from '~/services/patient.service'
import { uploadCode } from '~/lib/constants'
// #region Props & Emits
const props = defineProps<{
@@ -97,13 +98,26 @@ async function sendRequest() {
try {
const result = await postPatient(formData)
const patientData: PatientBase = result.body
const patientData: PatientBase = result.body.data
if (result.success) {
console.log('Patient created successfully:', patientData)
const createdPatientId = patientData.id
// void run uploadAttachment in background so this try-catch non blocking
// behavior: fire-and-forget
if (patient?.values.residentIdentityFile) {
void uploadAttachment(patient?.values.residentIdentityFile, createdPatientId, 'ktp')
}
if (patient?.values.familyIdentityFile) {
void uploadAttachment(patient?.values.familyIdentityFile, createdPatientId, 'kk')
}
// 30s
await new Promise((r) => setTimeout(r, 30_000))
// If has callback provided redirect to callback with patientData
if (props.callbackUrl) {
await navigateTo(props.callbackUrl + '?patient-id=' + patientData.person_id)
await navigateTo(props.callbackUrl + '?patient-id=' + patientData.id)
return
}
@@ -115,7 +129,6 @@ async function sendRequest() {
}
} catch (error) {
console.error('Error creating patient:', error)
// Handle error - show error message to user
}
}
// #endregion region
@@ -28,6 +28,13 @@ const hintMsg = computed(() => {
}
return 'Gunakan file yang sesuai'
})
async function onFileChange(event: Event, handleChange: (value: any) => void) {
const target = event.target as HTMLInputElement
const file = target.files?.[0]
handleChange(file)
}
</script>
<template>
@@ -44,20 +51,21 @@ const hintMsg = computed(() => {
:errors="errors"
>
<FormField
v-slot="{ componentField }"
v-slot="{ componentField, handleChange }"
:name="fieldName"
>
<FormItem>
<FormControl>
<Input
<FormControl class="flex flex-col">
<input
@change="onFileChange($event, handleChange)"
type="file"
:disabled="isDisabled"
v-bind="componentField"
:placeholder="placeholder"
:class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0')"
/>
<span class="my-2 text-xs">{{ hintMsg }}</span>
</FormControl>
<span class="my-2 text-xs">{{ hintMsg }}</span>
<FormMessage />
</FormItem>
</FormField>
+4 -1
View File
@@ -327,7 +327,10 @@ export const uploadCode: Record<string, string> = {
kk: 'person-family-card',
paspor: 'person-passport',
'mcu-report': 'mcu-item-result',
}
} as const
export type UploadCodeKey = keyof typeof uploadCode
export type UploadCodeValue = (typeof uploadCode)[UploadCodeKey]
export const infraGroupCodes: Record<string, string> = {
building: 'Bangunan',
+20 -2
View File
@@ -12,6 +12,8 @@ const IsNewBornSchema = z
})
.transform((val) => val === 'YA')
const ACCEPTED_UPLOAD_TYPES = ['image/jpeg', 'image/png', 'application/pdf']
const PatientSchema = z
.object({
// Data Diri Pasien
@@ -21,8 +23,24 @@ const PatientSchema = z
// })
// .min(16, 'NIK harus berupa angka 16 digit')
// .regex(/^\d+$/, 'NIK harus berupa angka 16 digit'),
// identityCardFile: z.instanceof(File, { message: 'File KTP harus dipilih' }),
// familyCardFile: z.instanceof(File, { message: 'File KK harus dipilih' }),
residentIdentityFile: z
.any()
.optional()
.refine((f) => !f || f instanceof File, { message: 'Harus berupa file yang valid' })
.refine((f) => !f || ACCEPTED_UPLOAD_TYPES.includes(f.type), {
message: 'Format file harus JPG, PNG, atau PDF',
})
.refine((f) => !f || f.size <= 1 * 1024 * 1024, { message: 'Maksimal 1MB' }),
familyIdentityFile: z
.any()
.optional()
.refine((f) => !f || f instanceof File, { message: 'Harus berupa file yang valid' })
.refine((f) => !f || ACCEPTED_UPLOAD_TYPES.includes(f.type), {
message: 'Format file harus JPG, PNG, atau PDF',
})
.refine((f) => !f || f.size <= 1 * 1024 * 1024, { message: 'Maksimal 1MB' }),
// .refine(f => ['image/jpeg', 'image/png'].includes(f.type), 'Hanya JPG/PNG')
// Informasi Dasar
// alias: z.string({
+28
View File
@@ -1,4 +1,5 @@
import { xfetch } from '~/composables/useXfetch'
import { uploadCode, type UploadCodeKey } from '~/lib/constants'
const mainUrl = '/api/v1/patient'
@@ -77,3 +78,30 @@ export async function removePatient(id: number) {
throw new Error('Failed to delete patient')
}
}
export async function uploadAttachment(file: File, userId: number, key: UploadCodeKey) {
try {
const resolvedKey = uploadCode[key]
if (!resolvedKey) {
throw new Error(`Invalid upload code key: ${key}`)
}
// siapkan form-data body
const formData = new FormData()
formData.append('code', resolvedKey)
formData.append('content', file)
// kirim via xfetch
const resp = await xfetch(`${mainUrl}/${userId}/upload`, 'POST', formData)
// struktur hasil sama seperti patchPatient
const result: any = {}
result.success = resp.success
result.body = (resp.body as Record<string, any>) || {}
return result
} catch (error) {
console.error('Error uploading attachment:', error)
throw new Error('Failed to upload attachment')
}
}