['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; } }