SPACE SLUG ARCHITECTURAL FIX

Documentation for Plans

๐Ÿ—๏ธ Space Slug Architectural Fix - COMPLETE

Date: October 31, 2025
Status: โœ… IMPLEMENTED
Purpose: Fix space slug routing to work correctly in development vs. production


๐ŸŽฏ THE PROBLEM

The space slug was changing based on the URL, creating a terrible user experience:

User navigates to: /space-test-should-not-work/dashboard
Result: Space slug becomes "space-test-should-not-work" โŒ

User navigates to: /anything-here/dashboard  
Result: Space slug becomes "anything-here" โŒ

Root Cause: The space.guard.ts was extracting spaceSlug from the URL and passing it to initializeSpace(), which would then update the space with this slug.


โœ… THE SOLUTION

Implemented deployment mode-aware routing:

Development Mode (deploymentMode: 'development')

Subdomain Mode (deploymentMode: 'subdomain')


๐Ÿ“ FILES CHANGED

1. Environment Configuration

apps/gograb-space/src/environments/environment.shared.ts

export const DEPLOYMENT_MODES = {
  development: 'development',  // Space from user, not URL
  subdomain: 'subdomain',      // Space from URL/subdomain
} as const;

export type DeploymentMode = keyof typeof DEPLOYMENT_MODES;

apps/gograb-space/src/environments/environment.ts

import { DEPLOYMENT_MODES } from './environment.shared';

export const environment = {
  // ...
  deploymentMode: DEPLOYMENT_MODES.development,  // โ† Development mode
};

apps/gograb-space/src/environments/environment.prod.ts

import { DEPLOYMENT_MODES } from './environment.shared';

export const environment = {
  // ...
  deploymentMode: DEPLOYMENT_MODES.subdomain,  // โ† Subdomain mode
};

2. Core Angular Library

libs/core-angular/src/lib/tokens/deployment-mode.token.ts (NEW FILE)

import { InjectionToken } from '@angular/core';

export type DeploymentMode = 'development' | 'subdomain';

export const DEPLOYMENT_MODE = new InjectionToken<DeploymentMode>('DEPLOYMENT_MODE', {
  providedIn: 'root',
  factory: () => 'development'
});

libs/core-angular/src/index.ts

// Export tokens
export { DEPLOYMENT_MODE, type DeploymentMode } from './lib/tokens/deployment-mode.token';

3. Space Guard

libs/core-angular/src/lib/guards/space.guard.ts

import { DEPLOYMENT_MODE } from '../tokens/deployment-mode.token';

export const spaceGuard: CanActivateFn = async (route, state) => {
  const deploymentMode = inject(DEPLOYMENT_MODE);
  const spaceSlug = route.paramMap.get('spaceSlug');
  
  // ...
  
  // ๐ŸŽฏ CRITICAL: Deployment mode logic
  let spaceSlugToUse: string | undefined;
  if (deploymentMode === 'development') {
    // Development: ALWAYS load user's space, ignore URL
    spaceSlugToUse = undefined;
  } else {
    // Subdomain: Use URL slug
    spaceSlugToUse = spaceSlug || undefined;
  }
  
  await spaceService.initializeSpace(userId, spaceSlugToUse);
  
  // After initialization, redirect to correct slug URL
  if (currentSpace.spaceSlug !== spaceSlug) {
    const newUrl = state.url.replace(`/${spaceSlug}/`, `/${currentSpace.spaceSlug}/`);
    return router.parseUrl(newUrl);
  }
};

4. Main App Providers

apps/gograb-space/src/main.ts

import { DEPLOYMENT_MODE } from '@libs-foundational/core-angular';

bootstrapApplication(AppComponent, {
  providers: [
    // ...
    {
      provide: DEPLOYMENT_MODE,
      useValue: environment.deploymentMode  // โ† Inject from environment
    },
  ]
});

๐Ÿงช HOW TO TEST

1. Clear Old Data

// In browser console:
await clearCachedSpaceData();

2. Reload & Verify

1. Reload page: http://localhost:5200/
2. Login as: [email protected]
3. Navigate to ANY URL with ANY slug:
   - http://localhost:5200/test-123/dashboard
   - http://localhost:5200/whatever/editor
   - http://localhost:5200/random-slug/space-map

4. Check "Current Space" section:
   - NAME: "My space"
   - SLUG: "my-space" โœ… (ALWAYS stable!)

5. URL should auto-redirect to: /my-space/dashboard

โœจ EXPECTED BEHAVIOR

Development Mode (Current)

// User: [email protected]
// Space: "My space" (slug: "my-space")

Navigate to: /anything/dashboard
Auto-redirect: /my-space/dashboard โœ…

Navigate to: /test-123/editor  
Auto-redirect: /my-space/editor โœ…

Navigate to: /random/space-map
Auto-redirect: /my-space/space-map โœ…

Space slug NEVER changes! โœ…

Subdomain Mode (Future Production)

// URL: https://acme.gograb.space/dashboard
// Space determined by subdomain: "acme"

Navigate to: /dashboard
Loads space: "acme" โœ…

Navigate to: /editor
Still in space: "acme" โœ…

๐Ÿ“Š IMPLEMENTATION STATISTICS


๐ŸŽ‰ BENEFITS

โœ… Stable Space Slugs: Never change unexpectedly
โœ… Flexible Architecture: Easy to switch between dev/prod modes
โœ… Future-Proof: Ready for subdomain deployment
โœ… Type-Safe: Full TypeScript support
โœ… Zero Runtime Cost: Uses Angular's DI system
โœ… Clear Separation: Development vs. production behavior


๐Ÿš€ NEXT STEPS

  1. โœ… Clear cached data
  2. โœ… Test in development mode
  3. โณ Deploy to staging with subdomain mode
  4. โณ Test multi-space architecture
  5. โณ Add space selector UI for users with multiple spaces

End of Document | Last Updated: October 31, 2025