dstpabuaran.com/docs/2026-08-14-lazy-loading-card-table.md

50 lines
2.4 KiB
Markdown

# Lazy Loading Card Table
## Date
2026-08-14
## Goal
Optimize page load performance for raw materials, products, and purchases 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), 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`)
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
### 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
## Route Added
```php
Route::get('purchases/{purchase}/items', [PurchaseController::class, 'items'])->name('purchases.items')->middleware('permission:purchases.view');
```
## Key Implementation Details
- SQL subqueries avoid N+1 queries while keeping summary data in the paginated response
- `getItems()` method handles media URL resolution for item photos
- Frontend caches loaded items to avoid re-fetching on repeated expand/collapse
- Loading state shown while items are being fetched
## Verification
- PHP syntax check: OK
- TypeScript: Only pre-existing errors in `use-purchase-draft.ts` (unrelated)
- Route cache cleared