5.5 KiB
5.5 KiB
Lazy Loading Card Table
Date
2026-08-14
Goal
Optimize page load performance for raw materials, products, purchases, cutting, transaction, and restock list pages by implementing lazy loading for child/variant data in card-based layouts.
Problem
Pages with 100+ records loaded all child data eagerly (variants, items, materials), causing slow initial page loads.
Solution Pattern
- Backend: Remove eager-loaded relationships from paginated query
- Backend: Add SQL subqueries for summary data (
variants_count,total_qty,material_name,unit,materials_count,total_usage,product_name,cutting_result,items_count,product_names) - Backend: Add new controller method returning JSON for lazy load
- Frontend: Use
useCardTableExpand(false)for default collapsed state - Frontend: Fetch child data via
fetch()on expand, store inRecord<number, T[]>state - Frontend: Pass
items+isLoadingprops to sub-row components
Files Modified
Restock (this session)
app/Services/Admin/Manage/RestockService.php-paginated()no longer eager loadsrestockItems; addedgetItems()methodapp/Http/Controllers/Admin/Manage/RestockController.php- addeditems()methodroutes/web.php- addedGET restocks/{restock}/itemsrouteresources/js/pages/admin/manage/restock/columns.tsx-Restocktype:items_count,total_qty,product_namesresources/js/pages/admin/manage/restock/restock-card.tsx- uses summary data from propsresources/js/pages/admin/manage/restock/restock-sub-row.tsx- acceptsitems&isLoadingpropsresources/js/pages/admin/manage/restock/index.tsx- lazy loading withfetchItems()
Transaction (this session)
app/Services/Admin/Manage/TransactionService.php-paginated()no longer eager loadsorderItems; addedgetItems()methodapp/Http/Controllers/Admin/Manage/TransactionController.php- addeditems()methodroutes/web.php- addedGET transactions/{transaction}/itemsrouteresources/js/pages/admin/manage/transaction/columns.tsx-Transactiontype:items_count,total_qty,product_namesresources/js/pages/admin/manage/transaction/transaction-card.tsx- uses summary data from propsresources/js/pages/admin/manage/transaction/transaction-sub-row.tsx- acceptsitems&isLoadingpropsresources/js/pages/admin/manage/transaction/index.tsx- lazy loading withfetchItems(), print fetches items on demand
Cutting (this session)
app/Services/Admin/Manage/CuttingService.php-paginated()no longer eager loadscuttingResults,cuttingMaterials,cuttingMaterialCombinations; addedgetMaterials()andgetCombinations()methodsapp/Http/Controllers/Admin/Manage/CuttingController.php- addedmaterials()methodroutes/web.php- addedGET cuttings/{cutting}/materialsrouteresources/js/pages/admin/manage/cutting/columns.tsx-Cuttingtype:materials_count,total_usage,product_name,cutting_resultresources/js/pages/admin/manage/cutting/cutting-card.tsx- uses summary data from propsresources/js/pages/admin/manage/cutting/cutting-sub-row.tsx- acceptsmaterials,combinations,isLoadingpropsresources/js/pages/admin/manage/cutting/index.tsx- lazy loading withfetchMaterials()resources/js/pages/admin/manage/cutting/show.tsx- passes full data to sub-row
Purchases (this session)
app/Services/Admin/Manage/PurchaseService.php-paginated()no longer eager loadspurchaseItems; addedgetItems()methodapp/Http/Controllers/Admin/Manage/PurchaseController.php- addeditems()methodroutes/web.php- addedGET purchases/{purchase}/itemsrouteresources/js/pages/admin/manage/purchase/columns.tsx-Purchasetype:variants_count,total_qty,material_name,unitresources/js/pages/admin/manage/purchase/purchase-card.tsx- uses summary data from propsresources/js/pages/admin/manage/purchase/purchase-sub-row.tsx- acceptsitems&isLoadingpropsresources/js/pages/admin/manage/purchase/index.tsx- lazy loading withfetchItems()
Raw Materials & Products (previous sessions)
- Same pattern applied to
RawMaterialService,RawMaterialController,ProductService,ProductController - Frontend pages updated with lazy loading state
Routes Added
Route::get('purchases/{purchase}/items', [PurchaseController::class, 'items'])->name('purchases.items')->middleware('permission:purchases.view');
Route::get('cuttings/{cutting}/materials', [CuttingController::class, 'materials'])->name('cuttings.materials')->middleware('permission:cuttings.view');
Route::get('transactions/{transaction}/items', [TransactionController::class, 'items'])->name('transactions.items')->middleware('permission:orders.view');
Route::get('restocks/{restock}/items', [RestockController::class, 'items'])->name('restocks.items')->middleware('permission:restocks.view');
Key Implementation Details
- SQL subqueries avoid N+1 queries while keeping summary data in the paginated response
getItems()/getMaterials()/getCombinations()methods handle media URL resolution for photos- Frontend caches loaded items to avoid re-fetching on repeated expand/collapse
- Loading state shown while items are being fetched
- Show pages (e.g.,
show.tsx) pass full data directly to sub-row components - Transaction print feature fetches items on demand if not cached
Verification
- PHP syntax check: OK
- TypeScript: Only pre-existing errors in
use-*-draft.ts,create.tsx,edit.tsxfiles (unrelated) - Route cache cleared