['component' => 'Home', 'loginRequired' => false], '/app' => ['component' => 'Home', 'loginRequired' => false], '/barangaysystem' => ['component' => 'Home', 'loginRequired' => false], // ── Dashboard / Home '/home' => ['component' => 'Home', 'loginRequired' => true], '/dashboard' => ['component' => 'Home', 'loginRequired' => true], // ── Auth '/accountsettings' => ['component' => 'AccountSettings', 'loginRequired' => true], // ── Announcements '/manageannouncements' => ['component' => 'ManageAnnouncements', 'loginRequired' => true, 'module' => 'announcements'], // ── System Settings / Admin '/systemsettings' => ['component' => 'SystemSettings', 'loginRequired' => true], '/landingpageeditor' => ['component' => 'LandingPageEditor', 'loginRequired' => true], '/adminconsole' => ['component' => 'AdminConsole', 'loginRequired' => true], // ── User Management '/userlist' => ['component' => 'UserList', 'loginRequired' => true], '/createuser' => ['component' => 'CreateUser', 'loginRequired' => true], '/edituser' => ['component' => 'EditUser', 'loginRequired' => true], '/manageuser' => ['component' => 'ManageUser', 'loginRequired' => true], '/userregistration' => ['component' => 'UserRegistration', 'loginRequired' => true], // ── Chapter Hierarchy '/createchapter' => ['component' => 'CreateChapter', 'loginRequired' => true, 'module' => 'chapters'], '/registerchapter' => ['component' => 'RegisterChapter', 'loginRequired' => true, 'module' => 'chapters'], '/chapterorgchart' => ['component' => 'ChapterOrgChart', 'loginRequired' => true, 'module' => 'chapters'], '/assignchapterofficer' => ['component' => 'AssignChapterOfficer', 'loginRequired' => true, 'module' => 'chapters'], // ── Barangay Residents '/barangay/manageresidents' => ['component' => 'Barangay.ManageResidents', 'loginRequired' => true, 'module' => 'residents'], '/barangay/residentprofile' => ['component' => 'Barangay.ResidentProfile', 'loginRequired' => true, 'module' => 'residents'], // ── Barangay Households '/barangay/managehouseholds' => ['component' => 'Barangay.ManageHouseholds', 'loginRequired' => true, 'module' => 'households'], // ── Blotters '/barangay/manageblotters' => ['component' => 'Barangay.ManageBlotters', 'loginRequired' => true, 'module' => 'blotters'], '/barangay/blotterdetail' => ['component' => 'Barangay.BlotterDetail', 'loginRequired' => true, 'module' => 'blotters'], // ── Document Requests '/barangay/requestdocument' => ['component' => 'Barangay.RequestDocument', 'loginRequired' => true, 'module' => 'certificates'], '/barangay/managedocumentrequests' => ['component' => 'Barangay.ManageDocumentRequests','loginRequired' => true, 'module' => 'documents'], '/barangay/documentrequestdetail' => ['component' => 'Barangay.DocumentRequestDetail','loginRequired' => true, 'module' => 'documents'], '/barangay/managerequesttypes' => ['component' => 'Barangay.ManageRequestTypes', 'loginRequired' => true, 'module' => 'documents'], // ── Projects '/barangay/manageprojects' => ['component' => 'Barangay.ManageProjects', 'loginRequired' => true, 'module' => 'projects'], // ── Budget '/barangay/budgetledger' => ['component' => 'Barangay.BudgetLedger', 'loginRequired' => true, 'module' => 'budget'], ]; /** * Helper method to check if user is allowed based on their type. * Returns true if the user can access the page, false otherwise. */ private static function isUserAllowed(string $currentUserType, array $allowedUserTypes): bool { // If no restrictions specified, allow all authenticated users if (empty($allowedUserTypes)) { return true; } // Check if user type matches any allowed types foreach ($allowedUserTypes as $type) { if (is_string($type) && $currentUserType === $type) { return true; } } return false; } /** * Registers the defined Vue routes into the application. * This should be called in `routes/web.php`. */ public static function registerRoutes(): void { foreach (self::$routes as $uri => $settings) { $component = $settings['component'] ?? ''; $middlewares = $settings['middlewares'] ?? []; $name = $settings['name'] ?? null; $loginRequired = $settings['loginRequired'] ?? false; $allowedUserTypes = $settings['allowedUserTypes'] ?? []; $options = []; if (!empty($middlewares)) { $options['middleware'] = $middlewares; } if ($name) { $options['as'] = $name; } // Add login requirement middleware if needed if ($loginRequired) { $options['middleware'] = array_unique(array_merge($options['middleware'] ?? [], ['auth'])); } // Add module requirement middleware if specified $moduleKey = $settings['module'] ?? null; if ($moduleKey) { $options['middleware'] = array_unique(array_merge($options['middleware'] ?? [], ["module:{$moduleKey}"])); } Route::get($uri, function (...$routeParams) use ($component, $loginRequired, $allowedUserTypes, $moduleKey) { // Check if user is authenticated if (empty(Auth::user())) { // If login is required but not available, redirect to login page if ($loginRequired) { return redirect('/login'); } } // Check if module is enabled if ($moduleKey && ModuleHelper::isDisabled($moduleKey)) { return redirect('/'); } // Global Page Disabled Check /** @var \App\Models\User $user */ $user = Auth::user(); $disabledPages = \App\Models\SystemSetting::getValue('disabled_pages', []); if (is_array($disabledPages) && in_array(strtolower((string)$component), array_map('strtolower', $disabledPages))) { // Ultimate accounts can still access to allow fixing settings if (!$user || $user->acct_type !== UserTypes::SUPER_ADMIN) { return redirect('/'); } } // Check allowed user types if specified if (!empty($allowedUserTypes)) { $currentUserType = $user->acct_type?->value ?? $user->acct_type ?? UserTypes::PUBLIC->value; $isAllowed = self::isUserAllowed($currentUserType, $allowedUserTypes); if (!$isAllowed) { // Redirect to a forbidden page or login return redirect('/login'); } } // Return Inertia page properly set up with current user data // Pass parameterized URL segments to the Vue component as properties $page = Inertia::render($component, [ 'user' => Auth::user(), 'routeParams' => empty($routeParams) ? null : $routeParams ]); return view('layouts/application-layout', compact('page')); }, $options); } } /** * Handles a generic SPA request by converting the path into a component name. * This acts as a catch-all universal router. */ public static function handleSpa(string $path = '/') { $path = trim($path, '/'); // Get user type for access control /** @var \App\Models\User $user */ $user = Auth::user(); $currentUserType = null; if ($user) { $currentUserType = $user->acct_type?->value ?? $user->acct_type ?? UserTypes::PUBLIC->value; } // Strip hashkey/payload suffix from path (e.g., "edituser--h:HASHKEY" -> "edituser") // Use RouteArgumentParser to properly extract the base component name $component = $path; try { $parser = new \App\Support\RouteArgumentParser(); $parsedData = $parser->parseArgument($path); // If we have a hash or payload format, use the slug as the component if (isset($parsedData['slug']) && ($parsedData['type'] === 'hash' || $parsedData['type'] === 'payload')) { $component = $parsedData['slug']; } } catch (\Throwable $th) { // If parsing fails or no hash/payload format, use original path } // fallback to Home if empty $component = empty($component) ? 'Home' : $component; // Convert lowercase component names to camelCase for Vue component matching // e.g., "edituser" -> "EditUser" $vueComponent = self::toCamelCase($component); // Find base component from routes map using the route key $componentLower = strtolower($component); $routeKey = '/' . $componentLower; $loginRequired = true; // Default: require login $pathWithSlash = '/' . ltrim($path, '/'); $pathLower = strtolower($pathWithSlash); $routeSettings = self::$routes[$pathWithSlash] ?? self::$routes[$pathLower] ?? null; $allowedUserTypes = []; $moduleKey = null; $foundInMap = false; if ($routeSettings) { $settings = $routeSettings; $vueComponent = $settings['component'] ?? $vueComponent; $loginRequired = $settings['loginRequired'] ?? true; $allowedUserTypes = $settings['allowedUserTypes'] ?? []; $moduleKey = $settings['module'] ?? null; $foundInMap = true; } else { // Try hyphen-insensitive match (e.g. "viewstoremarket" vs "view-store-market") $cleanSlug = str_replace(['-', '_'], '', $componentLower); foreach (self::$routes as $uri => $settings) { $cleanUri = str_replace(['-', '_'], '', strtolower(trim($uri, '/'))); if ($cleanUri === $cleanSlug) { $vueComponent = $settings['component'] ?? $vueComponent; $loginRequired = $settings['loginRequired'] ?? true; $allowedUserTypes = $settings['allowedUserTypes'] ?? []; $moduleKey = $settings['module'] ?? null; $foundInMap = true; break; } } if (!$foundInMap) { // Fallback: search by Vue component name (case-insensitive) in case they passed the component direct name foreach (self::$routes as $uri => $settings) { $mappedComponent = $settings['component'] ?? ''; if (strcasecmp($mappedComponent, $vueComponent) === 0) { $vueComponent = $mappedComponent; // Use the correctly cased name from map $loginRequired = $settings['loginRequired'] ?? true; $allowedUserTypes = $settings['allowedUserTypes'] ?? []; $moduleKey = $settings['module'] ?? null; $foundInMap = true; break; } } } } // Enforce login requirement if ($loginRequired && empty($user)) { return redirect('/login'); } // Enforce module requirement if ($moduleKey && ModuleHelper::isDisabled($moduleKey)) { return redirect('/'); } // For store-level users, check the store-specific module toggle $storeModuleKey = $routeSettings['store_module'] ?? null; if ($storeModuleKey && in_array($currentUserType, ['store owner', 'store manager'])) { if (ModuleHelper::isDisabled($storeModuleKey)) { return redirect('/'); } } // Global Page Disabled Check $disabledPages = \App\Models\SystemSetting::getValue('disabled_pages', []); if (is_array($disabledPages) && in_array(strtolower((string)$vueComponent), array_map('strtolower', $disabledPages))) { // Ultimate accounts can still access to allow fixing settings if (!$user || $user->acct_type !== UserTypes::SUPER_ADMIN) { return redirect('/'); } } // Check allowed user types if specified if (!empty($allowedUserTypes) && $currentUserType !== null) { $isAllowed = self::isUserAllowed($currentUserType, $allowedUserTypes); if (!$isAllowed) { // Redirect to a forbidden page or login return redirect('/login'); } } // Pass standard Inertia props with hashkey if present $props = [ 'user' => Auth::user(), ]; // Add hashkey to props if it was in the URL for special pages if (isset($parsedData['type']) && $parsedData['type'] === 'hash') { // Provide the hash value under multiple common prop names for better compatibility $props['hashkey'] = $parsedData['value']; $props['target'] = $parsedData['value']; $props['id'] = $parsedData['value']; } elseif (isset($parsedData['type']) && $parsedData['type'] === 'payload') { $props['payload'] = $parsedData['value']; } $page = Inertia::render($vueComponent, $props); return view('layouts/application-layout', compact('page')); } /** * Convert snake_case or lowercase to camelCase for Vue component matching. */ private static function toCamelCase(string $name): string { // Replace slashes with dots first to handle directory structures $name = str_replace('/', '.', $name); // Split by dot (hierarchy) $parts = explode('.', $name); $pascalParts = array_map(function($part) { // Split by hyphen and capitalize each part for PascalCase $subParts = explode('-', $part); return implode('', array_map('ucfirst', $subParts)); }, $parts); // Return with dots if original had them, otherwise just one string return implode('.', $pascalParts); } }