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
This commit is contained in:
241
app/Http/Controllers/ChapterController.php
Normal file
241
app/Http/Controllers/ChapterController.php
Normal file
@@ -0,0 +1,241 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user