initial: bootstrap from BukidBountyApp base
This commit is contained in:
112
.claude/plans/db3f8841a2ce6fbe5c0279ccba883ec9-complete.md
Normal file
112
.claude/plans/db3f8841a2ce6fbe5c0279ccba883ec9-complete.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
task: Redesign HomeCooperative.vue into a proper Coordinator/Cooperative-user dashboard and wire it into Home.vue
|
||||
cycles: 5
|
||||
context: true
|
||||
private: false
|
||||
started: 2026-05-16T00:00:00Z
|
||||
finished: 2026-05-16T00:01:00Z
|
||||
---
|
||||
|
||||
## files
|
||||
- `resources/js/Pages/Home.vue` [lines 57-58] — COORDINATOR currently maps to `HomeShared`; must import and use `HomeCooperative` instead
|
||||
- `resources/js/Pages/Fragments/Home/HomeCooperative.vue` — full rewrite: currently only a chapter/geo-map component, needs full dashboard
|
||||
- `resources/js/Pages/Fragments/Home/HomeStoreOwner.vue` — reference pattern: BalanceBox + ServiceButtonGrid + HomeSkeleton
|
||||
- `resources/js/Components/Core/Stats/BalanceBox.vue` — stat card component (props: `stats[]`, `footerItems[]`, emits `footer-click`)
|
||||
- `resources/js/Components/Core/Services/ServiceButtonGrid.vue` — icon grid (prop: `items[]` with `icon`, `title`, `pagename` or `action`)
|
||||
- `resources/js/Components/Core/Services/SideTextButtonList.vue` — text list of quick actions
|
||||
- `resources/js/Components/Core/Skeleton/HomeSkeleton.vue` — loading skeleton
|
||||
- `resources/js/composables/usePageData.js` — `fetchPageData(url, payload)` pattern
|
||||
- `routes/web.php` [lines 76-135] — `/home-data` GET endpoint; must add COORDINATOR-scoped cooperative stats
|
||||
- `app/Http/Controllers/Market/CooperativeController.php` — available methods: list, show, registerMember, publicRegisterMember
|
||||
|
||||
## steps
|
||||
1. **`routes/web.php` @ `/home-data` closure** — After the existing `$myStoresCount` block (line ~114), add a COORDINATOR-specific stats block:
|
||||
- Check `$acctType === UserTypes::COORDINATOR`
|
||||
- Query `Organization::where('type','COOPERATIVE')->count()` → `cooperative_total_no`
|
||||
- Query `CooperativeMember::whereHas('organization', fn($q)=>$q->where('created_by',$user->id))->count()` → `cooperative_members_no`
|
||||
- Query `Organization::where('type','COOPERATIVE')->where('created_by',$user->id)->withCount('members')->count()` → `my_cooperatives_no`
|
||||
- Add `pending_members_no`: count of `cooperative_members` with `created_at >= now()->subDays(7)` under the coordinator's cooperatives
|
||||
- Include all four keys in `$props['props']['stats']` unconditionally (0 for non-coordinators is fine)
|
||||
|
||||
2. **`resources/js/Pages/Home.vue`** — Add import at top: `import HomeCooperative from './Fragments/Home/HomeCooperative.vue';`
|
||||
— Change line 57-58 from `<HomeShared title="Coordinator" />` to `<HomeCooperative />`
|
||||
|
||||
3. **`resources/js/Pages/Fragments/Home/HomeCooperative.vue`** — Full rewrite. Preserve the chapter map as a collapsible section at the bottom. New structure:
|
||||
|
||||
**Script:**
|
||||
- Imports: `ref, onMounted, computed, h`, `usePageData`, `useNavigate`, `useAuth`, `useModal`, `BalanceBox`, `ServiceButtonGrid`, `SideTextButtonList`, `HomeSkeleton`, and the existing `useChapters` (keep for map section)
|
||||
- Stats ref: `[{ title:'Cooperatives', number:0, unit:'Total', numberId:'cooperative_total_no' }, { title:'Members', number:0, unit:'Enrolled', numberId:'cooperative_members_no' }, { title:'New (7d)', number:0, unit:'Members', numberId:'pending_members_no' }]`
|
||||
- Footer items: `[{ title:'Cooperatives', icon:'<people icon url>', pagename:'CooperativeList' }, { title:'My Profile', icon:'<profile icon url>', pagename:'UserInfoEdit' }]`
|
||||
- `services` array: 8 items:
|
||||
1. `{ icon:'<people svg>', title:'Cooperatives', pagename:'CooperativeList' }`
|
||||
2. `{ icon:'<person-plus svg>', title:'Register Member', pagename:'CooperativeMemberRegister' }` (needs active coop hash — use `action:'registerMember'` to prompt)
|
||||
3. `{ icon:'<building svg>', title:'My Cooperative', action:'viewMyCoop' }`
|
||||
4. `{ icon:'<bar-chart svg>', title:'Reports', pagename:'ListReports' }`
|
||||
5. `{ icon:'<file-text svg>', title:'Documents', action:'viewDocs' }`
|
||||
6. `{ icon:'<check-square svg>', title:'Resolutions', action:'viewResolutions' }`
|
||||
7. `{ icon:'<people svg>', title:'Members', action:'viewMembers' }`
|
||||
8. `{ icon:'<map svg>', title:'Chapter Map', action:'toggleMap' }`
|
||||
- `quickActions` list items:
|
||||
- `{ text:'Create Cooperative', pagename:'CreateCooperative', icon:'...' }`
|
||||
- `{ text:'Member Ledger', pagename:'AccountingDashboard', icon:'...' }`
|
||||
- `{ text:'Add Transaction', pagename:'AddTransaction', icon:'...' }`
|
||||
- `{ text:'My Personal Profile', pagename:'UserInfoEdit', icon:'...' }`
|
||||
- `activeOrgHash`: `computed(() => user.value?.settings?.cooperatives?.[0] ?? null)`
|
||||
- `showMap`: `ref(false)` — toggled by `toggleMap` action
|
||||
- `viewMyCoop()`: if `activeOrgHash.value`, navigate to `CooperativeDetail` with `target: activeOrgHash.value`; else `modal.quickDismiss({title:'No Cooperative', body:'You have not joined a cooperative yet.'})`
|
||||
- `handleItemClick(item)`: dispatch on `item.action` or `navigate({ page: item.pagename })`
|
||||
- On `onMounted`: `fetchPageData('/home-data', {})` then `applyStats()`
|
||||
|
||||
**Template:**
|
||||
- `<HomeSkeleton v-if="loading" />`
|
||||
- BalanceBox with cooperative stats and footer items, `@footer-click="handleItemClick"`
|
||||
- `<h5>Cooperative Services</h5>` + `<ServiceButtonGrid :items="services" @item-click="handleItemClick" />`
|
||||
- `<h5>Quick Actions</h5>` + `<SideTextButtonList :items="quickActions" @item-click="handleItemClick" />`
|
||||
- Collapsible map section: `<button @click="showMap=!showMap">Chapter Map</button>` + `<div v-show="showMap">` containing the existing Leaflet map code (preserve all existing refs and lifecycle hooks, but only `initMap()` when `showMap` becomes true via `watch`)
|
||||
|
||||
**Style:** No `bg-white`/`bg-light`/`text-dark`. Use `var(--bg-card)`, `var(--text-primary)`. `:global(.dark-mode) .chapter-row { background: var(--bg-card) !important; }`.
|
||||
|
||||
4. **Icon URLs** — Reuse CDN URLs already in the project. Map to Font Awesome alternatives in the `icon` field or pick from existing `/assets/micons/` SVGs already used in `HomeUltimate.vue`. Acceptable: use `fas fa-*` class names as `icon` if `ServiceButtonGrid` supports icon class strings — check the component prop; if it only accepts URLs, use the same CDN icon URLs already referenced in `HomeShared.vue` and `HomeStoreOwner.vue` (people: `a/04d0e432a298.bin`, profile: `a/ac7a1cebe580.bin`, chart: `a/f87407046b18.bin`, docs: `a/c9fd442fe676.bin`).
|
||||
|
||||
5. **VueRouteMap check** — Confirm `CooperativeList`, `CooperativeDetail`, `CreateCooperative`, `CooperativeMemberRegister` all have `coordinator` in `allowedUserTypes`. If any are missing, add `'coordinator'` to their arrays in `app/Http/Controllers/Support/VueRouteMap.php`.
|
||||
|
||||
6. **UserPermissions check** — Confirm `UserTypes::COORDINATOR` has `ViewCooperatives`, `CreateCooperative`, `JoinCooperative`, `ViewAccountingReports` in `UserPermissions::roles()`. Add any missing ones.
|
||||
|
||||
## context
|
||||
```
|
||||
// routes/web.php ~line 92
|
||||
$acctType = $user?->acct_type instanceof \App\Enums\UserTypes
|
||||
? $user->acct_type
|
||||
: \App\Enums\UserTypes::tryFrom($user?->acct_type ?? '');
|
||||
// stats keys currently returned: total_users_no, active_stores_no, pending_orders_no,
|
||||
// total_balance_php, total_transactions_no, total_transactions_php, projected_income_today,
|
||||
// transactions_today_no, cash_flow_today_php, my_stores_no
|
||||
|
||||
// BalanceBox props:
|
||||
// stats: Array<{ title, number, unit, align?, numberId? }>
|
||||
// footerItems: Array<{ title, icon?, pagename?, action? }>
|
||||
// boxStyle: string (CSS)
|
||||
|
||||
// ServiceButtonGrid: prop items[] = [{ icon: URL|string, title, pagename?, action? }]
|
||||
// SideTextButtonList: prop items[] = [{ text, pagename?, action?, icon? }]
|
||||
// usePageData: { data, loading, error, fetchPageData(url, payload) }
|
||||
// applyStats pattern from HomeStoreOwner.vue:
|
||||
// stats.value = stats.value.map(s => {
|
||||
// const v = data.value?.stats[s.numberId];
|
||||
// return v !== undefined ? { ...s, number: v } : s;
|
||||
// });
|
||||
|
||||
// Home.vue line 57-58 currently:
|
||||
// <template v-else-if="isCoordinator">
|
||||
// <HomeShared title="Coordinator" />
|
||||
// </template>
|
||||
|
||||
// Organization model: type='COOPERATIVE', created_by=user.id
|
||||
// cooperative_members table: organization_id, user_id, created_at
|
||||
// active coop hash from: user.value?.settings?.cooperatives[0]
|
||||
```
|
||||
|
||||
## notes
|
||||
- dictionary: `ai-docs/dictionary.md`
|
||||
- linters: none detected
|
||||
- constraints: No `bg-white`/`bg-light`/`text-dark` hardcoding; use theme CSS vars. Leaflet map is lazy (only init when `showMap=true`). All icon URLs must use CDN pattern already established in the project. COORDINATOR must appear in `allowedUserTypes` for coop pages.
|
||||
Reference in New Issue
Block a user