import { useEffect } from 'react'; import { ALLOWED_MODES, defaultStackConfig } from '../../types/provisioning'; import type { ComponentMode, ProvisioningRequest, StackConfig, TenantEnvironment, TenantTier } from '../../types/provisioning'; interface Props { signalParent: (state: { isValid: boolean }) => void; data: ProvisioningRequest; onChange: (updated: Partial) => void; } const ENVIRONMENTS: { value: TenantEnvironment; label: string; description: string }[] = [ { value: 'fdev', label: 'Dev (fdev)', description: 'Feature dev — fast provisioning, no production data.' }, { value: 'uat', label: 'UAT', description: 'User acceptance testing — mirrors production config.' }, { value: 'prod', label: 'Production', description: 'Live production. Full isolation enforced.' }, ]; const TIERS: { value: TenantTier; label: string; badge: string; description: string }[] = [ { value: 'Trial', label: 'Trial', badge: 'Sandbox', description: 'Ephemeral all-in-one sandbox. No persistent data guarantee.' }, { value: 'Shared', label: 'Shared', badge: 'Standard', description: 'Shared platform services, isolated by realm/schema/bucket.' }, { value: 'Dedicated', label: 'Dedicated', badge: 'Professional', description: 'Own sidecar containers per component on the shared host.' }, { value: 'Enterprise', label: 'Enterprise', badge: 'Enterprise', description: 'Full VM isolation per component, provisioned via Pulumi.' }, ]; const MODE_LABELS: Record = { SharedPlatform: 'Shared Platform', Bundled: 'Bundled (in image)', OwnContainer: 'Own Container', VpsDocker: 'VPS — Docker', VpsBareMetal: 'VPS — Bare Metal', }; const COMPONENTS: { key: keyof StackConfig; label: string; description: string }[] = [ { key: 'postgres', label: 'PostgreSQL', description: 'Relational database for tenant data.' }, { key: 'keycloak', label: 'Keycloak', description: 'Identity & access management (realms, OIDC clients).' }, { key: 'vault', label: 'Vault', description: 'Secrets management and dynamic credentials.' }, { key: 'minio', label: 'MinIO', description: 'Object storage (S3-compatible).' }, ]; export default function DeploymentConfigStep({ signalParent, data, onChange }: Props) { useEffect(() => { signalParent({ isValid: !!data.tier && !!data.environment }); }, [data.tier, data.environment, signalParent]); function handleTierChange(tier: TenantTier) { // Reset stackConfig to the default for the new tier so nothing is invalid onChange({ tier, stackConfig: defaultStackConfig(tier) }); } function handleComponentChange(key: keyof StackConfig, mode: ComponentMode) { onChange({ stackConfig: { ...data.stackConfig, [key]: mode } }); } const allowed = ALLOWED_MODES[data.tier]; return (

Choose the deployment environment, isolation tier, and per-component infrastructure mode.

{/* ── Environment ───────────────────────────────────────── */}

Environment

{ENVIRONMENTS.map((env) => ( ))}
{/* ── Isolation Tier ────────────────────────────────────── */}

Isolation Tier

{TIERS.map((tier) => ( ))}
{/* ── Per-Component Stack Config ────────────────────────── */}

Stack Configuration

Defaults are set by the tier. Override individual components as needed.

{COMPONENTS.map(({ key, label, description }) => ( ))}
Component Description Mode
{label} {description}
); }