12 KiB
task, cycles, context, private, started, finished
| task | cycles | context | private | started | finished |
|---|---|---|---|---|---|
| Implement COOP_MEMBER and COOP_OFFICER account types with geographic chapter hierarchy | 5 | true | false | 2026-05-28T17:08:44Z | 2026-05-28T17:09:30Z |
files
app/Enums/UserTypes.php— add COOP_MEMBER and COOP_OFFICER casesapp/Enums/UserActions.php— add ViewChapterOrgChart, ManageChapterMembers, ViewScopedMemberReports, AssignChapterOfficerapp/Http/Controllers/Helpers/Permissions/UserPermissions.phplines 818–838 — add permission sets for new types; update UserTypeService::getAllowedUserTypesapp/Models/Chapter.php— add cooperative_id to fillable; add cooperative() relationshipapp/Models/ChapterMember.php— add role field to fillable; add isOfficer() helperdatabase/migrations/2026_04_19_100001_create_chapters_table.php— reference only, do NOT modify; write new migration insteaddatabase/migrations/2026_04_19_100002_create_chapter_members_table.php— reference only; write new migration for role columnapp/Http/Controllers/Market/CooperativeController.php— update registerMember to set acct_type=COOP_MEMBER if user is currently USERapp/Http/Controllers/Support/VueRouteMap.phplines 217–311 — add new chapter pages; update existing coop pages to allow new typesresources/js/utils/UserTypes.js— add COOP_MEMBER and COOP_OFFICER constantsresources/js/composables/Core/useAuth.jslines 84–133 — add isCoopMember, isCoopOfficer computed propertiesresources/js/Pages/Home.vuelines 73–111 — add v-else-if blocks for COOP_OFFICER and COOP_MEMBER before the User fallbackresources/js/Pages/Fragments/Home/HomeCoopOfficer.vue— NEW: officer dashboard showing chapter org chart + scoped member statsresources/js/Pages/Fragments/Home/HomeCoopMember.vue— NEW: member dashboard showing membership card, chapter info, wallet, marketplaceresources/js/Pages/ChapterOrgChart.vue— NEW: org chart page filtered by officer's chapter level/geographic scoperesources/js/composables/useChapters.js— may need fetchOfficerScope() method
steps
1. New migration — add cooperative_id to chapters
Create database/migrations/2026_05_28_000001_add_cooperative_id_to_chapters.php:
$table->unsignedBigInteger('cooperative_id')->nullable()->after('name');$table->foreign('cooperative_id')->references('id')->on('organizations')->nullOnDelete();- Add
$table->index('cooperative_id'); - Use Hypervel imports (NOT Illuminate):
use Hyperf\Database\Schema\Blueprint; use Hypervel\Database\Migrations\Migration; use Hypervel\Support\Facades\Schema;
2. New migration — add role to chapter_members
Create database/migrations/2026_05_28_000002_add_role_to_chapter_members.php:
$table->string('role')->nullable()->after('position')->comment('Officer role: PRESIDENT, VICE_PRESIDENT, SECRETARY, TREASURER, AUDITOR, BOARD_MEMBER, MEMBER');- The existing
positionfield remains for free-text title;roleis the canonical enum-like string - Use Hypervel imports
3. UserTypes PHP enum — app/Enums/UserTypes.php
After case COORDINATOR = 'coordinator';, add:
case COOP_OFFICER = 'coop officer';
case COOP_MEMBER = 'coop member';
4. UserActions PHP enum — app/Enums/UserActions.php
Add after the last existing case:
case ViewChapterOrgChart = 'viewchapterorgchart';
case ManageChapterMembers = 'managechaptermembers';
case ViewScopedMemberReports = 'viewscopedmemberreports';
case AssignChapterOfficer = 'assignchapterofficer';
5. UserPermissions — add new type permission sets
In UserPermissions::roles() after the UserTypes::USER->value block (line 818), add:
UserTypes::COOP_MEMBER->value => [
UserActions::JoinCooperative,
UserActions::ViewUserInfo,
UserActions::ManageUserInfo,
UserActions::ViewChapterOrgChart,
],
UserTypes::COOP_OFFICER->value => [
UserActions::JoinCooperative,
UserActions::ViewUserInfo,
UserActions::ManageUserInfo,
UserActions::ViewOrganizations,
UserActions::ViewChapterOrgChart,
UserActions::ManageChapterMembers,
UserActions::ViewScopedMemberReports,
UserActions::AssignChapterOfficer,
UserActions::ViewAccountingReports,
UserActions::CheckifMobileNumberExists,
UserActions::CheckifUsernameExists,
],
Also add ViewChapterOrgChart, ManageChapterMembers, ViewScopedMemberReports, AssignChapterOfficer to $RoleswithNoTargetUser array.
6. UserPermissions — getAllowedUserTypes
In UserTypeService::getAllowedUserTypes(), add COOP_OFFICER and COOP_MEMBER:
- COORDINATOR can create COOP_OFFICER and COOP_MEMBER
- COOP_OFFICER can create COOP_MEMBER
- ULTIMATE/SUPER_OPERATOR/OPERATOR can create both (already implied by wildcard but add explicitly)
7. Chapter model — add cooperative_id
In app/Models/Chapter.php, add 'cooperative_id' to $fillable and add relationship:
public function cooperative() {
return $this->belongsTo(\App\Models\Market\Organization::class, 'cooperative_id');
}
8. ChapterMember model — add role to fillable
In app/Models/ChapterMember.php, add 'role' to $fillable.
Add helper:
public function isOfficer(): bool {
return !empty($this->role) && $this->role !== 'MEMBER';
}
9. CooperativeController — update registerMember to set acct_type
In CooperativeController@registerMember (and publicRegisterMember), after successfully creating/confirming the cooperative_members row, check:
if ($user->acct_type === UserTypes::USER) {
$user->acct_type = UserTypes::COOP_MEMBER;
$user->save();
}
This upgrades plain USER accounts to COOP_MEMBER on cooperative registration. Do not downgrade any existing type that is higher (COORDINATOR, OPERATOR, etc.).
10. Frontend UserTypes.js
In resources/js/utils/UserTypes.js, add:
COOP_OFFICER: 'coop officer',
COOP_MEMBER: 'coop member',
11. useAuth.js — add computed helpers
In resources/js/composables/Core/useAuth.js, after isUser (line 113), add:
const isCoopOfficer = computed(() => role.value === UserTypes.COOP_OFFICER);
const isCoopMember = computed(() => role.value === UserTypes.COOP_MEMBER);
Export both from the return object.
12. Home.vue — add dashboard fragments for new types
Import two new components at top of <script setup>:
import HomeCoopOfficer from './Fragments/Home/HomeCoopOfficer.vue';
import HomeCoopMember from './Fragments/Home/HomeCoopMember.vue';
Add to destructured useAuth(): isCoopOfficer, isCoopMember.
In the template, insert BEFORE the v-else-if="isUser" block (line 109):
<!-- Coop Officer -->
<template v-else-if="isCoopOfficer">
<HomeCoopOfficer />
</template>
<!-- Coop Member -->
<template v-else-if="isCoopMember">
<HomeCoopMember />
</template>
13. HomeCoopOfficer.vue — NEW officer dashboard
resources/js/Pages/Fragments/Home/HomeCoopOfficer.vue
Key elements:
- Stats card: member count in officer's chapter scope, sub-chapter count, new members (7d)
- Officer's chapter badge showing their level (BARANGAY / MUNICIPAL / PROVINCIAL / etc.) and geographic name
- ServiceButtonGrid with: Chapter Org Chart (
ChapterOrgChart), Members, Reports, My Profile, Cooperative Detail - SideTextButtonList with: Assign Officer, Add Member, Member Ledger
- Fetch data from
/home-data(reuse existing endpoint; add coop_officer branch on backend if stats differ) - The officer's chapter info comes from
user.value?.chapter_membership(add to home-data response or fetch separately via/Chapters/MyChapters)
14. HomeCoopMember.vue — NEW member dashboard
resources/js/Pages/Fragments/Home/HomeCoopMember.vue
Key elements:
- Membership card: user name, membership type, chapter name (their barangay/municipal chapter), member since
- Stats: wallet balance (if applicable), chapter contact officer name
- ServiceButtonGrid with: Cooperative Detail, My Profile, Marketplace, My Wallet
- SideTextButtonList with: Member Ledger, View Chapter
- Fetch stats from
/home-data(add member branch)
15. ChapterOrgChart.vue — NEW page
resources/js/Pages/ChapterOrgChart.vue
- Fetches chapter hierarchy from
/Chapters/OrgChart(new backend endpoint) - Backend scopes response by caller's
acct_type:- COOP_OFFICER: return the subtree rooted at their highest chapter
- COORDINATOR/OPERATOR/Big3: return full tree or coop-scoped tree
- COOP_MEMBER: return their barangay chapter and its officers only (read-only)
- Renders a tree: collapsible nodes per level, each showing chapter name, officer names + roles
- Route:
/chapter-org-chart(no hash needed unless scoped to a specific coop)
16. VueRouteMap.php — register new pages
Add entries:
'/chapter-org-chart' => [
'component' => 'ChapterOrgChart',
'loginRequired' => true,
'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer', 'coop member'],
'module' => 'cooperatives',
],
Update existing cooperative pages (cooperative-list, cooperative-detail, cooperative-member-register) to also allow 'coop officer' and 'coop member' in allowedUserTypes.
17. Backend: /Chapters/OrgChart endpoint
In ChapterController.php, add method getOrgChart:
- Auth:
authmiddleware - Logic: determine caller's chapter scope (join
chapter_members→chaptersfor the caller user) - For COOP_OFFICER: find their chapter_members rows, get the highest-level chapter they are in, return full subtree via recursive children() eager load
- For COOP_MEMBER: return just their barangay chapter + officers
- For Big3/COORDINATOR: accept optional
?cooperative_id=param, return that coop's full chapter tree - Response: nested JSON
{ id, hashkey, name, level, location_key, children: [...], officers: [{user_id, name, role, position}] } - Register route in
routes/web.php:POST /Chapters/OrgChartwithauthmiddleware
18. Backend: /home-data — add COOP_OFFICER and COOP_MEMBER branches
In the controller that handles GET /home-data, add:
if ($user->acct_type === UserTypes::COOP_OFFICER) {
// count members in scope, sub-chapters, new members (7d)
// attach chapter info (name, level, location_key) from chapter_members join
}
if ($user->acct_type === UserTypes::COOP_MEMBER) {
// attach membership info: chapter name, officer contact, member since
}
context
Existing chapters migration (canonical)
level enum: ['national', 'region', 'province', 'city', 'barangay']
parent_id → chapters.id (nullOnDelete — chapter survives parent deletion)
location_key — normalized slug for address matching
NOTE: schema uses 'city' not 'municipal'. In PH context a city/municipality are different — if 'municipal' level is required, add it to the enum in migration step 1 or handle in the plan as a separate sub-step.
chapter_members.position
Free-text string from chapter_positions system setting. The new role field (step 2) is the canonical enum: PRESIDENT, VICE_PRESIDENT, SECRETARY, TREASURER, AUDITOR, BOARD_MEMBER, MEMBER.
UserPermissions::roles() — existing USER block (line 818)
UserTypes::USER->value => [
UserActions::JoinCooperative,
UserActions::ViewUserInfo,
UserActions::ManageUserInfo,
],
Insert COOP_MEMBER and COOP_OFFICER blocks immediately after this.
Home.vue fragment routing pattern
isCoordinator→HomeCooperative(already exists, used by COORDINATOR)- Insert
isCoopOfficer→HomeCoopOfficerBEFOREisUsercheck - Insert
isCoopMember→HomeCoopMemberBEFOREisUsercheck
CooperativeController::registerMember acct_type upgrade rule
Only upgrade USER → COOP_MEMBER. Never downgrade COORDINATOR, OPERATOR, STORE_OWNER, etc. Check: $user->acct_type === UserTypes::USER.
Definition of Done checklist reminder
- UserActions::ViewChapterOrgChart etc. added to UserActions.php
- UserPermissions::roles() entries for COOP_MEMBER and COOP_OFFICER
- VueRouteMap allowedUserTypes updated for /chapter-org-chart and existing coop pages
- Direct URL access tested for /chapter-org-chart
- No bg-white/bg-light hardcoded in new Vue fragments
- New raw DB queries (if any) include created_by/updated_by
notes
- dictionary: ai-docs/dictionary.md
- linters: none detected
- constraints: Use Hypervel imports (NOT Illuminate) in all migrations. chapters.level enum uses 'city' not 'municipal' — discuss with user before changing enum or treat city=municipal for now. The chapter auto-assignment (Chapter::autoAssignUser) already runs on UserInfo save and covers COOP_MEMBER address-based assignment. COOP_OFFICER chapter assignment is manual/administrative only.