8.3 KiB
task, cycles, context, private, started, finished
| task | cycles | context | private | started | finished |
|---|---|---|---|---|---|
| Redesign HomeCooperative.vue into a proper Coordinator/Cooperative-user dashboard and wire it into Home.vue | 5 | true | false | 2026-05-16T00:00:00Z | 2026-05-16T00:01:00Z |
files
resources/js/Pages/Home.vue[lines 57-58] — COORDINATOR currently maps toHomeShared; must import and useHomeCooperativeinsteadresources/js/Pages/Fragments/Home/HomeCooperative.vue— full rewrite: currently only a chapter/geo-map component, needs full dashboardresources/js/Pages/Fragments/Home/HomeStoreOwner.vue— reference pattern: BalanceBox + ServiceButtonGrid + HomeSkeletonresources/js/Components/Core/Stats/BalanceBox.vue— stat card component (props:stats[],footerItems[], emitsfooter-click)resources/js/Components/Core/Services/ServiceButtonGrid.vue— icon grid (prop:items[]withicon,title,pagenameoraction)resources/js/Components/Core/Services/SideTextButtonList.vue— text list of quick actionsresources/js/Components/Core/Skeleton/HomeSkeleton.vue— loading skeletonresources/js/composables/usePageData.js—fetchPageData(url, payload)patternroutes/web.php[lines 76-135] —/home-dataGET endpoint; must add COORDINATOR-scoped cooperative statsapp/Http/Controllers/Market/CooperativeController.php— available methods: list, show, registerMember, publicRegisterMember
steps
-
routes/web.php@/home-dataclosure — After the existing$myStoresCountblock (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 ofcooperative_memberswithcreated_at >= now()->subDays(7)under the coordinator's cooperatives - Include all four keys in
$props['props']['stats']unconditionally (0 for non-coordinators is fine)
- Check
-
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 /> -
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 existinguseChapters(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' }] servicesarray: 8 items:{ icon:'<people svg>', title:'Cooperatives', pagename:'CooperativeList' }{ icon:'<person-plus svg>', title:'Register Member', pagename:'CooperativeMemberRegister' }(needs active coop hash — useaction:'registerMember'to prompt){ icon:'<building svg>', title:'My Cooperative', action:'viewMyCoop' }{ icon:'<bar-chart svg>', title:'Reports', pagename:'ListReports' }{ icon:'<file-text svg>', title:'Documents', action:'viewDocs' }{ icon:'<check-square svg>', title:'Resolutions', action:'viewResolutions' }{ icon:'<people svg>', title:'Members', action:'viewMembers' }{ icon:'<map svg>', title:'Chapter Map', action:'toggleMap' }
quickActionslist 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 bytoggleMapactionviewMyCoop(): ifactiveOrgHash.value, navigate toCooperativeDetailwithtarget: activeOrgHash.value; elsemodal.quickDismiss({title:'No Cooperative', body:'You have not joined a cooperative yet.'})handleItemClick(item): dispatch onitem.actionornavigate({ page: item.pagename })- On
onMounted:fetchPageData('/home-data', {})thenapplyStats()
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 onlyinitMap()whenshowMapbecomes true viawatch)
Style: No
bg-white/bg-light/text-dark. Usevar(--bg-card),var(--text-primary).:global(.dark-mode) .chapter-row { background: var(--bg-card) !important; }. - Imports:
-
Icon URLs — Reuse CDN URLs already in the project. Map to Font Awesome alternatives in the
iconfield or pick from existing/assets/micons/SVGs already used inHomeUltimate.vue. Acceptable: usefas fa-*class names asiconifServiceButtonGridsupports icon class strings — check the component prop; if it only accepts URLs, use the same CDN icon URLs already referenced inHomeShared.vueandHomeStoreOwner.vue(people:a/04d0e432a298.bin, profile:a/ac7a1cebe580.bin, chart:a/f87407046b18.bin, docs:a/c9fd442fe676.bin). -
VueRouteMap check — Confirm
CooperativeList,CooperativeDetail,CreateCooperative,CooperativeMemberRegisterall havecoordinatorinallowedUserTypes. If any are missing, add'coordinator'to their arrays inapp/Http/Controllers/Support/VueRouteMap.php. -
UserPermissions check — Confirm
UserTypes::COORDINATORhasViewCooperatives,CreateCooperative,JoinCooperative,ViewAccountingReportsinUserPermissions::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-darkhardcoding; use theme CSS vars. Leaflet map is lazy (only init whenshowMap=true). All icon URLs must use CDN pattern already established in the project. COORDINATOR must appear inallowedUserTypesfor coop pages.