Select-Project Route Enhancements
Side navigation message and dual-mode search implementation
๐ฏ Select-Project Route Enhancements
Date: October 29, 2025
Time: Morning
Status: โ
COMPLETE
Purpose: Enhance UX on /select-project route with smart navigation and search behavior
๐ Overview
Implemented intelligent behavior for the /select-project route to improve user experience:
- Side Navigation: Show helpful message instead of empty/irrelevant navigation
- Smart Search: Dual-mode search that filters projects on selection page, searches content elsewhere
- Proper Scoping: Respect project settings for cross-project search and private project isolation
โ Feature 1: Side Navigation Message
What Changed
When users are on /select-project route, the side navigation now displays a helpful message instead of showing navigation structure (which doesn't exist yet since no project is selected).
Implementation
File: apps/docs-angular/src/app/shared/docs-side-nav/docs-side-nav.component.ts
// ๐ฏ PROJECT-SCOPED: Detect if we're on the project selection route
readonly isSelectProjectRoute = computed(() => {
const currentUrl = this.router.url;
return currentUrl === '/select-project' || currentUrl.startsWith('/select-project');
});
File: apps/docs-angular/src/app/shared/docs-side-nav/docs-side-nav.component.html
<!-- ๐ฏ PROJECT SELECTION MESSAGE - Show helpful message when on selection route -->
@if (isSelectProjectRoute()) {
<div class="project-selection-message">
<div class="message-icon">๐</div>
<h3 class="message-title">Select a Project</h3>
<p class="message-text">Please select a project to view the navigation structure.</p>
<div class="message-hint">
<svg>...</svg>
<span>Choose from the available projects above</span>
</div>
</div>
} @else {
<!-- Regular navigation structure -->
}
File: apps/docs-angular/src/app/shared/docs-side-nav/docs-side-nav.component.scss
.project-selection-message {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem 2rem;
text-align: center;
gap: 1.5rem;
animation: fadeIn 0.4s cubic-bezier(0.23, 1, 0.32, 1);
.message-icon {
font-size: 4rem;
opacity: 0.9;
animation: floatIcon 3s ease-in-out infinite;
}
.message-title {
font-size: 1.5rem;
font-weight: 700;
color: var(--text-primary, #1f2937);
}
.message-text {
font-size: 1rem;
color: var(--text-secondary, #6b7280);
max-width: 220px;
line-height: 1.6;
}
.message-hint {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1rem;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(139, 92, 246, 0.06) 100%);
border: 1px solid rgba(59, 130, 246, 0.15);
border-radius: 12px;
font-size: 0.875rem;
color: var(--text-secondary, #6b7280);
}
}
Benefits
- โ Clear, helpful guidance for users
- โ Beautiful animated message
- โ No confusing empty navigation
- โ Encourages users to select a project
โ Feature 2: Dual-Mode Search
What Changed
Search now operates in two intelligent modes:
- Project Filter Mode (on
/select-project): Filters available projects by name, description, tags - Content Search Mode (everywhere else): Searches documentation content with proper scoping
Implementation
File: apps/docs-angular/src/app/services/search.service.ts
// ๐ฏ SEARCH MODE: Detect if we're on project selection page
readonly searchMode = computed(() => {
const currentUrl = this.router.url;
const isSelectProject = currentUrl === '/select-project' || currentUrl.startsWith('/select-project');
return isSelectProject ? 'project-filter' : 'content-search';
});
private performSearch(term: string): SearchResult[] {
const mode = this.searchMode();
if (mode === 'project-filter') {
return this.performProjectFilter(term);
} else {
return this.performContentSearch(term);
}
}
Project Filter Logic:
private performProjectFilter(term: string): SearchResult[] {
const searchTerms = term.toLowerCase().split(/\s+/);
const activeLanguage = this.documentationService.currentLanguage();
const rootProjects = this.accessService.getRootProjectsFor(languageKey);
const results: SearchResult[] = [];
for (const project of rootProjects) {
let score = 0;
// Score based on matches
for (const term of searchTerms) {
if (projectNameLower.includes(term)) score += 20;
if (displayNameLower.includes(term)) score += 18;
if (descriptionLower.includes(term)) score += 10;
if (tagsLower.some(tag => tag.includes(term))) score += 15;
}
if (score > 0) {
results.push({
title: project.displayName || project.name,
path: '/select-project',
excerpt: project.description,
section: 'Projects',
score,
type: 'page',
projectName: project.name
});
}
}
return results.sort((a, b) => b.score - a.score);
}
Content Search Logic (enhanced):
private performContentSearch(term: string): SearchResult[] {
const effectiveConfig = this.getEffectiveSearchConfig(currentProject);
let filteredIndex = this.searchIndex.filter(i => {
// RULE 1: Language filter
if (i.language !== activeLanguage) return false;
// RULE 2: Scope to current project if configured
if (effectiveConfig.scopeToCurrentProject && !effectiveConfig.crossProjectSearch) {
if (resultProjectName !== currentProject) return false;
}
// RULE 3: Check access permissions
const canAccess = this.accessService.canAccessProject(resultProjectId);
if (!canAccess) return false;
// RULE 4: Private projects only searchable from within themselves
if (i.visibility === 'private' && resultProjectName !== currentProject) {
return false;
}
// RULE 5: Respect crossProjectSearch setting
if (!effectiveConfig.crossProjectSearch && resultProjectName !== currentProject) {
return false;
}
return true;
});
// ... rest of search logic
}
UI Updates:
File: apps/docs-angular/src/app/shared/global-search/global-search.component.ts
// Dynamic placeholder
[placeholder]="searchService.searchMode() === 'project-filter' ? 'Filter projects...' : 'Search documentation...'"
// Different search tips based on mode
@if (searchService.searchMode() === 'project-filter') {
<div class="search-tips">
<h3>Project Filter</h3>
<p class="search-mode-description">Search to filter available projects by name, description, or tags.</p>
<ul>
<li>Try: "GoGrab Space", "Backend", "Architecture"</li>
<li>Filter by type: "public", "private", "documentation"</li>
<li>Use keyboard shortcuts: <kbd>โK</kbd> or <kbd>Ctrl+K</kbd></li>
</ul>
</div>
} @else {
<div class="search-tips">
<h3>Search Tips</h3>
<ul>
<li>Search for specific features: "components", "services", "widgets"</li>
<li>Find guides: "getting started", "build guide", "architecture"</li>
<li>Look for libraries: "core-models", "theme", "ux-menu"</li>
<li>Use keyboard shortcuts: <kbd>โK</kbd> or <kbd>Ctrl+K</kbd> to search</li>
</ul>
</div>
}
// Hide project selector in project-filter mode
@if (searchService.allAvailableProjects().length > 1 && searchService.searchMode() === 'content-search') {
<div class="project-selector">
<!-- Project selection chips -->
</div>
}
Benefits
- โ Intuitive behavior: filter projects on selection page, search content elsewhere
- โ Clear visual feedback with different placeholders and tips
- โ Proper project isolation and privacy
- โ Respects all configuration settings
โ Feature 3: Search Scoping Rules
Implementation
The search now properly respects these rules:
Rule 1: Current Project Scope
if (effectiveConfig.scopeToCurrentProject && !effectiveConfig.crossProjectSearch) {
if (resultProjectName !== currentProject) return false;
}
When scopeToCurrentProject: true, only show content from the current project.
Rule 2: Access Control
const canAccess = this.accessService.canAccessProject(resultProjectId);
if (!canAccess) return false;
Use the existing AccessService to check permissions before showing results.
Rule 3: Private Project Isolation
if (i.visibility === 'private' && resultProjectName !== currentProject) {
return false;
}
Critical: Private projects can ONLY be searched from within themselves. This protects sensitive documentation.
Rule 4: Cross-Project Search Setting
if (!effectiveConfig.crossProjectSearch && resultProjectName !== currentProject) {
return false;
}
Respect the crossProjectSearch configuration flag per project.
Configuration Example
File: apps/docs-angular/src/assets/docs-folders-config.json
{
"globalConfig": {
"search": {
"scopeToCurrentProject": true,
"crossProjectSearch": false,
"includePrivateInGlobalSearch": false,
"maxResultsPerProject": 10,
"hasAccessToPublic": true
}
},
"projects": {
"private": {
"visibility": "private",
"password": "test123"
}
}
}
Per-Project Config (if needed):
{
"name": "some-project",
"config": {
"search": {
"crossProjectSearch": true,
"hasAccessToPublic": true
}
}
}
๐ User Flows
Flow 1: Project Selection with Search
1. User navigates to /select-project
โ
2. Side navigation shows: "Please select a project to view the navigation."
โ
3. User presses โK to search
โ
4. Search modal opens with placeholder: "Filter projects..."
โ
5. User types: "gograb"
โ
6. Search filters projects:
- "GoGrab Space" (score: 40)
- "GoGrab Pulse" (score: 38)
- "GoGrab Space Deck" (score: 35)
โ
7. User clicks on "GoGrab Space"
โ
8. Redirects back to /select-project with that project highlighted
Flow 2: Content Search Within Project
1. User is in "GoGrab Space" project at /
โ
2. User presses โK to search
โ
3. Search modal opens with placeholder: "Search documentation..."
โ
4. User types: "widgets"
โ
5. Search returns results ONLY from GoGrab Space:
- "Widgets Library" (page)
- "Widget Components" (heading)
- "Widget System Architecture" (page)
โ
6. NO results from other projects shown (scopeToCurrentProject: true)
Flow 3: Global Search (if enabled)
1. User in "Public Docs" project
โ
2. Public Docs has: crossProjectSearch: true, hasAccessToPublic: true
โ
3. User searches: "authentication"
โ
4. Results shown from:
โ
Public Docs (own project)
โ
Backend API (public project)
โ
Architecture (public project)
โ Private Docs (blocked - private)
โ
5. User clicks result from Backend API
โ
6. Navigates to Backend API project at /authentication
Flow 4: Private Project Search
1. User in "Private" project (private documentation)
โ
2. User searches: "sensitive data"
โ
3. Results shown ONLY from:
โ
Private project (own project)
โ All other projects (blocked - can't search private from outside)
โ
4. Privacy protected!
๐ Benefits Summary
User Experience
- โ Clear guidance on project selection page
- โ Intuitive search behavior based on context
- โ No confusion about what's being searched
- โ Beautiful, animated UI feedback
Security
- โ Private projects isolated from global search
- โ Access control respected at all times
- โ Per-project configuration support
- โ No data leakage between projects
Performance
- โ Project filtering is instant (no file loading)
- โ Content search properly scoped (fewer results)
- โ Efficient filtering before scoring
- โ Respects user's project configuration
๐งช Testing Checklist
Test 1: Side Navigation Message
- Navigate to
/select-project - Verify side navigation shows the message
- Verify message is centered and animated
- Verify icon floats smoothly
Test 2: Project Filter Mode
- On
/select-project, press โK - Verify placeholder says "Filter projects..."
- Type "gograb" and verify projects are filtered
- Verify search tips mention "Project Filter"
Test 3: Content Search Mode
- Select a project (e.g., "GoGrab Space")
- Navigate to root
/ - Press โK
- Verify placeholder says "Search documentation..."
- Type "widgets" and verify only GoGrab Space results shown
Test 4: Private Project Isolation
- From "Public Docs", search for "private"
- Verify NO results from "Private" project
- Switch to "Private" project
- Search for content within Private
- Verify results are shown
Test 5: Cross-Project Search
- Edit config to enable
crossProjectSearch: true - Search from a project
- Verify results from multiple projects shown
- Disable
crossProjectSearch: false - Search again and verify only current project shown
๐ Configuration Reference
Global Search Config
interface GlobalSearchConfig {
scopeToCurrentProject: boolean; // Default: true - Search only current project
crossProjectSearch: boolean; // Default: false - Allow cross-project results
includePrivateInGlobalSearch: boolean; // Default: false - Include private in global search
maxResultsPerProject: number; // Default: 10 - Max results per project
enableProjectSwitchingFromSearch: boolean; // Default: true - Allow project switch from search
}
Per-Project Config
{
"name": "project-name",
"config": {
"search": {
"scopeToCurrentProject": false, // Override global
"crossProjectSearch": true, // Allow searching other projects
"hasAccessToPublic": true // Can access public projects
}
}
}
๐ฏ Summary
What Was Implemented:
- โ
Side navigation message on
/select-projectroute - โ Dual-mode search (project filter vs content search)
- โ Proper search scoping with 5 rules
- โ Private project isolation
- โ Configuration-driven behavior
- โ Beautiful UI with contextual feedback
Files Changed:
docs-side-nav.component.ts- Added route detectiondocs-side-nav.component.html- Added message templatedocs-side-nav.component.scss- Added message stylingsearch.service.ts- Added dual-mode search logicglobal-search.component.ts- Updated UI for modesglobal-search.component.scss- Added mode description styling
Status: โ COMPLETE - READY FOR TESTING
Last Updated: October 29, 2025
Impact: High - Significantly improves UX on project selection and search behavior