Refactor CashTransactionFormModal and EmployeeAdvanceFormModal to utilize useFormDialog for improved form handling. Replace MediaImageUpload with ImageUploadField for photo uploads, enhancing user experience. Update form data handling to accommodate new photo upload logic and streamline code.

This commit is contained in:
Yoga Pangestu 2026-06-12 16:37:47 +07:00
parent 10e188807d
commit e871da4c5f
2 changed files with 67 additions and 54 deletions

View File

@ -1,9 +1,8 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { Save } from '@lucide/vue';
import { computed, watch } from 'vue';
import { computed, ref } from 'vue';
import { toast } from 'vue-sonner';
import MediaImageUpload from '@/components/media/MediaImageUpload.vue';
import { Button } from '@/components/ui/button';
import {
Dialog,
@ -19,12 +18,13 @@ import {
FieldLabel,
FieldSet,
} from '@/components/ui/field';
import ImageUploadField from '@/components/ui/image-upload-field/ImageUploadField.vue';
import { RupiahInput } from '@/components/ui/rupiah-input';
import { Textarea } from '@/components/ui/textarea';
import { useFormDialog } from '@/composables/useFormDialog';
import { formErrors } from '@/lib/form';
import { parseRupiah } from '@/lib/rupiah';
import type { CashTransactionListItem } from '@/types/cash';
import { appendRootPhotosToFormData, createMediaUploadState } from '@/types/media';
const open = defineModel<boolean>('open', { default: false });
@ -34,15 +34,21 @@ const props = defineProps<{
const isEditing = computed(() => props.transaction != null);
const existingPhotoId = ref<number | null>(null);
const currentPhotoUrl = computed(() => props.transaction?.photos?.[0]?.url ?? null);
const form = useForm({
amount: '',
description: '',
media: createMediaUploadState(),
photo: null as File | null,
});
function resetForm() {
form.reset();
form.media = createMediaUploadState();
form.photo = null;
existingPhotoId.value = null;
form.clearErrors();
}
@ -55,22 +61,14 @@ function populateForm(transaction: CashTransactionListItem | null | undefined) {
form.amount = String(transaction.amount);
form.description = transaction.description;
form.media = createMediaUploadState(transaction.photos ?? []);
existingPhotoId.value = transaction.photos?.[0]?.id ?? null;
}
watch(
() => props.transaction,
(transaction) => {
populateForm(transaction);
},
);
watch(open, (isOpen) => {
if (isOpen) {
populateForm(props.transaction);
} else {
resetForm();
}
useFormDialog({
open,
source: () => props.transaction,
populate: populateForm,
reset: resetForm,
});
function buildFormData(forUpdate: boolean): FormData {
@ -82,12 +80,21 @@ function buildFormData(forUpdate: boolean): FormData {
formData.append('amount', parseRupiah(form.amount));
formData.append('description', form.description);
appendRootPhotosToFormData(formData, form.media);
if (form.photo) {
formData.append('photos[]', form.photo);
if (existingPhotoId.value) {
formData.append('remove_media_ids[]', String(existingPhotoId.value));
}
}
return formData;
}
function submit() {
const payload = buildFormData(isEditing.value);
const options = {
forceFormData: true,
preserveScroll: true,
@ -101,13 +108,13 @@ function submit() {
},
};
const payload = buildFormData(isEditing.value);
if (isEditing.value && props.transaction) {
form.transform(() => payload).post(`/admin/finance/cash/transactions/${props.transaction.id}`, options);
} else {
form.transform(() => payload).post('/admin/finance/cash/deposit', options);
form.transform(() => payload).put(`/admin/finance/cash/transactions/${props.transaction.id}`, options);
return;
}
form.transform(() => payload).post('/admin/finance/cash/deposit', options);
}
</script>
@ -132,8 +139,8 @@ function submit() {
placeholder="Contoh: Setoran kas harian" rows="3" />
<FieldError :errors="formErrors(form, 'description')" />
</Field>
<MediaImageUpload id="cash-photos" v-model="form.media" label="Foto Bukti" required
:errors="formErrors(form, 'photos')" />
<ImageUploadField id="cash-photos" v-model="form.photo" label="Foto Bukti" required
:current-url="currentPhotoUrl" :errors="formErrors(form, 'photos')" />
</FieldSet>
</FieldGroup>

View File

@ -1,9 +1,8 @@
<script setup lang="ts">
import { useForm } from '@inertiajs/vue3';
import { Save } from '@lucide/vue';
import { computed, watch } from 'vue';
import { computed, ref } from 'vue';
import { toast } from 'vue-sonner';
import MediaImageUpload from '@/components/media/MediaImageUpload.vue';
import { Button } from '@/components/ui/button';
import { DatePicker } from '@/components/ui/date-picker';
import {
@ -20,12 +19,13 @@ import {
FieldLabel,
FieldSet,
} from '@/components/ui/field';
import ImageUploadField from '@/components/ui/image-upload-field/ImageUploadField.vue';
import { RupiahInput } from '@/components/ui/rupiah-input';
import { Textarea } from '@/components/ui/textarea';
import { useFormDialog } from '@/composables/useFormDialog';
import { formErrors } from '@/lib/form';
import { parseRupiah } from '@/lib/rupiah';
import type { EmployeeAdvanceListItem } from '@/types/employee-advance';
import { appendRootPhotosToFormData, createMediaUploadState } from '@/types/media';
const open = defineModel<boolean>('open', { default: false });
@ -35,16 +35,21 @@ const props = defineProps<{
const isEditing = computed(() => props.employeeAdvance != null);
const existingPhotoId = ref<number | null>(null);
const currentPhotoUrl = computed(() => props.employeeAdvance?.photos?.[0]?.url ?? null);
const form = useForm({
amount: '',
description: '',
due_date: '',
media: createMediaUploadState(),
photo: null as File | null,
});
function resetForm() {
form.reset();
form.media = createMediaUploadState();
form.photo = null;
existingPhotoId.value = null;
form.clearErrors();
}
@ -58,22 +63,14 @@ function populateForm(employeeAdvance: EmployeeAdvanceListItem | null | undefine
form.amount = String(employeeAdvance.amount);
form.description = employeeAdvance.description;
form.due_date = employeeAdvance.due_date_input;
form.media = createMediaUploadState(employeeAdvance.photos ?? []);
existingPhotoId.value = employeeAdvance.photos?.[0]?.id ?? null;
}
watch(
() => props.employeeAdvance,
(employeeAdvance) => {
populateForm(employeeAdvance);
},
);
watch(open, (isOpen) => {
if (isOpen) {
populateForm(props.employeeAdvance);
} else {
resetForm();
}
useFormDialog({
open,
source: () => props.employeeAdvance,
populate: populateForm,
reset: resetForm,
});
function buildFormData(forUpdate: boolean): FormData {
@ -86,12 +83,21 @@ function buildFormData(forUpdate: boolean): FormData {
formData.append('amount', parseRupiah(form.amount));
formData.append('description', form.description);
formData.append('due_date', form.due_date);
appendRootPhotosToFormData(formData, form.media);
if (form.photo) {
formData.append('photos[]', form.photo);
if (existingPhotoId.value) {
formData.append('remove_media_ids[]', String(existingPhotoId.value));
}
}
return formData;
}
function submit() {
const payload = buildFormData(isEditing.value);
const options = {
forceFormData: true,
preserveScroll: true,
@ -103,13 +109,13 @@ function submit() {
},
};
const payload = buildFormData(isEditing.value);
if (isEditing.value && props.employeeAdvance) {
form.transform(() => payload).post(`/admin/finance/employee-advances/${props.employeeAdvance.id}`, options);
} else {
form.transform(() => payload).post('/admin/finance/employee-advances', options);
form.transform(() => payload).put(`/admin/finance/employee-advances/${props.employeeAdvance.id}`, options);
return;
}
form.transform(() => payload).post('/admin/finance/employee-advances', options);
}
</script>
@ -139,8 +145,8 @@ function submit() {
<DatePicker id="employee-advance-due-date" v-model="form.due_date" />
<FieldError :errors="formErrors(form, 'due_date')" />
</Field>
<MediaImageUpload id="employee-advance-photos" v-model="form.media" label="Foto Bukti" required
:errors="formErrors(form, 'photos')" />
<ImageUploadField id="employee-advance-photos" v-model="form.photo" label="Foto Bukti" required
:current-url="currentPhotoUrl" :errors="formErrors(form, 'photos')" />
</FieldSet>
</FieldGroup>