refactor: remove photo attachment functionality from employee advances module
This commit is contained in:
parent
c40e9e5e87
commit
e6ed485718
@ -4,14 +4,11 @@
|
||||
|
||||
use App\Enums\EmployeeAdvanceStatus;
|
||||
use App\Enums\Permission;
|
||||
use App\Http\Requests\Concerns\ValidatesMediaUploads;
|
||||
use App\Models\EmployeeAdvance;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class EmployeeAdvanceRequest extends FormRequest
|
||||
{
|
||||
use ValidatesMediaUploads;
|
||||
|
||||
public function authorize(): bool
|
||||
{
|
||||
$user = $this->user();
|
||||
@ -44,8 +41,7 @@ public function rules(): array
|
||||
return [
|
||||
'amount' => ['required', 'integer', 'min:1'],
|
||||
'description' => ['required', 'string', 'max:100'],
|
||||
'due_date' => ['required', 'date','after_or_equal:today'],
|
||||
...$this->photoRules(),
|
||||
'due_date' => ['required', 'date', 'after_or_equal:today'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -58,7 +54,6 @@ public function attributes(): array
|
||||
'amount' => 'jumlah',
|
||||
'description' => 'keterangan',
|
||||
'due_date' => 'jatuh tempo',
|
||||
...$this->photoUploadAttributes('foto bukti'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
namespace App\Models;
|
||||
|
||||
use App\Enums\EmployeeAdvanceStatus;
|
||||
use App\Models\Concerns\HasModuleMedia;
|
||||
use App\Models\Concerns\HasRejection;
|
||||
use App\Models\Concerns\InteractsWithActivityLog;
|
||||
use Illuminate\Database\Eloquent\Attributes\Appends;
|
||||
@ -12,7 +11,6 @@
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Spatie\MediaLibrary\HasMedia;
|
||||
|
||||
#[Guarded(['id'])]
|
||||
#[Appends([
|
||||
@ -27,10 +25,9 @@
|
||||
'can_verify',
|
||||
'can_pay',
|
||||
])]
|
||||
class EmployeeAdvance extends Model implements HasMedia
|
||||
class EmployeeAdvance extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
use HasModuleMedia;
|
||||
use HasRejection;
|
||||
use InteractsWithActivityLog;
|
||||
|
||||
@ -140,14 +137,4 @@ public function statusLabel(): Attribute
|
||||
get: fn () => $this->status?->label(),
|
||||
);
|
||||
}
|
||||
|
||||
public static function mediaModuleName(): string
|
||||
{
|
||||
return 'employee-advance';
|
||||
}
|
||||
|
||||
public function registerMediaCollections(): void
|
||||
{
|
||||
$this->addMediaCollection('photos');
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,9 +6,7 @@
|
||||
use App\Models\EmployeeAdvance;
|
||||
use App\Models\User;
|
||||
use App\Services\Concerns\ResolvesAuthEmployee;
|
||||
use App\Services\Media\MediaService;
|
||||
use App\Services\System\PushNotificationService;
|
||||
use App\Support\Media\MediaPresenter;
|
||||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@ -22,7 +20,6 @@ class EmployeeAdvanceService
|
||||
|
||||
public function __construct(
|
||||
private readonly CashService $cashService,
|
||||
private readonly MediaService $mediaService,
|
||||
private readonly PushNotificationService $pushNotificationService,
|
||||
) {}
|
||||
|
||||
@ -52,7 +49,7 @@ public function outstandingSummary(): array
|
||||
public function paginateForIndex(array $tableQuery): LengthAwarePaginator
|
||||
{
|
||||
$query = EmployeeAdvance::query()
|
||||
->with(['employee.user.profile', 'rejection', 'media'])
|
||||
->with(['employee.user.profile', 'rejection'])
|
||||
->when($tableQuery['search'] !== '', function (Builder $query) use ($tableQuery): void {
|
||||
$search = $tableQuery['search'];
|
||||
$query->where(function (Builder $query) use ($search): void {
|
||||
@ -67,15 +64,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator
|
||||
|
||||
return $query
|
||||
->paginate(10)
|
||||
->withQueryString()
|
||||
->through(function (EmployeeAdvance $employeeAdvance) {
|
||||
$employeeAdvance->setAttribute(
|
||||
'photos',
|
||||
MediaPresenter::collection($employeeAdvance, 'photos'),
|
||||
);
|
||||
|
||||
return $employeeAdvance;
|
||||
});
|
||||
->withQueryString();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,17 +75,13 @@ public function create(array $validated, User $user): void
|
||||
$employee = $this->resolveAuthEmployee($user);
|
||||
|
||||
$employeeAdvance = DB::transaction(function () use ($validated, $employee) {
|
||||
$employeeAdvance = EmployeeAdvance::create([
|
||||
return EmployeeAdvance::create([
|
||||
'employee_id' => $employee->id,
|
||||
'amount' => (int) $validated['amount'],
|
||||
'description' => $validated['description'],
|
||||
'due_date' => $validated['due_date'],
|
||||
'status' => EmployeeAdvanceStatus::PENDING,
|
||||
]);
|
||||
|
||||
$this->syncPhotos($employeeAdvance, $validated);
|
||||
|
||||
return $employeeAdvance;
|
||||
});
|
||||
|
||||
$this->pushNotificationService->sendToRoles(
|
||||
@ -120,8 +105,6 @@ public function update(EmployeeAdvance $employeeAdvance, array $validated, User
|
||||
$employeeAdvance->description = $validated['description'];
|
||||
$employeeAdvance->due_date = $validated['due_date'];
|
||||
$employeeAdvance->save();
|
||||
|
||||
$this->syncPhotos($employeeAdvance, $validated);
|
||||
});
|
||||
}
|
||||
|
||||
@ -130,7 +113,6 @@ public function delete(EmployeeAdvance $employeeAdvance, User $user): void
|
||||
$this->ensureOwnedBySubmitter($employeeAdvance, $user);
|
||||
$this->ensurePending($employeeAdvance, 'Kasbon hanya dapat dihapus saat status menunggu.');
|
||||
|
||||
$employeeAdvance->clearMediaCollection('photos');
|
||||
$employeeAdvance->delete();
|
||||
}
|
||||
|
||||
@ -229,22 +211,6 @@ public function pay(EmployeeAdvance $employeeAdvance, User $user): void
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $validated
|
||||
*/
|
||||
private function syncPhotos(EmployeeAdvance $employeeAdvance, array $validated): void
|
||||
{
|
||||
$this->mediaService->syncCollection(
|
||||
$employeeAdvance,
|
||||
'photos',
|
||||
$validated['photos'] ?? null,
|
||||
$validated['remove_media_ids'] ?? null,
|
||||
self::MAX_PHOTOS,
|
||||
required: true,
|
||||
errorKey: 'photos',
|
||||
);
|
||||
}
|
||||
|
||||
private function ensureOwnedBySubmitter(EmployeeAdvance $employeeAdvance, User $user): void
|
||||
{
|
||||
if ($user->employee?->id !== $employeeAdvance->employee_id) {
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { useForm } from '@inertiajs/vue3';
|
||||
import { Save } from '@lucide/vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { toast } from 'vue-sonner';
|
||||
import ImageUploadField from '@/components/form/image-upload-field/ImageUploadField.vue';
|
||||
import { RupiahInput } from '@/components/form/rupiah-input';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DatePicker } from '@/components/ui/date-picker';
|
||||
@ -27,7 +26,6 @@ import { FIELD_LIMITS } from '@/lib/field-limits';
|
||||
import { formErrors } from '@/lib/form';
|
||||
import { parseRupiah } from '@/lib/rupiah';
|
||||
import type { EmployeeAdvanceFormData, EmployeeAdvanceListItem } from '@/types/employee-advance';
|
||||
import { appendPhotosToFormData } from '@/types/media';
|
||||
|
||||
const open = defineModel<boolean>('open', { default: false });
|
||||
|
||||
@ -37,30 +35,14 @@ 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<EmployeeAdvanceFormData>({
|
||||
amount: '',
|
||||
description: '',
|
||||
due_date: '',
|
||||
photos: [],
|
||||
remove_media_ids: [],
|
||||
});
|
||||
|
||||
const photoFile = computed<File | null>({
|
||||
get: () => form.photos[0] ?? null,
|
||||
set: (file) => {
|
||||
form.photos = file ? [file] : [];
|
||||
},
|
||||
});
|
||||
|
||||
function resetForm() {
|
||||
form.reset();
|
||||
form.photos = [];
|
||||
form.remove_media_ids = [];
|
||||
existingPhotoId.value = null;
|
||||
form.clearErrors();
|
||||
}
|
||||
|
||||
@ -74,7 +56,6 @@ function populateForm(employeeAdvance: EmployeeAdvanceListItem | null | undefine
|
||||
form.amount = String(employeeAdvance.amount);
|
||||
form.description = employeeAdvance.description;
|
||||
form.due_date = employeeAdvance.due_date_input;
|
||||
existingPhotoId.value = employeeAdvance.photos?.[0]?.id ?? null;
|
||||
}
|
||||
|
||||
useFormDialog({
|
||||
@ -95,14 +76,6 @@ function buildFormData(forUpdate: boolean): FormData {
|
||||
formData.append('description', form.description);
|
||||
formData.append('due_date', form.due_date);
|
||||
|
||||
const removeMediaIds = [...form.remove_media_ids];
|
||||
|
||||
if (form.photos.length > 0 && existingPhotoId.value !== null) {
|
||||
removeMediaIds.push(existingPhotoId.value);
|
||||
}
|
||||
|
||||
appendPhotosToFormData(formData, form.photos, removeMediaIds);
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
@ -157,8 +130,6 @@ function submit() {
|
||||
<DatePicker id="employee-advance-due-date" v-model="form.due_date" />
|
||||
<FieldError :errors="formErrors(form, 'due_date')" />
|
||||
</Field>
|
||||
<ImageUploadField id="employee-advance-photos" v-model="photoFile" label="Foto Bukti" required
|
||||
:current-url="currentPhotoUrl" :errors="formErrors(form, 'photos')" />
|
||||
</FieldSet>
|
||||
</FieldGroup>
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ import type { ColumnDef } from '@tanstack/vue-table';
|
||||
import { h } from 'vue';
|
||||
import DataTableActions from '@/components/admin/finance/employee-advances/data-table-actions.vue';
|
||||
import { DataTableColumnHeader } from '@/components/data-table';
|
||||
import MediaThumbnailCell from '@/components/media/MediaThumbnailCell.vue';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import type { EmployeeAdvanceListItem } from '@/types/employee-advance';
|
||||
|
||||
@ -45,12 +44,7 @@ export function createColumns(
|
||||
enableSorting: true,
|
||||
header: () => h(DataTableColumnHeader, { title: 'Keterangan', column: 'description' }),
|
||||
},
|
||||
{
|
||||
id: 'photos',
|
||||
enableSorting: false,
|
||||
header: () => 'Foto',
|
||||
cell: ({ row }) => h(MediaThumbnailCell, { items: row.original.photos ?? [] }),
|
||||
},
|
||||
|
||||
{
|
||||
accessorKey: 'due_date_formatted',
|
||||
enableSorting: true,
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import type { MediaItem } from '@/types/media';
|
||||
|
||||
export type EmployeeAdvanceListItem = {
|
||||
id: number;
|
||||
employee_id: number;
|
||||
@ -16,15 +14,12 @@ export type EmployeeAdvanceListItem = {
|
||||
is_editable: boolean;
|
||||
can_verify: boolean;
|
||||
can_pay: boolean;
|
||||
photos?: MediaItem[];
|
||||
};
|
||||
|
||||
export type EmployeeAdvanceFormData = {
|
||||
amount: string;
|
||||
description: string;
|
||||
due_date: string;
|
||||
photos: File[];
|
||||
remove_media_ids: number[];
|
||||
};
|
||||
|
||||
export type PaginatedEmployeeAdvances = {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user