265 lines
12 KiB
Markdown
265 lines
12 KiB
Markdown
---
|
||
task: Implement COOP_MEMBER and COOP_OFFICER account types with geographic chapter hierarchy
|
||
cycles: 5
|
||
context: true
|
||
private: false
|
||
started: 2026-05-28T17:08:44Z
|
||
finished: 2026-05-28T17:09:30Z
|
||
---
|
||
|
||
## files
|
||
- `app/Enums/UserTypes.php` — add COOP_MEMBER and COOP_OFFICER cases
|
||
- `app/Enums/UserActions.php` — add ViewChapterOrgChart, ManageChapterMembers, ViewScopedMemberReports, AssignChapterOfficer
|
||
- `app/Http/Controllers/Helpers/Permissions/UserPermissions.php` lines 818–838 — add permission sets for new types; update UserTypeService::getAllowedUserTypes
|
||
- `app/Models/Chapter.php` — add cooperative_id to fillable; add cooperative() relationship
|
||
- `app/Models/ChapterMember.php` — add role field to fillable; add isOfficer() helper
|
||
- `database/migrations/2026_04_19_100001_create_chapters_table.php` — reference only, do NOT modify; write new migration instead
|
||
- `database/migrations/2026_04_19_100002_create_chapter_members_table.php` — reference only; write new migration for role column
|
||
- `app/Http/Controllers/Market/CooperativeController.php` — update registerMember to set acct_type=COOP_MEMBER if user is currently USER
|
||
- `app/Http/Controllers/Support/VueRouteMap.php` lines 217–311 — add new chapter pages; update existing coop pages to allow new types
|
||
- `resources/js/utils/UserTypes.js` — add COOP_MEMBER and COOP_OFFICER constants
|
||
- `resources/js/composables/Core/useAuth.js` lines 84–133 — add isCoopMember, isCoopOfficer computed properties
|
||
- `resources/js/Pages/Home.vue` lines 73–111 — add v-else-if blocks for COOP_OFFICER and COOP_MEMBER before the User fallback
|
||
- `resources/js/Pages/Fragments/Home/HomeCoopOfficer.vue` — NEW: officer dashboard showing chapter org chart + scoped member stats
|
||
- `resources/js/Pages/Fragments/Home/HomeCoopMember.vue` — NEW: member dashboard showing membership card, chapter info, wallet, marketplace
|
||
- `resources/js/Pages/ChapterOrgChart.vue` — NEW: org chart page filtered by officer's chapter level/geographic scope
|
||
- `resources/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 `position` field remains for free-text title; `role` is the canonical enum-like string
|
||
- Use Hypervel imports
|
||
|
||
### 3. UserTypes PHP enum — app/Enums/UserTypes.php
|
||
After `case COORDINATOR = 'coordinator';`, add:
|
||
```php
|
||
case COOP_OFFICER = 'coop officer';
|
||
case COOP_MEMBER = 'coop member';
|
||
```
|
||
|
||
### 4. UserActions PHP enum — app/Enums/UserActions.php
|
||
Add after the last existing case:
|
||
```php
|
||
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:
|
||
|
||
```php
|
||
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:
|
||
```php
|
||
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:
|
||
```php
|
||
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:
|
||
```php
|
||
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:
|
||
```js
|
||
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:
|
||
```js
|
||
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>`:
|
||
```js
|
||
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):
|
||
```html
|
||
<!-- 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:
|
||
```php
|
||
'/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: `auth` middleware
|
||
- Logic: determine caller's chapter scope (join `chapter_members` → `chapters` for 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/OrgChart` with `auth` middleware
|
||
|
||
### 18. Backend: /home-data — add COOP_OFFICER and COOP_MEMBER branches
|
||
In the controller that handles `GET /home-data`, add:
|
||
```php
|
||
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)
|
||
```php
|
||
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` → `HomeCoopOfficer` BEFORE `isUser` check
|
||
- Insert `isCoopMember` → `HomeCoopMember` BEFORE `isUser` check
|
||
|
||
### 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.
|