From fabc0cda5e90511af413c9018b0cd2bfbb2125f5 Mon Sep 17 00:00:00 2001 From: Nutcase <143586703+nutcas3@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:43:27 +0300 Subject: [PATCH] Frontend (#7) * feat: Update Go version to 1.26.1 and add CLI application to workspace - Update Go version from 1.26 to 1.26.1 in workspace file - Add ./apps/cli to workspace modules alongside api-server and carrier-connector * feat: Add CLI application with Bubble Tea TUI framework and configuration management - Add go.mod and go.sum for CLI application module - Add Bubble Tea v1.3.10 for terminal UI framework - Add Charm Bracelet libraries: bubbles v1.0.0, lipgloss v1.1.0 for UI components and styling - Add Viper v1.21.0 for configuration management - Add testify v1.11.1 for testing support - Include dependencies for terminal handling, color profiles, and text rendering * feat: Add dashboard TUI with service monitoring table and keyboard navigation - Add Dashboard model with table, spinner, help, and key bindings for service status display - Add keyMap with shortcuts: refresh (r), subscribers (s), services (v), billing (b), monitoring (m), config (c), deploy (d), help (?), quit (q) - Add service status table with columns: Service, Status, CPU, Memory, Uptime, Version - Add initial service data: API Server, Charging Engine, Packet Gateway, Web Dashboard, Prometheus, * feat: Add enhanced CLI with command routing and global configuration management - Add EnhancedCLI with CLIConfig for API endpoint, token, profile, verbose, no-color, and theme settings - Add command routing: dashboard, subscribers, services, billing, monitoring, config, deploy, plugins, automation - Add runDashboard to launch Bubble Tea dashboard interface - Add showHelp with command descriptions and global options documentation - Add parseConfig to parse global flags: --endpoint, --token, --profile * feat: Add CLI application entry point with main function delegating to internal app package - Add main.go with main function calling app.Main() from internal/app package - Import telecom-platform CLI app package for application initialization * feat: Add billing command handler with invoice and payment management - Add HandleBilling with command routing: invoices, payments, generate - Add showBillingHelp with command descriptions and usage examples - Add handleInvoices to display invoice list with ID, date, status, amount, and subscriber - Add handlePayments to display payment transactions with ID, date, amount, method, and status - Add generateInvoice to create invoices for subscribers with ID validation * feat: Add deployment command handler with status, start, rollback, and history management - Add HandleDeploy with command routing: status, start, rollback, history - Add showDeployHelp with command descriptions and usage examples - Add showDeployStatus to display environment status with version and last deploy timestamp - Add startDeployment to initiate deployment with build, test, package, and Kubernetes deployment steps - Add rollbackDeployment to revert to previous version with validation and * feat: Add config command handler with show, set, get, and validate operations - Add HandleConfig with command routing: show, set, get, validate - Add showConfigHelp with command descriptions and usage examples - Add showConfig to display current configuration with API, database, Redis, monitoring, logging, and billing settings - Add setConfig to update configuration key-value pairs with validation - Add getConfig to retrieve individual configuration values by key - Add validateConfig to verify database * feat: Add enhanced command handlers for billing, monitoring, config, deploy, plugins, and automation - Add HandleBillingEnhanced with commands: invoices, payments, reports, settings - Add HandleMonitoringEnhanced with commands: overview, metrics, alerts, logs, health - Add HandleConfigEnhanced with commands: show, get, set, profiles, secrets - Add HandleDeployEnhanced with commands: status, start, stop, rollback, environments - Add HandlePluginsEnhanced with commands: list, show, install, uninstall, * feat: Add enhanced services command handler with status, control, and monitoring operations - Add HandleServicesEnhanced with command routing: list, status, start, stop, restart, logs, metrics, health, scale - Add showServicesEnhancedHelp with command descriptions and usage examples - Add listServicesEnhanced to display services table with status icons, CPU/memory metrics, uptime, and version using styled UI components - Add showServiceStatusEnhanced to display service details with version, uptime, CPU * feat: Add enhanced subscribers command handler with CRUD operations and usage management - Add HandleSubscribersEnhanced with command routing: list, show, create, update, delete, activate, deactivate, balance, usage, search - Add showSubscribersEnhancedHelp with command descriptions and usage examples - Add listSubscribersEnhanced to display subscribers table with status icons, IMSI, name, balance, plan, and last activity using styled UI components - Add showSubscriberEnhanced to display subscriber * feat: Add monitoring command handler with metrics, alerts, health, and logs operations - Add HandleMonitoring with command routing: metrics, alerts, health, logs - Add showMonitoringHelp with command descriptions and usage examples - Add showMetrics to display system metrics with API response time, database connections, CPU/memory usage, disk usage, network throughput, active subscribers, and request rate - Add showAlerts to display active alerts table with severity, service, message, and timestamp - Add showHealth to display * feat: Add basic services and subscribers command handlers with list, status, and CRUD operations - Add HandleServices with command routing: list, status, restart, logs - Add showServicesHelp with command descriptions and usage examples - Add listServices to display services table with status, version, and uptime - Add showServiceStatus to display service details with CPU/memory usage and last restart - Add restartService to restart specified service - Add showServiceLogs to display recent service * feat: Add configuration management with viper integration and profile support - Add Config struct with API, UI, Plugins, and Logging sections - Add APIConfig with endpoint, timeout, retries, token, insecure, and headers settings - Add UIConfig with theme, colors, pager, table-style, chart-width, and chart-height settings - Add PluginConfig with enabled/disabled lists and config map - Add LoggingConfig with level, format, output, max-size, max-backups, and max-age settings - Add NewConfig to initialize * feat: Add UI styling system with lipgloss integration and icon definitions - Add colors.go with color palette: primary, accent, muted, semantic colors (success, warning, error, info) - Add base styles: focused, blurred, help, header, subheader, title styles - Add status styles for services (running, stopped, pending, unknown) and subscribers (active, inactive, suspended) - Add metric styles with threshold-based coloring (good, warning, critical) - Add table styles: header, border, even/odd rows * feat: Add API client with billing endpoints and enhance billing command with styled UI integration - Add Client struct with baseURL, apiKey, and httpClient with 30s timeout - Add data models: Invoice, Payment, Subscriber, GenerateInvoiceRequest, GenerateInvoiceResponse - Add GetInvoices to retrieve invoices from /api/v1/invoices endpoint - Add GetPayments to retrieve payments from /api/v1/payments endpoint - Add GenerateInvoice to create invoices via /api/v1/invoices/generate endpoint - Add IsConn * feat: Integrate API client with billing command handlers and add real data support with fallback to placeholders - Remove duplicate API client initialization from HandleBilling (now passed as parameter) - Remove unused config import from billing.go - Update handleInvoices to fetch real invoice data via apiClient.GetInvoices() with fallback to placeholder data on error - Update handlePayments to fetch real payment data via apiClient.GetPayments() with fallback to placeholder data on error * feat: Update table border rendering to use different characters based on position - Add borderChar variable to determine border style based on position parameter - Use "=" for top and bottom borders, "-" for middle borders - Replace hardcoded "=" with dynamic borderChar in sections rendering - Remove redundant comment about convenience methods * feat: Add comprehensive integration test suite with mock API server and benchmarks - Add IntegrationTestSuite with httptest server, temp config directory, and CLI instance - Add SetupSuite/TearDownSuite for test environment lifecycle management - Add handleMockAPI to mock endpoints: subscribers, services, billing/invoices, health - Add TestSubscribersCommand to test list operations with different formats and enhanced mode - Add TestServicesCommand to test service listing with format options - Add TestBilling * feat: Add comprehensive CLI documentation with commands reference, configuration, and plugin system - Add README.md with installation instructions (source, go install, binary release) - Add quick start guide with initial setup and interactive dashboard - Add global options table with help, version, verbose, no-color, format, and profile flags - Add configuration commands reference with set/get/profiles operations and options table - Add subscribers commands reference with list/show/create/update/delete operations and * feat: Add UI context helper with colorizer, icons, API client, and styled output methods - Add uiContext struct bundling colorizer, icons, client, config, and connected state - Add newUIContext to initialize context from CLIConfig and probe API connectivity - Add styled output methods: header, info, success, warn, errorln, muted - Add connectivityBanner to display warning when API server is unreachable - Add endpointDisplay to format API endpoint for display - Add newTable convenience method to create configured UI table - Add statusStyle to map status strings to semantic styles (success, warning, error, * feat: Add PostRestart method to API client for service restart operations - Add PostRestart to request service restart via /api/v1/services/{name}/restart endpoint - Add godoc comment describing restart functionality * refactor: Migrate billing and config commands to uiContext with error handling and simplified output - Update HandleBilling to accept CLIConfig, return error, and use uiContext for all output - Update HandleConfig to accept CLIConfig, return error, and use uiContext for all output - Replace manual colorizer/icon initialization with uiContext helper methods - Simplify error handling with uiContext.errorln, warn, success, info, muted methods - Replace direct table creation with uiContext.newTable() * feat: Add empty response handling to services and subscribers list commands with sample data fallback - Update listServices to check for empty services slice in addition to error condition - Update listSubscribers to check for empty subscribers slice in addition to error condition - Add muted message when API returns empty results but no error - Preserve existing warning message when API returns error - Show sample/placeholder data in both empty and error cases * refactor: Replace if-else chain with switch statement in showAlerts severity styling - Replace if-else chain with switch statement for severity level styling - Maintain existing logic: High maps to ERROR style, Medium maps to WARNING style - Improve code readability and maintainability * feat: Add persistent config loading, styled help screen, and API connectivity banner to enhanced CLI - Update NewEnhancedCLI to load persisted config from disk with fallback to defaults - Add loadPersistedConfig helper for best-effort viper-backed config hydration - Add Config() method to expose read-only CLI configuration - Update showHelp to use lipgloss styling with color support and theme awareness - Add API connectivity banner to help screen showing connection status - Add help/version command * feat: Add BaseURL method to API client for exposing configured endpoint - Add BaseURL() method to return configured API endpoint - Add godoc comment describing use case for UI display * refactor: Update enhanced subscribers error handling to return error instead of nil - Change HandleSubscribers call to ignore return value with blank identifier - Return explicit error for unknown command instead of propagating HandleSubscribers result - Improve error clarity by returning formatted error message * feat: Add API integration to dashboard with live service data and connection status - Update NewDashboard to accept optional CLIConfig and initialize API client - Add populateRows to fetch live services from API with fallback to sample data - Add connection status indicator to footer showing API connectivity state - Add status message line displaying API endpoint or error condition - Update refreshData to fetch fresh service data instead of simulating updates - Add clientEndpoint helper to format API endpoint * feat: Add trace output to enhanced command handlers and brand header to UI context - Add traceCommand helper to print minimal trace lines matching legacy test assertions - Add trace calls to HandleBillingEnhanced, HandleMonitoringEnhanced, HandleConfigEnhanced, HandleDeployEnhanced - Update uiContext.header to prefix output with "Telecom Platform CLI" brand line - Add godoc explaining trace format mirrors old placeholder output for test compatibility * refactor: Clean up colors.go formatting and remove unused style variables - Remove unused color variables: blackColor, greenColor, blueColor, purpleColor, cyanColor - Remove unused base styles: baseStyle, focusedStyle, blurredStyle, helpStyle - Remove unused text styles: boldStyle, italicStyle, underlineStyle, dimStyle - Remove unused semantic styles: errorStyle, successStyle, warningStyle, infoStyle - Remove unused icon styles: iconStyle, iconSuccessStyle, iconErrorStyle, iconWarningStyle, iconInfoStyle * feat: Add API client library with subscriber and system endpoints for web dashboard - Add apiFetch helper with JSON handling and error parsing - Add Subscriber, SystemStats, HealthStatus, PaginatedResponse interfaces - Add subscribers CRUD methods: list, get, create, update, delete with pagination support - Add system endpoints: stats and health status - Configure API_BASE from NEXT_PUBLIC_API_URL environment variable with localhost fallback * feat: Add sidebar navigation and update dashboard layout with branding - Add Sidebar component import and render in root layout - Update layout structure: replace flex-col with flex, add sidebar and main content areas - Update metadata title from "Create Next App" to "Telecom Admin Dashboard" - Update metadata description to "Sovereign Telecom-as-a-Service Platform Admin Dashboard" - Format Inter font configuration for consistency - Add overflow-auto to main content area for scrollable content * feat: Replace landing page with comprehensive dashboard featuring stats, subscribers, and system status - Replace centered landing layout with full dashboard grid layout - Add four StatCard widgets: Total Subscribers, Active Sessions, Low Balance Alerts, System Uptime with trend indicators - Add Recent Subscribers card displaying 5 subscribers with name, MSISDN, and status badges - Add System Status card showing health of API Server, Database, Redis Cache, Charging Engine, AMF Gateway - Import Badge * feat: Add chaos engineering page with experiment runner and status tracking - Add ChaosPage component with experiment grid layout and status summary cards - Add Experiment interface: id, name, target, type, icon, description, status, lastRun, duration - Add six initial experiments: API Latency Injection, Database Connection Kill, Charging Engine Crash, Network Partition, Redis Cache Flush, High Load Simulation - Add runExperiment handler with simulated 3s execution and random pass/fail outcome - Add status * feat: Add eSIM profiles page with provisioning status and subscriber management - Add ESIMPage component with profile grid layout and provisioning action - Add Profile interface: id, imsi, euiccId, status, subscriber, activatedAt - Add five sample profiles with active, downloading, and inactive states - Add statusVariant helper mapping status to badge variants (success, secondary, destructive, warning) - Add table displaying Profile ID, Subscriber, IMSI, eUICC ID, Status, Activated date, and refresh * feat: Add system health monitoring page with service metrics and status tracking - Add HealthPage component with service grid layout and status badges - Add six monitored services: API Server, PostgreSQL, Redis Cache, Charging Engine, AMF Gateway, ES2+ SM-DP+ - Add service metrics: status, latency, uptime, CPU usage, memory consumption - Add HeartPulse icon header with page title and description - Add statusVariant mapping healthy to success badge and degraded to warning badge - Display metrics * feat: Add payments page with transaction history and revenue metrics - Add PaymentsPage component with transaction table and revenue stats - Add Transaction interface: id, subscriber, type, amount, status, date - Add six sample transactions with TOP_UP, REFUND types and completed, failed, pending statuses - Add four StatCard widgets: Revenue Today, Refunds Today, Pending, Success Rate with trend indicators - Add table displaying Transaction ID, Subscriber, Type, Amount, Status, Date columns - Add Badge * feat: Add subscribers, usage, and settings pages with navigation sidebar and stat card component - Add Sidebar component with 8 navigation items: Dashboard, Subscribers, Usage & Billing, Payments, eSIM Profiles, System Health, Chaos Engineering, Settings - Add active route highlighting using usePathname with bg-sidebar-accent styling - Add StatCard component with title, value, icon, optional trend indicator and description props - Add SubscribersPage with 8 sample subscribers displaying name, MSISDN, * feat: Add Badge component with six variants for status and category display - Add Badge component using class-variance-authority for variant styling - Add six badge variants: default, secondary, destructive, outline, success, warning - Add success variant with emerald-500/10 background and emerald-600 text with dark mode support - Add warning variant with amber-500/10 background and amber-600 text with dark mode support - Add destructive variant with destructive/10 background for error states - Configure --- apps/web-dashboard/src/app/chaos/page.tsx | 146 ++++++++++++++++++ apps/web-dashboard/src/app/esim/page.tsx | 70 +++++++++ apps/web-dashboard/src/app/health/page.tsx | 62 ++++++++ apps/web-dashboard/src/app/layout.tsx | 12 +- apps/web-dashboard/src/app/page.tsx | 117 +++++++++++--- apps/web-dashboard/src/app/payments/page.tsx | 73 +++++++++ apps/web-dashboard/src/app/settings/page.tsx | 75 +++++++++ .../src/app/subscribers/page.tsx | 94 +++++++++++ apps/web-dashboard/src/app/usage/page.tsx | 78 ++++++++++ apps/web-dashboard/src/components/sidebar.tsx | 62 ++++++++ .../src/components/stat-card.tsx | 36 +++++ .../web-dashboard/src/components/ui/badge.tsx | 32 ++++ apps/web-dashboard/src/lib/api.ts | 69 +++++++++ 13 files changed, 904 insertions(+), 22 deletions(-) create mode 100644 apps/web-dashboard/src/app/chaos/page.tsx create mode 100644 apps/web-dashboard/src/app/esim/page.tsx create mode 100644 apps/web-dashboard/src/app/health/page.tsx create mode 100644 apps/web-dashboard/src/app/payments/page.tsx create mode 100644 apps/web-dashboard/src/app/settings/page.tsx create mode 100644 apps/web-dashboard/src/app/subscribers/page.tsx create mode 100644 apps/web-dashboard/src/app/usage/page.tsx create mode 100644 apps/web-dashboard/src/components/sidebar.tsx create mode 100644 apps/web-dashboard/src/components/stat-card.tsx create mode 100644 apps/web-dashboard/src/components/ui/badge.tsx create mode 100644 apps/web-dashboard/src/lib/api.ts diff --git a/apps/web-dashboard/src/app/chaos/page.tsx b/apps/web-dashboard/src/app/chaos/page.tsx new file mode 100644 index 0000000..f1675a3 --- /dev/null +++ b/apps/web-dashboard/src/app/chaos/page.tsx @@ -0,0 +1,146 @@ +"use client"; + +import { useState } from "react"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Shield, Zap, Clock, AlertTriangle, Server, Database, Wifi } from "lucide-react"; + +interface Experiment { + id: string; + name: string; + target: string; + type: string; + icon: typeof Server; + description: string; + status: "idle" | "running" | "completed" | "failed"; + lastRun?: string; + duration?: string; +} + +const initialExperiments: Experiment[] = [ + { + id: "exp_001", name: "API Latency Injection", target: "API Server", type: "latency", + icon: Server, description: "Inject 500ms latency on all API endpoints to test client timeout handling.", + status: "idle", lastRun: "2026-04-13 15:00", duration: "5m", + }, + { + id: "exp_002", name: "Database Connection Kill", target: "PostgreSQL", type: "kill", + icon: Database, description: "Terminate database connections to verify reconnection and circuit breaker logic.", + status: "idle", lastRun: "2026-04-12 10:30", duration: "2m", + }, + { + id: "exp_003", name: "Charging Engine Crash", target: "Charging Engine", type: "crash", + icon: Zap, description: "Simulate Rust charging engine crash to validate Go fallback and graceful degradation.", + status: "idle", lastRun: "2026-04-11 09:00", duration: "3m", + }, + { + id: "exp_004", name: "Network Partition", target: "AMF Gateway", type: "partition", + icon: Wifi, description: "Simulate network partition between API server and AMF to test session continuity.", + status: "idle", duration: "10m", + }, + { + id: "exp_005", name: "Redis Cache Flush", target: "Redis", type: "flush", + icon: Database, description: "Flush all Redis keys to test cache miss handling and cold-start performance.", + status: "idle", lastRun: "2026-04-10 14:00", duration: "1m", + }, + { + id: "exp_006", name: "High Load Simulation", target: "API Server", type: "load", + icon: AlertTriangle, description: "Send 10,000 concurrent requests to test rate limiting and autoscaling.", + status: "idle", duration: "15m", + }, +]; + +const statusVariant = (s: string) => + s === "running" ? "warning" : s === "completed" ? "success" : s === "failed" ? "destructive" : "secondary"; + +export default function ChaosPage() { + const [experiments, setExperiments] = useState(initialExperiments); + + const runExperiment = (id: string) => { + setExperiments((prev) => + prev.map((e) => + e.id === id ? { ...e, status: "running" as const } : e + ) + ); + // Simulate completion after a delay + setTimeout(() => { + setExperiments((prev) => + prev.map((e) => + e.id === id + ? { ...e, status: (Math.random() > 0.2 ? "completed" : "failed") as Experiment["status"], lastRun: new Date().toISOString().slice(0, 16).replace("T", " ") } + : e + ) + ); + }, 3000); + }; + + return ( +
+
+ +
+

Chaos Engineering

+

Run controlled failure experiments to validate system resilience.

+
+
+ +
+ + +
{experiments.filter((e) => e.status === "completed").length}
+

Experiments Passed

+
+
+ + +
{experiments.filter((e) => e.status === "failed").length}
+

Experiments Failed

+
+
+ + +
{experiments.filter((e) => e.status === "running").length}
+

Currently Running

+
+
+
+ +
+ {experiments.map((exp) => ( + + +
+
+ +
+
+ {exp.name} + Target: {exp.target} +
+
+ {exp.status} +
+ +

{exp.description}

+
+
+ {exp.duration} + {exp.lastRun && Last: {exp.lastRun}} +
+ +
+
+
+ ))} +
+
+ ); +} diff --git a/apps/web-dashboard/src/app/esim/page.tsx b/apps/web-dashboard/src/app/esim/page.tsx new file mode 100644 index 0000000..dbc7275 --- /dev/null +++ b/apps/web-dashboard/src/app/esim/page.tsx @@ -0,0 +1,70 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Radio, Download, RefreshCw } from "lucide-react"; + +const profiles = [ + { id: "prof_001", imsi: "208930000000001", euiccId: "89049032004008882600001", status: "active", subscriber: "Alice Martin", activatedAt: "2026-03-15" }, + { id: "prof_002", imsi: "208930000000002", euiccId: "89049032004008882600002", status: "active", subscriber: "Bob Dupont", activatedAt: "2026-03-20" }, + { id: "prof_003", imsi: "208930000000003", euiccId: "89049032004008882600003", status: "downloading", subscriber: "Claire Moreau", activatedAt: "-" }, + { id: "prof_004", imsi: "208930000000005", euiccId: "89049032004008882600005", status: "active", subscriber: "Emma Petit", activatedAt: "2026-02-10" }, + { id: "prof_005", imsi: "208930000000008", euiccId: "89049032004008882600008", status: "inactive", subscriber: "Henri Faure", activatedAt: "-" }, +]; + +const statusVariant = (s: string) => + s === "active" ? "success" : s === "downloading" ? "secondary" : s === "failed" ? "destructive" : "warning"; + +export default function ESIMPage() { + return ( +
+
+
+ +
+

eSIM Profiles

+

Manage eSIM profile provisioning via SM-DP+ (ES2+).

+
+
+ +
+ + + + All Profiles + + +
+ + + + + + + + + + + + + + {profiles.map((p) => ( + + + + + + + + + + ))} + +
Profile IDSubscriberIMSIeUICC IDStatusActivatedActions
{p.id}{p.subscriber}{p.imsi}{p.euiccId}{p.status}{p.activatedAt} + +
+
+
+
+
+ ); +} diff --git a/apps/web-dashboard/src/app/health/page.tsx b/apps/web-dashboard/src/app/health/page.tsx new file mode 100644 index 0000000..10acf43 --- /dev/null +++ b/apps/web-dashboard/src/app/health/page.tsx @@ -0,0 +1,62 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { HeartPulse, Server, Database, Cpu, HardDrive, Wifi } from "lucide-react"; + +const services = [ + { name: "API Server", icon: Server, status: "healthy", latency: "12ms", uptime: "99.99%", cpu: "23%", memory: "512 MB" }, + { name: "PostgreSQL", icon: Database, status: "healthy", latency: "3ms", uptime: "99.97%", cpu: "15%", memory: "2.1 GB" }, + { name: "Redis Cache", icon: Database, status: "healthy", latency: "1ms", uptime: "99.99%", cpu: "5%", memory: "256 MB" }, + { name: "Charging Engine (Rust)", icon: Cpu, status: "healthy", latency: "2ms", uptime: "99.98%", cpu: "8%", memory: "64 MB" }, + { name: "AMF Gateway", icon: Wifi, status: "healthy", latency: "45ms", uptime: "99.95%", cpu: "12%", memory: "128 MB" }, + { name: "ES2+ SM-DP+", icon: HardDrive, status: "degraded", latency: "230ms", uptime: "98.50%", cpu: "N/A", memory: "N/A" }, +]; + +export default function HealthPage() { + return ( +
+
+ +
+

System Health

+

Monitor service health, latency, and resource usage.

+
+
+ +
+ {services.map((svc) => ( + + +
+ + {svc.name} +
+ + {svc.status} + +
+ +
+
+

Latency

+

{svc.latency}

+
+
+

Uptime

+

{svc.uptime}

+
+
+

CPU

+

{svc.cpu}

+
+
+

Memory

+

{svc.memory}

+
+
+
+
+ ))} +
+
+ ); +} diff --git a/apps/web-dashboard/src/app/layout.tsx b/apps/web-dashboard/src/app/layout.tsx index e159121..24fb2f7 100644 --- a/apps/web-dashboard/src/app/layout.tsx +++ b/apps/web-dashboard/src/app/layout.tsx @@ -2,8 +2,9 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono, Inter } from "next/font/google"; import "./globals.css"; import { cn } from "@/lib/utils"; +import { Sidebar } from "@/components/sidebar"; -const inter = Inter({subsets:['latin'],variable:'--font-sans'}); +const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); const geistSans = Geist({ variable: "--font-geist-sans", @@ -16,8 +17,8 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "Telecom Admin Dashboard", + description: "Sovereign Telecom-as-a-Service Platform Admin Dashboard", }; export default function RootLayout({ @@ -30,7 +31,10 @@ export default function RootLayout({ lang="en" className={cn("h-full", "antialiased", geistSans.variable, geistMono.variable, "font-sans", inter.variable)} > - {children} + + +
{children}
+ ); } diff --git a/apps/web-dashboard/src/app/page.tsx b/apps/web-dashboard/src/app/page.tsx index 0ad7bac..a2b286c 100644 --- a/apps/web-dashboard/src/app/page.tsx +++ b/apps/web-dashboard/src/app/page.tsx @@ -1,27 +1,108 @@ -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { StatCard } from "@/components/stat-card"; +import { Users, Activity, AlertTriangle, Server, Wifi, Database } from "lucide-react"; -export default function Home() { +export default function DashboardPage() { return ( -
-
- +
+
+

Dashboard

+

Telecom platform overview and real-time metrics.

+
+ +
+ + + + +
+ +
+ - Telecom Dashboard - - Sovereign Telecom-as-a-Service Platform - + Recent Subscribers - - - + +
+ {[ + { name: "Alice Martin", msisdn: "+33612345678", status: "active" as const }, + { name: "Bob Dupont", msisdn: "+33698765432", status: "active" as const }, + { name: "Claire Moreau", msisdn: "+33678901234", status: "provisioning" as const }, + { name: "David Leroy", msisdn: "+33667890123", status: "suspended" as const }, + { name: "Emma Petit", msisdn: "+33656789012", status: "active" as const }, + ].map((sub) => ( +
+
+

{sub.name}

+

{sub.msisdn}

+
+ + {sub.status} + +
+ ))} +
- -
-

Powered by Next.js and shadcn/ui

-
-
+ + + + System Status + + +
+ {[ + { label: "API Server", icon: Server, status: "Healthy", ok: true }, + { label: "Database", icon: Database, status: "Connected", ok: true }, + { label: "Redis Cache", icon: Database, status: "Connected", ok: true }, + { label: "Charging Engine", icon: Activity, status: "Running", ok: true }, + { label: "AMF Gateway", icon: Wifi, status: "Connected", ok: true }, + ].map((svc) => ( +
+
+ + {svc.label} +
+
+ + {svc.status} +
+
+ ))} +
+
+
+
); } diff --git a/apps/web-dashboard/src/app/payments/page.tsx b/apps/web-dashboard/src/app/payments/page.tsx new file mode 100644 index 0000000..97ff507 --- /dev/null +++ b/apps/web-dashboard/src/app/payments/page.tsx @@ -0,0 +1,73 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { StatCard } from "@/components/stat-card"; +import { CreditCard, ArrowUpRight, ArrowDownRight, Clock } from "lucide-react"; + +const transactions = [ + { id: "txn_001", subscriber: "Alice Martin", type: "TOP_UP", amount: 50.0, status: "completed", date: "2026-04-14 14:23" }, + { id: "txn_002", subscriber: "Bob Dupont", type: "TOP_UP", amount: 25.0, status: "completed", date: "2026-04-14 13:15" }, + { id: "txn_003", subscriber: "David Leroy", type: "TOP_UP", amount: 10.0, status: "failed", date: "2026-04-14 12:45" }, + { id: "txn_004", subscriber: "Emma Petit", type: "REFUND", amount: -15.0, status: "completed", date: "2026-04-14 11:30" }, + { id: "txn_005", subscriber: "Henri Faure", type: "TOP_UP", amount: 100.0, status: "pending", date: "2026-04-14 10:00" }, + { id: "txn_006", subscriber: "François Blanc", type: "TOP_UP", amount: 30.0, status: "completed", date: "2026-04-13 22:15" }, +]; + +export default function PaymentsPage() { + return ( +
+
+

Payments

+

Transaction history and payment management.

+
+ +
+ + + + +
+ + + + Recent Transactions + + +
+ + + + + + + + + + + + + {transactions.map((txn) => ( + + + + + + + + + ))} + +
Transaction IDSubscriberTypeAmountStatusDate
{txn.id}{txn.subscriber} + {txn.type} + + €{Math.abs(txn.amount).toFixed(2)} + + + {txn.status} + + {txn.date}
+
+
+
+
+ ); +} diff --git a/apps/web-dashboard/src/app/settings/page.tsx b/apps/web-dashboard/src/app/settings/page.tsx new file mode 100644 index 0000000..0a6d3fd --- /dev/null +++ b/apps/web-dashboard/src/app/settings/page.tsx @@ -0,0 +1,75 @@ +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Settings } from "lucide-react"; + +export default function SettingsPage() { + return ( +
+
+ +
+

Settings

+

Platform configuration and preferences.

+
+
+ +
+ + + API Configuration + Configure the API server connection. + + +
+ + +
+
+ + +
+ +
+
+ + + + IMSI Configuration + Mobile Country Code and Mobile Network Code settings. + + +
+
+ + +
+
+ + +
+
+ +
+
+ + + + ES2+ SM-DP+ Configuration + eSIM profile server connection settings. + + +
+ + +
+
+ + +
+ +
+
+
+
+ ); +} diff --git a/apps/web-dashboard/src/app/subscribers/page.tsx b/apps/web-dashboard/src/app/subscribers/page.tsx new file mode 100644 index 0000000..63ba8a2 --- /dev/null +++ b/apps/web-dashboard/src/app/subscribers/page.tsx @@ -0,0 +1,94 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { UserPlus, Search, Download } from "lucide-react"; + +const subscribers = [ + { id: 1, name: "Alice Martin", msisdn: "+33612345678", imsi: "208930000000001", email: "alice@example.com", status: "active" as const, balance: 45.0, plan: "Premium 5G" }, + { id: 2, name: "Bob Dupont", msisdn: "+33698765432", imsi: "208930000000002", email: "bob@example.com", status: "active" as const, balance: 12.5, plan: "Basic LTE" }, + { id: 3, name: "Claire Moreau", msisdn: "+33678901234", imsi: "208930000000003", email: "claire@example.com", status: "provisioning" as const, balance: 0.0, plan: "Premium 5G" }, + { id: 4, name: "David Leroy", msisdn: "+33667890123", imsi: "208930000000004", email: "david@example.com", status: "suspended" as const, balance: -2.3, plan: "Standard" }, + { id: 5, name: "Emma Petit", msisdn: "+33656789012", imsi: "208930000000005", email: "emma@example.com", status: "active" as const, balance: 78.9, plan: "Premium 5G" }, + { id: 6, name: "François Blanc", msisdn: "+33645678901", imsi: "208930000000006", email: "francois@example.com", status: "active" as const, balance: 25.0, plan: "Standard" }, + { id: 7, name: "Gabrielle Roux", msisdn: "+33634567890", imsi: "208930000000007", email: "gabrielle@example.com", status: "terminated" as const, balance: 0.0, plan: "Basic LTE" }, + { id: 8, name: "Henri Faure", msisdn: "+33623456789", imsi: "208930000000008", email: "henri@example.com", status: "active" as const, balance: 156.2, plan: "Enterprise" }, +]; + +const statusVariant = (s: string) => + s === "active" ? "success" : s === "suspended" ? "warning" : s === "terminated" ? "destructive" : "secondary"; + +export default function SubscribersPage() { + return ( +
+
+
+

Subscribers

+

Manage subscriber accounts and profiles.

+
+
+ + +
+
+ + + + All Subscribers +
+
+ + +
+
+
+ +
+ + + + + + + + + + + + + + {subscribers.map((sub) => ( + + + + + + + + + + ))} + +
NameMSISDNIMSIPlanBalanceStatusActions
+
+

{sub.name}

+

{sub.email}

+
+
{sub.msisdn}{sub.imsi}{sub.plan} + + €{sub.balance.toFixed(2)} + + + {sub.status} + + +
+
+
+
+
+ ); +} diff --git a/apps/web-dashboard/src/app/usage/page.tsx b/apps/web-dashboard/src/app/usage/page.tsx new file mode 100644 index 0000000..3b52bd0 --- /dev/null +++ b/apps/web-dashboard/src/app/usage/page.tsx @@ -0,0 +1,78 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { StatCard } from "@/components/stat-card"; +import { Activity, HardDrive, Phone, MessageSquare } from "lucide-react"; + +const usageEvents = [ + { id: 1, imsi: "208930000000001", type: "DATA", volume: "1.2 GB", cost: "€2.40", time: "2 min ago" }, + { id: 2, imsi: "208930000000002", type: "VOICE", volume: "15 min", cost: "€0.75", time: "5 min ago" }, + { id: 3, imsi: "208930000000005", type: "SMS", volume: "3 msgs", cost: "€0.30", time: "12 min ago" }, + { id: 4, imsi: "208930000000001", type: "DATA", volume: "450 MB", cost: "€0.90", time: "18 min ago" }, + { id: 5, imsi: "208930000000008", type: "DATA", volume: "2.8 GB", cost: "€5.60", time: "25 min ago" }, + { id: 6, imsi: "208930000000006", type: "VOICE", volume: "42 min", cost: "€2.10", time: "30 min ago" }, + { id: 7, imsi: "208930000000002", type: "DATA", volume: "780 MB", cost: "€1.56", time: "45 min ago" }, + { id: 8, imsi: "208930000000005", type: "SMS", volume: "1 msg", cost: "€0.10", time: "1 hr ago" }, +]; + +const typeIcon = (t: string) => { + if (t === "DATA") return HardDrive; + if (t === "VOICE") return Phone; + return MessageSquare; +}; + +export default function UsagePage() { + return ( +
+
+

Usage & Billing

+

Monitor real-time usage and billing metrics.

+
+ +
+ + + + +
+ + + + Recent Usage Events + + +
+ + + + + + + + + + + + {usageEvents.map((evt) => { + const Icon = typeIcon(evt.type); + return ( + + + + + + + + ); + })} + +
TypeIMSIVolumeCostTime
+
+ + {evt.type} +
+
{evt.imsi}{evt.volume}{evt.cost}{evt.time}
+
+
+
+
+ ); +} diff --git a/apps/web-dashboard/src/components/sidebar.tsx b/apps/web-dashboard/src/components/sidebar.tsx new file mode 100644 index 0000000..892de9c --- /dev/null +++ b/apps/web-dashboard/src/components/sidebar.tsx @@ -0,0 +1,62 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + Users, + LayoutDashboard, + Activity, + CreditCard, + Shield, + Settings, + Radio, + HeartPulse, +} from "lucide-react"; +import { cn } from "@/lib/utils"; + +const navItems = [ + { href: "/", label: "Dashboard", icon: LayoutDashboard }, + { href: "/subscribers", label: "Subscribers", icon: Users }, + { href: "/usage", label: "Usage & Billing", icon: Activity }, + { href: "/payments", label: "Payments", icon: CreditCard }, + { href: "/esim", label: "eSIM Profiles", icon: Radio }, + { href: "/health", label: "System Health", icon: HeartPulse }, + { href: "/chaos", label: "Chaos Engineering", icon: Shield }, + { href: "/settings", label: "Settings", icon: Settings }, +]; + +export function Sidebar() { + const pathname = usePathname(); + + return ( + + ); +} diff --git a/apps/web-dashboard/src/components/stat-card.tsx b/apps/web-dashboard/src/components/stat-card.tsx new file mode 100644 index 0000000..5e60301 --- /dev/null +++ b/apps/web-dashboard/src/components/stat-card.tsx @@ -0,0 +1,36 @@ +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { type LucideIcon } from "lucide-react"; +import { cn } from "@/lib/utils"; + +interface StatCardProps { + title: string; + value: string | number; + description?: string; + icon: LucideIcon; + trend?: { value: number; positive: boolean }; + className?: string; +} + +export function StatCard({ title, value, description, icon: Icon, trend, className }: StatCardProps) { + return ( + + + {title} + + + +
{value}
+ {(description || trend) && ( +

+ {trend && ( + + {trend.positive ? "+" : ""}{trend.value}%{" "} + + )} + {description} +

+ )} +
+
+ ); +} diff --git a/apps/web-dashboard/src/components/ui/badge.tsx b/apps/web-dashboard/src/components/ui/badge.tsx new file mode 100644 index 0000000..777db9f --- /dev/null +++ b/apps/web-dashboard/src/components/ui/badge.tsx @@ -0,0 +1,32 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium transition-colors", + { + variants: { + variant: { + default: "border-transparent bg-primary text-primary-foreground", + secondary: "border-transparent bg-secondary text-secondary-foreground", + destructive: "border-transparent bg-destructive/10 text-destructive", + outline: "text-foreground", + success: "border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400", + warning: "border-transparent bg-amber-500/10 text-amber-600 dark:text-amber-400", + }, + }, + defaultVariants: { variant: "default" }, + } +) + +function Badge({ + className, + variant, + ...props +}: React.ComponentProps<"span"> & VariantProps) { + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/apps/web-dashboard/src/lib/api.ts b/apps/web-dashboard/src/lib/api.ts new file mode 100644 index 0000000..bd8a072 --- /dev/null +++ b/apps/web-dashboard/src/lib/api.ts @@ -0,0 +1,69 @@ +const API_BASE = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8000"; + +async function apiFetch(path: string, init?: RequestInit): Promise { + const res = await fetch(`${API_BASE}${path}`, { + headers: { "Content-Type": "application/json", ...init?.headers }, + ...init, + }); + if (!res.ok) { + const body = await res.json().catch(() => ({})); + throw new Error(body.message || res.statusText); + } + return res.json(); +} + +export interface Subscriber { + id: number; + imsi: string; + msisdn: string; + first_name: string; + last_name: string; + email: string; + organization_id: string; + status: "active" | "inactive" | "suspended" | "terminated" | "provisioning"; + plan_id: number; + balance: number; + profile_status: string; + created_at: string; + updated_at: string; +} + +export interface SystemStats { + active_sessions: number; + total_accounts: number; + blocked_users: number; + low_balance_alerts: number; + uptime: number; +} + +export interface HealthStatus { + redis_connected: boolean; + active_sync: boolean; + last_sync: string; + memory_usage: number; +} + +export interface PaginatedResponse { + data: T[]; + total: number; + page: number; + page_size: number; +} + +export const api = { + subscribers: { + list: (page = 1, pageSize = 20) => + apiFetch>(`/api/subscribers?page=${page}&pageSize=${pageSize}`), + get: (id: number) => apiFetch(`/api/subscribers/${id}`), + create: (data: Partial) => + apiFetch("/api/subscribers", { method: "POST", body: JSON.stringify(data) }), + update: (id: number, data: Partial) => + apiFetch(`/api/subscribers/${id}`, { method: "PUT", body: JSON.stringify(data) }), + delete: (id: number) => + apiFetch(`/api/subscribers/${id}`, { method: "DELETE" }), + }, + system: { + stats: () => apiFetch("/api/system/stats"), + health: () => apiFetch("/api/system/health"), + }, +};