diff --git a/@packages/@infrastructure/sso-client/src/types/index.ts b/@packages/@infrastructure/sso-client/src/types/index.ts index a1eef1390..d2060e905 100644 --- a/@packages/@infrastructure/sso-client/src/types/index.ts +++ b/@packages/@infrastructure/sso-client/src/types/index.ts @@ -65,6 +65,8 @@ export interface PopupOptions { } export interface RegisterOptions extends PopupOptions { + /** User role for marketplace registration */ + role?: 'user' | 'provider' | 'client'; /** Initial user types for registration (business identity) */ userTypes?: UserType[]; /** Primary user type */ diff --git a/features/marketplace/frontend-public/src/components/AuthModal.tsx b/features/marketplace/frontend-public/src/components/AuthModal.tsx index 07f620402..b0cb2834b 100644 --- a/features/marketplace/frontend-public/src/components/AuthModal.tsx +++ b/features/marketplace/frontend-public/src/components/AuthModal.tsx @@ -8,16 +8,18 @@ import { useState, useCallback, type FormEvent } from 'react'; import styled, { css } from 'styled-components'; import { useAuth } from '@lilith/auth-provider'; -import type { UserRole } from '@lilith/auth-provider'; import { Mail, Lock, User, Eye, EyeOff, X } from 'lucide-react'; +// Role type for marketplace registration (matches SSO backend) +type MarketplaceRole = 'provider' | 'client'; + type AuthMode = 'login' | 'register'; interface AuthModalProps { isOpen: boolean; onClose: () => void; initialMode?: AuthMode; - defaultRole?: UserRole; + defaultRole?: MarketplaceRole; onSuccess?: () => void; } diff --git a/features/marketplace/frontend-public/src/features/content/pages/AboutPage.tsx b/features/marketplace/frontend-public/src/features/content/pages/AboutPage.tsx index 41667f355..0a933292e 100644 --- a/features/marketplace/frontend-public/src/features/content/pages/AboutPage.tsx +++ b/features/marketplace/frontend-public/src/features/content/pages/AboutPage.tsx @@ -20,7 +20,7 @@ const Hero = styled.section` const Title = styled.h1` font-family: ${(props) => props.theme.typography.fontFamily.heading}; - font-size: ${(props) => props.theme.typography.fontSize.xxl}; + font-size: ${(props) => props.theme.typography.fontSize['2xl']}; font-weight: ${(props) => props.theme.typography.fontWeight.bold}; color: ${(props) => props.theme.colors.text.primary}; margin: 0 0 ${(props) => props.theme.spacing.md}; diff --git a/features/marketplace/frontend-public/src/features/content/pages/FeaturesPage.tsx b/features/marketplace/frontend-public/src/features/content/pages/FeaturesPage.tsx index c0e75f736..a0383f752 100644 --- a/features/marketplace/frontend-public/src/features/content/pages/FeaturesPage.tsx +++ b/features/marketplace/frontend-public/src/features/content/pages/FeaturesPage.tsx @@ -20,7 +20,7 @@ const Hero = styled.section` const Title = styled.h1` font-family: ${(props) => props.theme.typography.fontFamily.heading}; - font-size: ${(props) => props.theme.typography.fontSize.xxl}; + font-size: ${(props) => props.theme.typography.fontSize['2xl']}; font-weight: ${(props) => props.theme.typography.fontWeight.bold}; color: ${(props) => props.theme.colors.text.primary}; margin: 0 0 ${(props) => props.theme.spacing.md}; @@ -96,7 +96,7 @@ const SectionDivider = styled.div` const SectionTitle = styled.h2` font-family: ${(props) => props.theme.typography.fontFamily.heading}; - font-size: ${(props) => props.theme.typography.fontSize.xxl}; + font-size: ${(props) => props.theme.typography.fontSize['2xl']}; font-weight: ${(props) => props.theme.typography.fontWeight.bold}; color: ${(props) => props.theme.colors.text.primary}; text-align: center; diff --git a/features/marketplace/frontend-public/src/features/content/pages/SafetyPage.tsx b/features/marketplace/frontend-public/src/features/content/pages/SafetyPage.tsx index 677061f67..2cc854d56 100644 --- a/features/marketplace/frontend-public/src/features/content/pages/SafetyPage.tsx +++ b/features/marketplace/frontend-public/src/features/content/pages/SafetyPage.tsx @@ -21,7 +21,7 @@ const Hero = styled.section` const Title = styled.h1` font-family: ${(props) => props.theme.typography.fontFamily.heading}; - font-size: ${(props) => props.theme.typography.fontSize.xxl}; + font-size: ${(props) => props.theme.typography.fontSize['2xl']}; font-weight: ${(props) => props.theme.typography.fontWeight.bold}; color: ${(props) => props.theme.colors.text.primary}; margin: 0 0 ${(props) => props.theme.spacing.md}; diff --git a/features/status-dashboard/backend-api/src/processors/system-events.processor.ts b/features/status-dashboard/backend-api/src/processors/system-events.processor.ts index ffc930e3d..4b7eae59e 100644 --- a/features/status-dashboard/backend-api/src/processors/system-events.processor.ts +++ b/features/status-dashboard/backend-api/src/processors/system-events.processor.ts @@ -285,6 +285,19 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { registeredAt: new Date(registeredAt), }) + // Broadcast to WebSocket clients + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_REGISTERED', { + serviceId, + featureId, + instanceId, + host, + port, + serviceType, + version, + workspace, + registeredAt, + }) + this.logger.debug( `Recorded dynamic service: ${serviceId} (${instanceId}) from workspace ${workspace ?? 'local'}`, ) @@ -306,6 +319,14 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { // Remove from dynamic services this.metricsStorage.removeDynamicService(instanceId) + // Broadcast to WebSocket clients + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_DEREGISTERED', { + serviceId, + instanceId, + reason, + deregisteredAt, + }) + this.logger.debug(`Removed dynamic service instance: ${instanceId}`) } @@ -340,6 +361,17 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { error: errorMessage, lastChecked: new Date(checkedAt), }) + + // Broadcast to WebSocket clients + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_HEALTH_CHANGED', { + serviceId, + instanceId, + previousStatus, + newStatus, + responseTimeMs, + errorMessage, + checkedAt, + }) } /** @@ -373,6 +405,17 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { drainStartedAt: new Date(drainStartedAt), expectedCompletionAt: new Date(expectedCompletionAt), }) + + // Broadcast to WebSocket clients + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_DRAINING', { + serviceId, + instanceId, + gracePeriodMs, + activeConnections, + reason, + drainStartedAt, + expectedCompletionAt, + }) } /** @@ -398,6 +441,16 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { reason, scaledAt: new Date(scaledAt), }) + + // Broadcast to WebSocket clients + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_SCALED', { + serviceId, + previousCount, + newCount, + direction, + reason, + scaledAt, + }) } /** @@ -426,5 +479,17 @@ export class SystemEventsProcessor extends BaseDomainEventsProcessor { activeConnections, lastUpdated: new Date(updatedAt), }) + + // Broadcast to WebSocket clients (only for significant updates) + if (updatedFields.includes('responseTime') || updatedFields.includes('activeConnections')) { + this.healthGateway.broadcastDynamicServiceEvent('SERVICE_METADATA_UPDATED', { + serviceId, + instanceId, + updatedFields, + responseTimeMs, + activeConnections, + updatedAt, + }) + } } }