[ | 'component' => 'MyVueComponent', | 'middlewares' => ['auth'], | 'name' => 'my.route.name', | 'loginRequired' => true, | 'allowedUserTypes' => ['ult', 'operator'], | ], */ // Public pages - no login required '/' => [ 'component' => 'Home', 'loginRequired' => false, ], '/app' => [ 'component' => 'Home', 'loginRequired' => false, ], '/bukidbountyapp' => [ 'component' => 'Home', 'loginRequired' => false, ], // Market pages - public access '/list-products-market' => [ 'component' => 'ListProductsMarket', 'loginRequired' => false, ], '/list-stores' => [ 'component' => 'ListStores', 'loginRequired' => false, ], '/my-stores' => [ 'component' => 'MyStores', 'loginRequired' => true, 'module' => 'stores', ], '/buy-view-product-market' => [ 'component' => 'BuyViewProductMarket', 'loginRequired' => false, ], '/view-store-market' => [ 'component' => 'ViewStoreMarket', 'loginRequired' => false, ], '/view-all-photos' => [ 'component' => 'ViewAllPhotos', 'loginRequired' => false, ], '/photo-viewer' => [ 'component' => 'PhotoViewer', 'loginRequired' => false, ], '/create-store' => [ 'component' => 'CreateStore', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner'], 'module' => 'stores', ], '/pos' => [ 'component' => 'PosMain', 'loginRequired' => false, 'module' => 'pos', ], // Account settings - requires login '/account-settings' => [ 'component' => 'AccountSettings', 'loginRequired' => true, ], '/create-user' => [ 'component' => 'CreateUser', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'store owner', 'store manager', 'supplier overseer', 'supplier'], ], // Administrative & Management pages '/create-product' => [ 'component' => 'CreateProductUltimate', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner'], 'module' => 'products', ], '/add-products-to-store' => [ 'component' => 'AddProductsToStore', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'stores', ], '/create-product-store-owner' => [ 'component' => 'CreateProductStoreOwner', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'products', ], '/edit-product' => [ 'component' => 'EditProductUltimate', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner'], 'module' => 'products', ], '/edit-store' => [ 'component' => 'EditStoreUltimate', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'stores', ], '/transfer-credit' => [ 'component' => 'TransferMyCredit', 'loginRequired' => true, 'module' => 'credits', ], '/user-list' => [ 'component' => 'UserList', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'audit'], ], '/manage-transactions' => [ 'component' => 'ManageGlobalTransactions', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'transactions', ], '/remove-product' => [ 'component' => 'RemoveProductFromStoreAdmin', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'stores', ], '/assign-product-to-store' => [ 'component' => 'AssignProductToStore', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], ], '/manage-products' => [ 'component' => 'ManageProductsAdmin', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'products', ], '/manage-stores' => [ 'component' => 'ManageStoresAdmin', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'stores', ], '/pos-access-keys' => [ 'component' => 'PosAccessKeys', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'pos', ], '/add-transaction' => [ 'component' => 'AddTransaction', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'transactions', ], '/manage-product-admin' => [ 'component' => 'ManageProductAdmin', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'products', ], '/batch-add-products' => [ 'component' => 'BatchAddProducts', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner'], 'module' => 'batch', ], '/batch-add-stores' => [ 'component' => 'BatchAddStores', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'batch', ], '/batch-add-users' => [ 'component' => 'BatchAddUsers', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'batch', ], '/batch-add-cooperatives' => [ 'component' => 'BatchAddCooperatives', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator'], 'module' => 'batch', ], '/pos-history' => [ 'component' => 'PosHistory', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'pos', ], // Logistics & Shipments // Property Management '/list-properties' => [ 'component' => 'ListProperties', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'properties', ], '/list-referrals' => [ 'component' => 'ListReferrals', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'properties', ], // Reports '/list-reports' => [ 'component' => 'ListReports', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'accounting', ], '/shipment-list' => [ 'component' => 'ShipmentList', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager', 'rider', 'audit'], 'module' => 'shipments', ], '/shipment-detail' => [ 'component' => 'ShipmentDetail', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager', 'rider', 'audit'], 'module' => 'shipments', ], '/farmer-profile-edit' => [ 'component' => 'FarmerProfileEdit', 'loginRequired' => true, 'module' => 'farmers', ], '/verification-dashboard' => [ 'component' => 'VerificationDashboard', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'farmers', ], '/cooperative-list' => [ 'component' => 'CooperativeList', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer', 'coop member'], 'module' => 'cooperatives', ], '/chapter-org-chart' => [ 'component' => 'ChapterOrgChart', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer', 'coop member'], 'module' => 'cooperatives', ], '/coop-member-search' => [ 'component' => 'CoopMemberSearch', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer'], 'module' => 'cooperatives', ], '/create-coop-user' => [ 'component' => 'CreateCoopUser', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer'], 'module' => 'cooperatives', ], '/assign-chapter-officer' => [ 'component' => 'AssignChapterOfficer', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer'], 'module' => 'cooperatives', ], '/create-chapter' => [ 'component' => 'CreateChapter', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer'], 'module' => 'cooperatives', ], '/register-chapter' => [ 'component' => 'RegisterChapter', 'loginRequired' => false, 'module' => 'cooperatives', ], '/create-cooperative' => [ 'component' => 'CreateCooperative', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'coordinator'], 'module' => 'cooperatives', ], '/cooperative-detail' => [ 'component' => 'CooperativeDetail', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator', 'coop officer', 'coop member'], 'module' => 'cooperatives', ], '/enroll-farmer' => [ 'component' => 'EnrollFarmer', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator'], 'module' => 'farmers', ], '/cooperative-member-register' => [ 'component' => 'CooperativeMemberRegister', 'loginRequired' => true, 'module' => 'cooperatives', ], '/register-coop' => [ 'component' => 'RegisterCoop', 'loginRequired' => false, 'module' => 'cooperatives', ], '/user-registration' => [ 'component' => 'UserRegistration', 'loginRequired' => false, ], '/user-info-edit' => [ 'component' => 'UserInfoEdit', 'loginRequired' => true, ], '/ultimate-console' => [ 'component' => 'UltimateConsole', 'loginRequired' => true, 'allowedUserTypes' => ['ult'], ], '/system-settings' => [ 'component' => 'SystemSettings', 'loginRequired' => true, 'allowedUserTypes' => ['ult'], ], '/landing-page-editor' => [ 'component' => 'LandingPageEditor', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'coordinator'], 'module' => 'landing_pages', ], '/accounting-dashboard' => [ 'component' => 'AccountingDashboard', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'accounting', 'store_module' => 'accounting_store', ], '/manage-accounts' => [ 'component' => 'ManageAccounts', 'loginRequired' => true, 'allowedUserTypes' => ['ult', 'super operator', 'operator', 'store owner', 'store manager'], 'module' => 'accounting', 'store_module' => 'accounting_store', ], ]; /** * 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::ULTIMATE) { 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::ULTIMATE) { 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); } }