Launch Station & Space Management Alignment

Integrate launch-station preview/deployment with space-management analytics

🚀 Launch Station & Space Management Alignment

Last Updated: November 14, 2025
Status: 🎯 INTEGRATION PLAN
Purpose: Complete the build → preview → deploy → manage workflow


🎯 WORKFLOW VISION

USER JOURNEY (Complete):

1. CREATE Universe
   → Dashboard

2. BUILD Structure
   → modular-universe (grid canvas, sections)

3. ADD Content
   → editor-web (pages, widgets)

4. ADD Logic
   → singularity (flows, blocks)

5. PREVIEW Universe ✨
   → launch-station/preview (test before deploy)

6. DEPLOY Universe ✨
   → launch-station/deploy (go live)

7. MANAGE Universe ✨
   → space-management (analytics, data, users)

📊 CURRENT STATE

Launch Station

Location: libs/libs-structural/launch-station/

Current Routes:

/space/:spaceSlug/launch-station
  /dashboard      → Universe dashboard (potentially mock data?)
  /preview/:universeId → Preview page (needs work)
  /deploy/:universeId  → Deployment wizard (exists)

Issues:

Space Management

Location: modules/space-management/

Current Routes:

/space/:spaceSlug/space-management
  (Need to verify what exists)

Issues:


🎯 LAUNCH STATION ENHANCEMENT

1. Universe Dashboard (Real Data)

// components/launch-station-dashboard.component.ts
@Component({
  selector: 'lib-launch-station-dashboard',
  standalone: true
})
export class LaunchStationDashboardComponent implements OnInit {
  
  private moduleService = inject(UniversalModuleService);
  private storage = inject(GgStorage);
  
  // State
  universes = signal<UniverseListItem[]>([]);
  isLoading = signal(true);
  
  async ngOnInit() {
    await this.loadUniverses();
  }
  
  async loadUniverses() {
    this.isLoading.set(true);
    
    // Load real universes from storage
    const spaceId = this.getSpaceId();
    const universeModules = await this.moduleService.query({ 
      type: 'universe' 
    });
    
    this.universes.set(universeModules.map(u => ({
      id: u.id,
      name: u.name,
      status: u.deploymentStatus || 'draft',
      lastModified: u.updatedAt,
      url: u.deploymentUrl,
      thumbnail: u.thumbnail,
      moduleCount: u.children?.length || 0
    })));
    
    this.isLoading.set(false);
  }
  
  previewUniverse(universeId: string) {
    this.router.navigate(['/launch-station/preview', universeId]);
  }
  
  deployUniverse(universeId: string) {
    this.router.navigate(['/launch-station/deploy', universeId]);
  }
  
  editUniverse(universeId: string) {
    this.router.navigate(['/modular-universe'], {
      queryParams: { universe: universeId }
    });
  }
}

2. Universe Preview (Real Rendering)

// components/universe-preview.component.ts
@Component({
  selector: 'lib-universe-preview',
  standalone: true
})
export class UniversePreviewComponent implements OnInit {
  
  private moduleService = inject(UniversalModuleService);
  private storage = inject(GgStorage);
  
  universeId = input.required<string>();
  
  // State
  universe = signal<UniversalModule | null>(null);
  layout = signal<LayoutConfiguration | null>(null);
  sections = computed(() => this.layout()?.sections || []);
  deviceMode = signal<'mobile' | 'tablet' | 'desktop'>('desktop');
  
  async ngOnInit() {
    await this.loadUniverseForPreview();
  }
  
  async loadUniverseForPreview() {
    const universeId = this.universeId();
    
    // Load universe module
    const universe = await this.moduleService.get(universeId);
    this.universe.set(universe);
    
    // Load layout configuration
    const spaceId = this.getSpaceId();
    const layoutKey = `modular-universe/layout/${spaceId}/${universeId}/universe`;
    const layout = await this.storage.get<LayoutConfiguration>(layoutKey);
    this.layout.set(layout);
    
    // Render universe structure
    this.renderPreview();
  }
  
  renderPreview() {
    const layout = this.layout();
    if (!layout) return;
    
    // For each section, render assigned module/page
    layout.sections.forEach(async section => {
      if (section.assignedModuleId) {
        const module = await this.moduleService.get(section.assignedModuleId);
        
        if (module.type === 'page') {
          // Render page content from editor-web
          this.renderPage(section, module);
        } else if (module.type === 'navigation') {
          // Render navigation module
          this.renderNavigation(section, module);
        }
        // ... handle other module types
      }
    });
  }
  
  async renderPage(section: SectionConfig, pageModule: UniversalModule) {
    // Get page content from editor-web storage
    const pageKey = `editor-web/page/${pageModule.id}`;
    const pageData = await this.storage.get(pageKey);
    
    // Render page widgets in section
    // ...
  }
  
  changeDeviceMode(mode: 'mobile' | 'tablet' | 'desktop') {
    this.deviceMode.set(mode);
  }
}

Template:

<!-- universe-preview.component.html -->
<div class="preview-container">
  <!-- Device Mode Selector -->
  <div class="device-toolbar">
    <button 
      *ngFor="let mode of ['mobile', 'tablet', 'desktop']"
      [class.active]="deviceMode() === mode"
      (click)="changeDeviceMode(mode)"
    >
      {{ mode }}
    </button>
  </div>
  
  <!-- Preview Frame -->
  <div 
    class="preview-frame"
    [class.mobile]="deviceMode() === 'mobile'"
    [class.tablet]="deviceMode() === 'tablet'"
    [class.desktop]="deviceMode() === 'desktop'"
  >
    <!-- Render universe layout -->
    <div 
      class="universe-container"
      [style]="getContainerStyles()"
    >
      <div 
        *ngFor="let section of sections()"
        class="preview-section"
        [style]="getSectionStyles(section)"
      >
        <!-- Render section content -->
        <ng-container *ngIf="section.assignedModuleId">
          <lib-module-renderer 
            [moduleId]="section.assignedModuleId"
          />
        </ng-container>
        
        <div *ngIf="!section.assignedModuleId" class="empty-section">
          <span>Empty Section: {{ section.name }}</span>
        </div>
      </div>
    </div>
  </div>
  
  <!-- Preview Actions -->
  <div class="preview-actions">
    <button (click)="refreshPreview()" class="btn-secondary">
      Refresh
    </button>
    <button (click)="openFullscreen()" class="btn-secondary">
      Fullscreen
    </button>
    <button (click)="proceedToDeploy()" class="btn-primary">
      Deploy This Universe
    </button>
  </div>
</div>

3. Deployment Wizard (Backend Integration)

// components/deployment-wizard.component.ts
@Component({
  selector: 'lib-deployment-wizard',
  standalone: true
})
export class DeploymentWizardComponent {
  
  universeId = input.required<string>();
  
  // Wizard steps
  currentStep = signal(1);
  totalSteps = 5;
  
  // Deployment config
  deploymentConfig = signal<DeploymentConfig>({
    domain: '',
    subdomain: '',
    environment: 'production',
    buildConfig: {},
    envVars: []
  });
  
  // Steps
  steps = [
    { id: 1, name: 'Verify Build', component: VerifyBuildStepComponent },
    { id: 2, name: 'Domain Configuration', component: DomainConfigStepComponent },
    { id: 3, name: 'Environment Variables', component: EnvVarsStepComponent },
    { id: 4, name: 'Deploy', component: DeployStepComponent },
    { id: 5, name: 'Success', component: SuccessStepComponent }
  ];
  
  async deployToBackend() {
    const config = this.deploymentConfig();
    const universeId = this.universeId();
    
    // Call backend deployment API
    const result = await this.deploymentService.deploy(universeId, config);
    
    if (result.success) {
      // Update universe with deployment info
      await this.moduleService.update(universeId, {
        deploymentStatus: 'deployed',
        deploymentUrl: result.url,
        deploymentDate: new Date()
      });
      
      // Navigate to success step
      this.currentStep.set(5);
    }
  }
}

🎯 SPACE MANAGEMENT ENHANCEMENT

1. Deployed Universe Dashboard

// components/space-management-dashboard.component.ts
@Component({
  selector: 'lib-space-management-dashboard',
  standalone: true
})
export class SpaceManagementDashboardComponent {
  
  deployedUniverses = signal<DeployedUniverse[]>([]);
  
  async loadDeployedUniverses() {
    // Get universes with deployment status = 'deployed'
    const universes = await this.moduleService.query({
      type: 'universe',
      deploymentStatus: 'deployed'
    });
    
    this.deployedUniverses.set(universes.map(u => ({
      id: u.id,
      name: u.name,
      url: u.deploymentUrl,
      status: u.deploymentStatus,
      deployedAt: u.deploymentDate,
      analytics: {
        users: 0,     // From backend
        pageViews: 0, // From backend
        storage: 0    // From backend
      }
    })));
  }
  
  manageUniverse(universeId: string) {
    this.router.navigate(['/space-management', universeId]);
  }
}

2. Universe Data Manager

// components/universe-data-manager.component.ts
@Component({
  selector: 'lib-universe-data-manager',
  standalone: true
})
export class UniverseDataManagerComponent {
  
  universeId = input.required<string>();
  
  // Data collections for this universe
  collections = signal<DataCollection[]>([]);
  selectedCollection = signal<DataCollection | null>(null);
  collectionData = signal<any[]>([]);
  
  async loadCollections() {
    const universeId = this.universeId();
    const spaceId = this.getSpaceId();
    
    // Get all collections for this universe
    // From universe-database-arquitecture.md:
    // universe_{universeId}_users
    // universe_{universeId}_products
    // universe_{universeId}_orders
    // etc.
    
    const collectionNames = await this.storage.listInUniverse(
      spaceId, 
      universeId, 
      ''
    );
    
    this.collections.set(collectionNames.map(name => ({
      name: name,
      count: 0, // Get from backend
      lastModified: new Date()
    })));
  }
  
  async loadCollectionData(collectionName: string) {
    const universeId = this.universeId();
    const spaceId = this.getSpaceId();
    
    // Query collection data
    const data = await this.storage.queryUniverseData(
      spaceId,
      universeId,
      collectionName,
      {} // filters
    );
    
    this.collectionData.set(data);
  }
  
  exportData(format: 'csv' | 'json' | 'excel') {
    // Export collection data
  }
}

Template:

<!-- universe-data-manager.component.html -->
<div class="data-manager">
  <div class="collections-sidebar">
    <h3>Collections</h3>
    <div 
      *ngFor="let collection of collections()"
      class="collection-item"
      [class.active]="selectedCollection()?.name === collection.name"
      (click)="loadCollectionData(collection.name)"
    >
      <span class="collection-name">{{ collection.name }}</span>
      <span class="collection-count">{{ collection.count }} records</span>
    </div>
  </div>
  
  <div class="data-view">
    <div *ngIf="selectedCollection()" class="collection-header">
      <h2>{{ selectedCollection()?.name }}</h2>
      <button (click)="exportData('csv')">Export CSV</button>
      <button (click)="exportData('json')">Export JSON</button>
    </div>
    
    <div class="data-table">
      <table>
        <thead>
          <tr>
            <th *ngFor="let column of getColumns()">{{ column }}</th>
          </tr>
        </thead>
        <tbody>
          <tr *ngFor="let row of collectionData()">
            <td *ngFor="let column of getColumns()">{{ row[column] }}</td>
          </tr>
        </tbody>
      </table>
    </div>
  </div>
</div>

3. Universe Analytics

// components/universe-analytics-dashboard.component.ts
@Component({
  selector: 'lib-universe-analytics-dashboard',
  standalone: true
})
export class UniverseAnalyticsDashboardComponent {
  
  universeId = input.required<string>();
  
  metrics = signal<UniverseAnalytics>({
    users: {
      total: 0,
      active: 0,
      new: 0
    },
    pageViews: {
      total: 0,
      unique: 0,
      avgDuration: 0
    },
    performance: {
      avgLoadTime: 0,
      errorRate: 0,
      uptime: 100
    },
    storage: {
      used: 0,
      limit: 0,
      collections: 0
    }
  });
  
  async loadAnalytics() {
    const universeId = this.universeId();
    
    // Get analytics from backend
    const analytics = await this.analyticsService.getUniverseAnalytics(universeId);
    this.metrics.set(analytics);
  }
}

🔗 INTEGRATION POINTS

From Modular-Universe to Launch Station

// In modular-universe-builder.component.ts

async previewUniverse() {
  const universeId = this.universeId();
  
  // Save current state
  await this.saveLayout();
  
  // Navigate to preview
  this.router.navigate(['/launch-station/preview', universeId]);
}

async deployUniverse() {
  const universeId = this.universeId();
  
  // Save current state
  await this.saveLayout();
  
  // Navigate to deployment wizard
  this.router.navigate(['/launch-station/deploy', universeId]);
}

From Launch Station to Space Management

// After successful deployment

async onDeploymentSuccess(universeId: string, deploymentUrl: string) {
  // Show success message
  this.showSuccessMessage(`Universe deployed to ${deploymentUrl}`);
  
  // Offer to manage
  const shouldManage = await this.confirmDialog(
    'Would you like to manage your deployed universe now?'
  );
  
  if (shouldManage) {
    this.router.navigate(['/space-management', universeId]);
  }
}

🎯 SUCCESS METRICS

Technical

User Experience


Status: ✅ PLAN COMPLETE
Next Action: Implement launch-station enhancements
Timeline: 2-3 weeks
Last Updated: November 14, 2025