initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View File

@@ -0,0 +1,264 @@
---
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 818838 — 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 217311 — 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 84133 — add isCoopMember, isCoopOfficer computed properties
- `resources/js/Pages/Home.vue` lines 73111 — 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.