Files
BarangaySystem/app/Http/Controllers/ChapterController.php
Jonathan Sykes fbb7e3ff37
Some checks failed
tests / PHP 8.2 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.3 (swoole-5.1.6) (push) Has been cancelled
tests / PHP 8.4 (swoole-6.0) (push) Has been cancelled
feat: implement barangay system phases 2-14
Complete adaptation from BukidBountyApp to Philippine barangay governance:

- Barangay models: Resident, Household, HouseholdMember, Blotter, BlotterHearing,
  DocumentRequest, RequestPayment, RequestType, BarangayProject, BarangayBudget
- Controllers: ResidentController, HouseholdController, BlotterController,
  BlotterHearingController, DocumentRequestController, RequestTypeController,
  ProjectController, BudgetController, QRPHController, AdminConsoleController,
  UserController, FileController, ChapterController, LoginController
- Vue pages: Home, ManageResidents, ResidentProfile, ManageHouseholds, ManageBlotters,
  BlotterDetail, RequestDocument, ManageDocumentRequests, DocumentRequestDetail,
  ManageRequestTypes, ManageProjects, BudgetLedger, AdminConsole
- Barangay roles: PunongBarangay, Kagawad, Secretary, Treasurer, SK, Tanod, BHW, Staff, Resident
- UserPermissions matrix rewritten with barangay-specific permission mappings
- VueRouteMap replaced with barangay SPA routes
- UserActions enum references corrected across all controllers
- Removed all market/cooperative/POS/subscription code and models
2026-06-07 03:09:09 +08:00

242 lines
8.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Controllers\Helpers\ResponseHelper;
use App\Http\Controllers\Helpers\Permissions\UserPermissions;
use App\Enums\UserActions;
use App\Models\Chapter;
use App\Models\ChapterMember;
use App\Models\User;
use App\Models\SystemSetting;
use App\Support\IslandGroupHelper;
use Hypervel\Http\Request;
use Hypervel\Support\Facades\Auth;
use Hypervel\Support\Facades\Validator;
use Hypervel\Support\Str;
class ChapterController
{
private const CHILD_LEVELS = [
'national' => ['region'],
'region' => ['province'],
'province' => ['city', 'municipal'],
'city' => ['barangay'],
'municipal' => ['barangay'],
'barangay' => ['purok'],
'purok' => [],
];
private function isAdmin(): bool
{
return UserPermissions::isActionPermitted(Auth::user()->acct_type, UserActions::ManageChapterMembers);
}
public function hierarchy(Request $request)
{
$chapterId = $request->input('chapter_id');
if (!$chapterId) {
$national = Chapter::where('level', 'national')->where('location_key', 'philippines')->first();
if (!$national) {
return response()->json(['chapters' => [], 'current' => null, 'breadcrumb' => []]);
}
$chapters = $national->children()
->where('is_active', true)
->withCount('activeMembers')
->with('leaders.user')
->get()
->map(fn ($c) => $this->formatChapter($c))
->values();
return response()->json(['chapters' => $chapters, 'current' => null, 'breadcrumb' => []]);
}
$current = Chapter::withCount('activeMembers')->with('leaders.user')->find($chapterId);
if (!$current) return ResponseHelper::returnError('Chapter not found', 404);
$chapters = $current->children()
->where('is_active', true)
->withCount('activeMembers')
->with('leaders.user')
->get()
->map(fn ($c) => $this->formatChapter($c))
->values();
return response()->json([
'chapters' => $chapters,
'current' => $this->formatChapter($current),
'breadcrumb' => $this->buildBreadcrumb($current),
]);
}
public function mapData(Request $request)
{
$level = $request->input('level', 'region');
$parentId = $request->input('parent_id');
$query = Chapter::where('level', $level)->where('is_active', true)->withCount('activeMembers');
if ($parentId) $query->where('parent_id', $parentId);
$chapters = $query->get()
->filter(fn ($c) => $c->lat && $c->lng)
->map(fn ($c) => [
'id' => $c->id,
'name' => $c->name,
'level' => $c->level,
'lat' => $c->lat,
'lng' => $c->lng,
'count' => $c->active_members_count,
])
->values();
return response()->json(['chapters' => $chapters]);
}
public function members(Request $request)
{
$chapterId = $request->input('chapter_id');
if (!$chapterId) return ResponseHelper::returnError('chapter_id required', 400);
$members = ChapterMember::with('user')
->where('chapter_id', $chapterId)
->where('is_active', true)
->get()
->map(fn ($cm) => [
'hashkey' => $cm->hashkey,
'name' => $cm->user?->fullname ?? $cm->user?->name,
'position' => $cm->position,
'photo' => $cm->user?->photourl ?? null,
'is_manual' => (bool) $cm->is_manual_override,
]);
return response()->json(['members' => $members]);
}
public function assignMember(Request $request)
{
if (!$this->isAdmin()) return ResponseHelper::returnUnauthorized();
$validator = Validator::make($request->all(), [
'user_hashkey' => 'required|string',
'chapter_id' => 'required|integer',
'position' => 'nullable|string|max:100',
]);
if ($validator->fails()) return ResponseHelper::returnError('Validation failed', 422, $validator->errors());
$user = User::where('hashkey', $request->input('user_hashkey'))->first();
if (!$user) return ResponseHelper::returnError('User not found', 404);
$chapter = Chapter::find($request->input('chapter_id'));
if (!$chapter) return ResponseHelper::returnError('Chapter not found', 404);
$existing = ChapterMember::where('user_id', $user->id)->where('chapter_id', $chapter->id)->first();
if ($existing) {
$existing->update([
'position' => $request->input('position'),
'is_manual_override' => true,
'assigned_by' => Auth::id(),
'assigned_at' => now(),
'updated_by' => Auth::id(),
]);
} else {
ChapterMember::create([
'hashkey' => (string) Str::uuid(),
'user_id' => $user->id,
'chapter_id' => $chapter->id,
'position' => $request->input('position'),
'is_manual_override' => true,
'is_active' => true,
'assigned_by' => Auth::id(),
'assigned_at' => now(),
'created_by' => Auth::id(),
]);
}
return response()->json(['success' => true]);
}
public function removeMember(Request $request)
{
if (!$this->isAdmin()) return ResponseHelper::returnUnauthorized();
$member = ChapterMember::where('hashkey', $request->input('hashkey'))->first();
if (!$member) return ResponseHelper::returnError('Member assignment not found', 404);
$member->update(['is_active' => false, 'updated_by' => Auth::id()]);
return response()->json(['success' => true]);
}
public function positions()
{
$raw = SystemSetting::getValue('chapter_positions');
$positions = is_string($raw) ? (json_decode($raw, true) ?? []) : [];
if (empty($positions)) {
$positions = ['Punong Barangay', 'Kagawad', 'Secretary', 'Treasurer', 'SK Chairperson', 'SK Councilor', 'Tanod', 'BHW', 'Daycare Worker', 'Staff', 'Member'];
}
return response()->json(['positions' => $positions]);
}
public function createChapter(Request $request)
{
if (!$this->isAdmin()) return ResponseHelper::returnUnauthorized();
$validator = Validator::make($request->all(), [
'name' => 'required|string|max:255',
'level' => 'required|in:national,region,province,city,municipal,barangay,purok',
'parent_id' => 'nullable|integer|exists:chapters,id',
'location_key' => 'nullable|string|max:255',
'lat' => 'nullable|numeric',
'lng' => 'nullable|numeric',
]);
if ($validator->fails()) {
return response()->json(['success' => false, 'errors' => $validator->errors()], 422);
}
$data = $validator->validated();
$data['hashkey'] = hash('sha256', uniqid((string) now(), true));
$data['is_active'] = true;
$data['created_by'] = Auth::id();
$data['updated_by'] = Auth::id();
$data['location_key'] = strtolower(trim($data['location_key'] ?? $data['name']));
$chapter = Chapter::create($data);
return response()->json(['success' => true, 'data' => $this->formatChapter($chapter), 'message' => 'Chapter created']);
}
private function formatChapter(Chapter $c): array
{
return [
'id' => $c->id,
'hashkey' => $c->hashkey,
'name' => $c->name,
'level' => $c->level,
'parent_id' => $c->parent_id,
'location_key' => $c->location_key,
'lat' => $c->lat,
'lng' => $c->lng,
'is_active' => $c->is_active,
'member_count' => $c->active_members_count ?? 0,
'child_levels' => self::CHILD_LEVELS[$c->level] ?? [],
];
}
private function buildBreadcrumb(Chapter $chapter): array
{
$crumbs = [];
$current = $chapter;
while ($current) {
array_unshift($crumbs, ['id' => $current->id, 'name' => $current->name, 'level' => $current->level]);
$current = $current->parent;
}
return $crumbs;
}
}