83 lines
5.5 KiB
Markdown
83 lines
5.5 KiB
Markdown
# 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
|
|
1. **Backend**: Remove eager-loaded relationships from paginated query
|
|
2. **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`)
|
|
3. **Backend**: Add new controller method returning JSON for lazy load
|
|
4. **Frontend**: Use `useCardTableExpand(false)` for default collapsed state
|
|
5. **Frontend**: Fetch child data via `fetch()` on expand, store in `Record<number, T[]>` state
|
|
6. **Frontend**: Pass `items` + `isLoading` props to sub-row components
|
|
|
|
## Files Modified
|
|
|
|
### Restock (this session)
|
|
- `app/Services/Admin/Manage/RestockService.php` - `paginated()` no longer eager loads `restockItems`; added `getItems()` method
|
|
- `app/Http/Controllers/Admin/Manage/RestockController.php` - added `items()` method
|
|
- `routes/web.php` - added `GET restocks/{restock}/items` route
|
|
- `resources/js/pages/admin/manage/restock/columns.tsx` - `Restock` type: `items_count`, `total_qty`, `product_names`
|
|
- `resources/js/pages/admin/manage/restock/restock-card.tsx` - uses summary data from props
|
|
- `resources/js/pages/admin/manage/restock/restock-sub-row.tsx` - accepts `items` & `isLoading` props
|
|
- `resources/js/pages/admin/manage/restock/index.tsx` - lazy loading with `fetchItems()`
|
|
|
|
### Transaction (this session)
|
|
- `app/Services/Admin/Manage/TransactionService.php` - `paginated()` no longer eager loads `orderItems`; added `getItems()` method
|
|
- `app/Http/Controllers/Admin/Manage/TransactionController.php` - added `items()` method
|
|
- `routes/web.php` - added `GET transactions/{transaction}/items` route
|
|
- `resources/js/pages/admin/manage/transaction/columns.tsx` - `Transaction` type: `items_count`, `total_qty`, `product_names`
|
|
- `resources/js/pages/admin/manage/transaction/transaction-card.tsx` - uses summary data from props
|
|
- `resources/js/pages/admin/manage/transaction/transaction-sub-row.tsx` - accepts `items` & `isLoading` props
|
|
- `resources/js/pages/admin/manage/transaction/index.tsx` - lazy loading with `fetchItems()`, print fetches items on demand
|
|
|
|
### Cutting (this session)
|
|
- `app/Services/Admin/Manage/CuttingService.php` - `paginated()` no longer eager loads `cuttingResults`, `cuttingMaterials`, `cuttingMaterialCombinations`; added `getMaterials()` and `getCombinations()` methods
|
|
- `app/Http/Controllers/Admin/Manage/CuttingController.php` - added `materials()` method
|
|
- `routes/web.php` - added `GET cuttings/{cutting}/materials` route
|
|
- `resources/js/pages/admin/manage/cutting/columns.tsx` - `Cutting` type: `materials_count`, `total_usage`, `product_name`, `cutting_result`
|
|
- `resources/js/pages/admin/manage/cutting/cutting-card.tsx` - uses summary data from props
|
|
- `resources/js/pages/admin/manage/cutting/cutting-sub-row.tsx` - accepts `materials`, `combinations`, `isLoading` props
|
|
- `resources/js/pages/admin/manage/cutting/index.tsx` - lazy loading with `fetchMaterials()`
|
|
- `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 loads `purchaseItems`; added `getItems()` method
|
|
- `app/Http/Controllers/Admin/Manage/PurchaseController.php` - added `items()` method
|
|
- `routes/web.php` - added `GET purchases/{purchase}/items` route
|
|
- `resources/js/pages/admin/manage/purchase/columns.tsx` - `Purchase` type: `variants_count`, `total_qty`, `material_name`, `unit`
|
|
- `resources/js/pages/admin/manage/purchase/purchase-card.tsx` - uses summary data from props
|
|
- `resources/js/pages/admin/manage/purchase/purchase-sub-row.tsx` - accepts `items` & `isLoading` props
|
|
- `resources/js/pages/admin/manage/purchase/index.tsx` - lazy loading with `fetchItems()`
|
|
|
|
### Raw Materials & Products (previous sessions)
|
|
- Same pattern applied to `RawMaterialService`, `RawMaterialController`, `ProductService`, `ProductController`
|
|
- Frontend pages updated with lazy loading state
|
|
|
|
## Routes Added
|
|
```php
|
|
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.tsx` files (unrelated)
|
|
- Route cache cleared
|