diff --git a/app/components/app/encounter/entry-form.vue b/app/components/app/encounter/entry-form.vue index f684b22f..3598a653 100644 --- a/app/components/app/encounter/entry-form.vue +++ b/app/components/app/encounter/entry-form.vue @@ -547,11 +547,12 @@ defineExpose({ {{ noteFile }} @@ -562,11 +563,12 @@ defineExpose({ {{ noteFile }} diff --git a/app/components/pub/my-ui/form/file-field.vue b/app/components/pub/my-ui/form/file-field.vue index 31885a6f..e71d06c5 100644 --- a/app/components/pub/my-ui/form/file-field.vue +++ b/app/components/pub/my-ui/form/file-field.vue @@ -8,7 +8,7 @@ import { cn } from '~/lib/utils' import * as DE from '~/components/pub/my-ui/doc-entry' -const props = defineProps<{ +interface Props { fieldName: string label?: string placeholder?: string @@ -21,6 +21,19 @@ const props = defineProps<{ isRequired?: boolean isDisabled?: boolean icons?: string +} + +const props = withDefaults(defineProps(), { + label: '', + placeholder: 'Choose file...', + maxSizeMb: 1, + isDisabled: false, + isRequired: false, +}) + +const emit = defineEmits<{ + (e: 'update:modelValue', value: File | null): void + (e: 'fileSelected', file: File | null): void }>() const hintMsg = computed(() => { @@ -35,7 +48,38 @@ async function onFileChange(event: Event, handleChange: (value: any) => void) { const target = event.target as HTMLInputElement const file = target.files?.[0] + if (!file) { + handleChange(null) + emit('update:modelValue', null) + emit('fileSelected', null) + return + } + + // Validate file size + const maxSizeBytes = props.maxSizeMb * 1024 * 1024 + if (file.size > maxSizeBytes) { + console.warn(`File size exceeds ${props.maxSizeMb}MB limit`) + handleChange(null) + emit('update:modelValue', null) + emit('fileSelected', null) + return + } + + // Validate file type if accept is specified + if (props.accept) { + const acceptedTypes = Array.isArray(props.accept) ? props.accept.map((ext) => `image/${ext}`) : [props.accept] + if (!acceptedTypes.includes(file.type)) { + console.warn(`File type not allowed: ${file.type}`) + handleChange(null) + emit('update:modelValue', null) + emit('fileSelected', null) + return + } + } + handleChange(file) + emit('update:modelValue', file) + emit('fileSelected', file) } @@ -62,7 +106,7 @@ async function onFileChange(event: Event, handleChange: (value: any) => void) { @change="onFileChange($event, handleChange)" type="file" :disabled="isDisabled" - v-bind="{ onBlur: componentField.onBlur }" + v-bind="{ onBlur: componentField.onBlur }" :placeholder="placeholder" :class="cn('focus:border-primary focus:ring-2 focus:ring-primary focus:ring-offset-0')" /> diff --git a/app/handlers/encounter-entry.handler.ts b/app/handlers/encounter-entry.handler.ts index cbc21256..e6501928 100644 --- a/app/handlers/encounter-entry.handler.ts +++ b/app/handlers/encounter-entry.handler.ts @@ -342,22 +342,16 @@ export function useEncounterEntry(props: { try { isLoadingDetail.value = true - console.log('📥 [EDIT MODE] Loading encounter detail:', { id: props.id }) - const result = await getEncounterDetail(props.id, { - includes: 'patient,patient-person,specialist,subspecialist', + includes: 'patient,patient-person,specialist,subspecialist,EncounterDocuments' }) - console.log('📥 [EDIT MODE] API Response:', { success: result.success, data: result.body?.data }) - if (result.success && result.body?.data) { encounterData.value = result.body.data await mapEncounterToForm(encounterData.value) isLoadingDetail.value = false - console.log('✅ [EDIT MODE] Encounter detail loaded and form mapped successfully') } else { const errorMsg = result.body?.message || 'Gagal memuat data kunjungan' - console.error('❌ [EDIT MODE] Failed to load encounter:', errorMsg) toast({ title: 'Gagal', description: errorMsg, @@ -366,7 +360,6 @@ export function useEncounterEntry(props: { await navigateTo(getListPath()) } } catch (error: any) { - console.error('❌ [EDIT MODE] Error loading encounter detail:', error) toast({ title: 'Gagal', description: error?.message || 'Gagal memuat data kunjungan', @@ -462,12 +455,6 @@ export function useEncounterEntry(props: { formObjects.value = formData - console.log('📋 [EDIT MODE] Mapped encounter to form:', { - encounterData: encounter, - formData: formData, - timestamp: new Date().toISOString(), - }) - if (formData.sepNumber) { sepNumber.value = formData.sepNumber } @@ -570,18 +557,17 @@ export function useEncounterEntry(props: { let result if (isEditMode.value) { - console.log('💾 [EDIT MODE] Sending PATCH request:', { id: props.id, payload }) result = await updateEncounter(props.id, payload) } else { console.log('💾 [ADD MODE] Sending POST request:', { payload }) result = await createEncounter(payload) } - console.log('📤 [SAVE] API Response:', { success: result.success, message: result.body?.message }) - if (result.success) { + console.log(result) + console.log(sepFile.value) + console.log(sippFile.value) // const encounterId = isEditMode.value ? props.id : result.body?.data?.id - if (patientId) { if (sepFile.value) { await uploadAttachment(sepFile.value, patientId, 'vclaim-sep') diff --git a/app/schemas/integration-encounter.schema.ts b/app/schemas/integration-encounter.schema.ts index 07dc7894..0ed5d1ee 100644 --- a/app/schemas/integration-encounter.schema.ts +++ b/app/schemas/integration-encounter.schema.ts @@ -52,8 +52,7 @@ const IntegrationEncounterSchema = z .min(1, ERROR_MESSAGES.required.sepType) .optional(), sepNumber: z - .string() - .min(1, ERROR_MESSAGES.required.sepNumber) + .string({ required_error: ERROR_MESSAGES.required.sepNumber }) .optional(), // File uploads