From 86ee45c1fbaae2ac980f554ad8192523a1be8027 Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Tue, 30 Jun 2026 17:42:48 +0700 Subject: [PATCH] feat: implement owner verification detail modal with data fetching and display functionality --- .../Manage/OwnerVerificationController.php | 19 ++ app/Services/Manage/PurchaseService.php | 3 +- app/Services/Master/ProductService.php | 3 +- app/Services/Master/RawMaterialService.php | 3 +- .../VerificationChangeFormatter.php | 45 ++- .../VerificationDetailModal.vue | 278 ++++++++++++++++++ .../purchases/table/PurchaseGroupedTable.vue | 30 +- .../products/table/ProductGroupedTable.vue | 26 +- .../table/RawMaterialGroupedTable.vue | 24 +- resources/js/types/product.ts | 1 + resources/js/types/purchase.ts | 1 + resources/js/types/raw-material.ts | 1 + routes/web.php | 3 + 13 files changed, 385 insertions(+), 52 deletions(-) create mode 100644 resources/js/components/owner-verification/VerificationDetailModal.vue diff --git a/app/Http/Controllers/Admin/Manage/OwnerVerificationController.php b/app/Http/Controllers/Admin/Manage/OwnerVerificationController.php index 1f52d67..78cca97 100644 --- a/app/Http/Controllers/Admin/Manage/OwnerVerificationController.php +++ b/app/Http/Controllers/Admin/Manage/OwnerVerificationController.php @@ -10,6 +10,9 @@ use App\Models\OwnerVerificationRequest; use App\Services\Manage\OwnerVerificationService; use App\Services\Manage\StockService; +use App\Support\ActivityLog\ModelLabel; +use App\Support\OwnerVerification\VerificationChangeFormatter; +use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; class OwnerVerificationController extends Controller @@ -21,6 +24,22 @@ public function __construct( private readonly StockService $stockService, ) {} + public function show(OwnerVerificationRequest $ownerVerificationRequest): JsonResponse + { + $payload = is_array($ownerVerificationRequest->payload) ? $ownerVerificationRequest->payload : []; + + return response()->json([ + 'id' => $ownerVerificationRequest->id, + 'action_label' => $ownerVerificationRequest->action->label(), + 'subject_label' => ModelLabel::for($ownerVerificationRequest->subject_type), + 'submitted_by_name' => $ownerVerificationRequest->submittedBy?->profile?->full_name + ?? $ownerVerificationRequest->submittedBy?->username + ?? '-', + 'created_at_formatted' => $ownerVerificationRequest->created_at?->translatedFormat('l, d F Y H:i'), + 'changes' => VerificationChangeFormatter::format($payload), + ]); + } + public function approveCutting(ApproveVerificationRequest $request, Cutting $cutting): RedirectResponse { $this->stockService->approveVerification( diff --git a/app/Services/Manage/PurchaseService.php b/app/Services/Manage/PurchaseService.php index e295b68..1988905 100644 --- a/app/Services/Manage/PurchaseService.php +++ b/app/Services/Manage/PurchaseService.php @@ -37,7 +37,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator ->with([ 'supplier:id,name', 'createdBy.profile', - 'pendingOwnerVerificationRequest', + 'pendingOwnerVerificationRequest.submittedBy.profile', 'items.rawMaterialPrice.rawMaterial:id,name,unit', 'media', ]) @@ -70,6 +70,7 @@ public function paginateForIndex(array $tableQuery): LengthAwarePaginator $purchase->setAttribute('pending_request_id', $pendingRequest?->id); $purchase->setAttribute('pending_request_action', $pendingRequest?->action->value); $purchase->setAttribute('pending_request_action_label', $pendingRequest?->action->label()); + $purchase->setAttribute('pending_request_submitted_by_name', $pendingRequest?->submittedBy?->profile?->full_name ?? $pendingRequest?->submittedBy?->username); $purchase->items->each(fn (PurchaseItem $item) => $this->breakItemCircularReference($item)); diff --git a/app/Services/Master/ProductService.php b/app/Services/Master/ProductService.php index e9af99b..0a49bb0 100644 --- a/app/Services/Master/ProductService.php +++ b/app/Services/Master/ProductService.php @@ -33,7 +33,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $ca $query = Product::query() ->with([ 'categories', - 'pendingOwnerVerificationRequest', + 'pendingOwnerVerificationRequest.submittedBy.profile', 'variants' => fn ($query) => $query ->with(['media', 'prices' => fn ($query) => $query->orderBy('type')]) ->orderBy('created_at'), @@ -89,6 +89,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $ca $product->setAttribute('pending_request_id', $pendingRequest?->id); $product->setAttribute('pending_request_action', $pendingRequest?->action->value); $product->setAttribute('pending_request_action_label', $pendingRequest?->action->label()); + $product->setAttribute('pending_request_submitted_by_name', $pendingRequest?->submittedBy?->profile?->full_name ?? $pendingRequest?->submittedBy?->username); $product->setAttribute( 'display_is_active', $pendingRequest?->pendingToggleIsActive() ?? $product->is_active, diff --git a/app/Services/Master/RawMaterialService.php b/app/Services/Master/RawMaterialService.php index 03e14e5..6e2e583 100644 --- a/app/Services/Master/RawMaterialService.php +++ b/app/Services/Master/RawMaterialService.php @@ -32,7 +32,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $st { $query = RawMaterial::query() ->with([ - 'pendingOwnerVerificationRequest', + 'pendingOwnerVerificationRequest.submittedBy.profile', 'prices' => fn ($query) => $query ->orderBy('created_at') ->with('media'), @@ -85,6 +85,7 @@ public function paginateForIndex(array $tableQuery, string $isActive, string $st $rawMaterial->setAttribute('pending_request_id', $pendingRequest?->id); $rawMaterial->setAttribute('pending_request_action', $pendingRequest?->action->value); $rawMaterial->setAttribute('pending_request_action_label', $pendingRequest?->action->label()); + $rawMaterial->setAttribute('pending_request_submitted_by_name', $pendingRequest?->submittedBy?->profile?->full_name ?? $pendingRequest?->submittedBy?->username); $rawMaterial->setAttribute( 'display_is_active', $pendingRequest?->pendingToggleIsActive() ?? $rawMaterial->is_active, diff --git a/app/Support/OwnerVerification/VerificationChangeFormatter.php b/app/Support/OwnerVerification/VerificationChangeFormatter.php index 0adcba6..48ea3a9 100644 --- a/app/Support/OwnerVerification/VerificationChangeFormatter.php +++ b/app/Support/OwnerVerification/VerificationChangeFormatter.php @@ -144,38 +144,35 @@ private static function presentValue(string $field, mixed $value): mixed if ($field === 'variants' && is_array($value)) { return collect($value) - ->map(function (array $variant): string { - $name = $variant['name'] ?? '-'; - $stock = $variant['stock'] ?? 0; - - return "{$name} (stok: {$stock})"; - }) - ->implode(', '); + ->map(fn (array $variant) => [ + 'name' => $variant['name'] ?? '-', + 'stock' => (int) ($variant['stock'] ?? 0), + ]) + ->values() + ->all(); } if ($field === 'prices' && is_array($value)) { return collect($value) - ->map(function (array $price): string { - $variant = $price['variant'] ?? '-'; - $stock = $price['stock'] ?? 0; - $priceValue = $price['price'] ?? 0; - - return "{$variant} (stok: {$stock}, harga: {$priceValue})"; - }) - ->implode(', '); + ->map(fn (array $price) => [ + 'variant' => $price['variant'] ?? '-', + 'stock' => (int) ($price['stock'] ?? 0), + 'price' => (int) ($price['price'] ?? 0), + ]) + ->values() + ->all(); } if ($field === 'items' && is_array($value)) { return collect($value) - ->map(function (array $item): string { - $name = $item['raw_material_name'] ?? '-'; - $variant = $item['variant'] ?? '-'; - $quantity = $item['quantity'] ?? 0; - $subtotal = $item['subtotal'] ?? 0; - - return "{$name} / {$variant} (jumlah: {$quantity}, subtotal: {$subtotal})"; - }) - ->implode('; '); + ->map(fn (array $item) => [ + 'raw_material_name' => $item['raw_material_name'] ?? '-', + 'variant' => $item['variant'] ?? '-', + 'quantity' => (int) ($item['quantity'] ?? 0), + 'subtotal' => (int) ($item['subtotal'] ?? 0), + ]) + ->values() + ->all(); } if ($field === 'has_photos') { diff --git a/resources/js/components/owner-verification/VerificationDetailModal.vue b/resources/js/components/owner-verification/VerificationDetailModal.vue new file mode 100644 index 0000000..d2743a4 --- /dev/null +++ b/resources/js/components/owner-verification/VerificationDetailModal.vue @@ -0,0 +1,278 @@ + + + diff --git a/resources/js/pages/admin/manage/purchases/table/PurchaseGroupedTable.vue b/resources/js/pages/admin/manage/purchases/table/PurchaseGroupedTable.vue index d1b29fe..8ab705c 100644 --- a/resources/js/pages/admin/manage/purchases/table/PurchaseGroupedTable.vue +++ b/resources/js/pages/admin/manage/purchases/table/PurchaseGroupedTable.vue @@ -1,9 +1,9 @@ diff --git a/resources/js/pages/admin/master/products/table/ProductGroupedTable.vue b/resources/js/pages/admin/master/products/table/ProductGroupedTable.vue index 35073cb..ec4a5fc 100644 --- a/resources/js/pages/admin/master/products/table/ProductGroupedTable.vue +++ b/resources/js/pages/admin/master/products/table/ProductGroupedTable.vue @@ -1,9 +1,10 @@ diff --git a/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue b/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue index 2c0d195..c7279b8 100644 --- a/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue +++ b/resources/js/pages/admin/master/raw-materials/table/RawMaterialGroupedTable.vue @@ -1,9 +1,10 @@ diff --git a/resources/js/types/product.ts b/resources/js/types/product.ts index ba4963f..1595f42 100644 --- a/resources/js/types/product.ts +++ b/resources/js/types/product.ts @@ -76,6 +76,7 @@ export interface ProductListItem extends Product { pending_request_id?: number; pending_request_action?: string; pending_request_action_label?: string; + pending_request_submitted_by_name?: string; display_is_active?: boolean; } diff --git a/resources/js/types/purchase.ts b/resources/js/types/purchase.ts index c93474d..6768e82 100644 --- a/resources/js/types/purchase.ts +++ b/resources/js/types/purchase.ts @@ -56,6 +56,7 @@ export type PurchaseListItem = { pending_request_id?: number; pending_request_action?: string; pending_request_action_label?: string; + pending_request_submitted_by_name?: string; }; export type PurchaseCartItem = { diff --git a/resources/js/types/raw-material.ts b/resources/js/types/raw-material.ts index 6876389..2c0b773 100644 --- a/resources/js/types/raw-material.ts +++ b/resources/js/types/raw-material.ts @@ -33,6 +33,7 @@ export type RawMaterialListItem = { pending_request_id?: number; pending_request_action?: string; pending_request_action_label?: string; + pending_request_submitted_by_name?: string; display_is_active?: boolean; }; diff --git a/routes/web.php b/routes/web.php index fd3773c..c7e7652 100644 --- a/routes/web.php +++ b/routes/web.php @@ -413,6 +413,9 @@ Route::prefix('owner-verifications')->name('owner_verifications.') ->group(function () { + Route::get('requests/{ownerVerificationRequest}', [OwnerVerificationController::class, 'show']) + ->name('show'); + Route::post('cuttings/{cutting}/approve', [OwnerVerificationController::class, 'approveCutting']) ->middleware('permission:'.Permission::OWNER_VERIFICATIONS_VERIFY->value) ->name('approve');