Apply Composer changes: comprehensive API updates, migrations, middleware, and infrastructure improvements

- Add comprehensive database migrations (001-024) for schema evolution
- Enhance API schema with expanded type definitions and resolvers
- Add new middleware: audit logging, rate limiting, MFA enforcement, security, tenant auth
- Implement new services: AI optimization, billing, blockchain, compliance, marketplace
- Add adapter layer for cloud integrations (Cloudflare, Kubernetes, Proxmox, storage)
- Update Crossplane provider with enhanced VM management capabilities
- Add comprehensive test suite for API endpoints and services
- Update frontend components with improved GraphQL subscriptions and real-time updates
- Enhance security configurations and headers (CSP, CORS, etc.)
- Update documentation and configuration files
- Add new CI/CD workflows and validation scripts
- Implement design system improvements and UI enhancements
This commit is contained in:
defiQUG
2025-12-12 18:01:35 -08:00
parent e01131efaf
commit 9daf1fd378
968 changed files with 160890 additions and 1092 deletions

View File

@@ -0,0 +1,113 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Building2, Users, CreditCard, Shield, ArrowRight } from 'lucide-react';
import Link from 'next/link';
export default function AdminPortalPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Admin Portal</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
const adminSections = [
{
title: 'Organization Management',
description: 'Manage multi-tenant organizations, tenant isolation, and resource quotas',
icon: Building2,
href: '/admin/organizations',
features: ['Multi-tenant view', 'Tenant isolation', 'Resource quotas'],
},
{
title: 'User Management',
description: 'Manage users, roles, and permissions across your organization',
icon: Users,
href: '/admin/users',
features: ['User list', 'Role assignment', 'Permission management'],
},
{
title: 'Billing & Subscriptions',
description: 'View subscriptions, usage-based billing, invoices, and payment methods',
icon: CreditCard,
href: '/admin/billing',
features: ['Subscriptions', 'Usage billing', 'Invoice history'],
},
{
title: 'Compliance & Reporting',
description: 'Compliance dashboard, audit logs, and export reports',
icon: Shield,
href: '/admin/compliance',
features: ['Compliance dashboard', 'Audit logs', 'Export reports'],
},
];
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Customer / Tenant Admin Portal</h1>
<p className="text-gray-400">Manage your organization, users, billing, and compliance</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{adminSections.map((section) => {
const Icon = section.icon;
return (
<Card key={section.href} className="bg-gray-800 border-gray-700 hover:border-orange-500 transition-colors">
<CardHeader>
<div className="flex items-center gap-3 mb-2">
<Icon className="h-6 w-6 text-orange-500" />
<CardTitle className="text-white">{section.title}</CardTitle>
</div>
<p className="text-sm text-gray-400">{section.description}</p>
</CardHeader>
<CardContent>
<ul className="space-y-2 mb-4">
{section.features.map((feature) => (
<li key={feature} className="flex items-center gap-2 text-sm text-gray-300">
<span className="text-orange-500"></span>
<span>{feature}</span>
</li>
))}
</ul>
<Link
href={section.href}
className="inline-flex items-center gap-2 text-sm text-orange-500 hover:text-orange-400 transition-colors"
>
Manage <ArrowRight className="h-4 w-4" />
</Link>
</CardContent>
</Card>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,49 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { AdvancedAnalytics } from '@/components/analytics/AdvancedAnalytics';
export default function AnalyticsPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Analytics</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Advanced Analytics</h1>
<p className="text-gray-400">Comprehensive analytics and insights for your infrastructure</p>
</div>
<AdvancedAnalytics />
</div>
);
}

View File

@@ -0,0 +1,28 @@
'use client'
import { useSession } from 'next-auth/react'
import { redirect } from 'next/navigation'
import ArgoCDApplications from '@/components/argocd/ArgoCDApplications'
export default function ArgoCDPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)
}
if (!session) {
redirect('/api/auth/signin')
}
return (
<div className="container mx-auto px-4 py-8">
<ArgoCDApplications />
</div>
)
}

View File

@@ -0,0 +1,28 @@
'use client'
import { useSession } from 'next-auth/react'
import { redirect } from 'next/navigation'
import CrossplaneResourceBrowser from '@/components/crossplane/CrossplaneResourceBrowser'
export default function CrossplanePage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)
}
if (!session) {
redirect('/api/auth/signin')
}
return (
<div className="container mx-auto px-4 py-8">
<CrossplaneResourceBrowser />
</div>
)
}

View File

@@ -0,0 +1,78 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { CostOverviewTile } from '@/components/dashboard/CostOverviewTile';
import { CostForecastingTile } from '@/components/dashboard/CostForecastingTile';
import { ResourceUsageTile } from '@/components/dashboard/ResourceUsageTile';
import { BillingTile } from '@/components/dashboard/BillingTile';
import { ServiceAdoptionTile } from '@/components/dashboard/ServiceAdoptionTile';
import { ComplianceStatusTile } from '@/components/dashboard/ComplianceStatusTile';
import { QuickActionsPanel } from '@/components/dashboard/QuickActionsPanel';
export default function BusinessDashboardPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Nexus Console</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold text-white">Business Owner Dashboard</h1>
<p className="text-gray-400 mt-2">Cost, usage, billing, and compliance overview</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<CostOverviewTile />
<CostForecastingTile />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<ResourceUsageTile />
<BillingTile />
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<BillingTile />
<ServiceAdoptionTile />
<ComplianceStatusTile />
</div>
<QuickActionsPanel
actions={[
{ label: 'View Reports', href: '/reports', icon: 'FileText' },
{ label: 'Manage Billing', href: '/admin/billing', icon: 'CreditCard' },
{ label: 'Contact Support', href: '/support', icon: 'HelpCircle' },
{ label: 'Export Data', href: '/reports/export', icon: 'Download' },
]}
/>
</div>
);
}

View File

@@ -0,0 +1,70 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { APIUsageTile } from '@/components/dashboard/APIUsageTile';
import { DeploymentsTile } from '@/components/dashboard/DeploymentsTile';
import { TestEnvironmentsTile } from '@/components/dashboard/TestEnvironmentsTile';
import { APIKeysTile } from '@/components/dashboard/APIKeysTile';
import { QuickActionsPanel } from '@/components/dashboard/QuickActionsPanel';
export default function DeveloperDashboardPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Nexus Console</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold text-white">Developer Dashboard</h1>
<p className="text-gray-400 mt-2">API usage, deployments, test environments, and developer tools</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<APIUsageTile />
<DeploymentsTile />
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<TestEnvironmentsTile />
<APIKeysTile />
</div>
<QuickActionsPanel
actions={[
{ label: 'Create API Key', href: '/developer/api-keys/new', icon: 'Key' },
{ label: 'Deploy Service', href: '/developer/deploy', icon: 'Rocket' },
{ label: 'View Docs', href: '/docs', icon: 'Book' },
{ label: 'View Logs', href: '/developer/logs', icon: 'FileText' },
]}
/>
</div>
);
}

View File

@@ -0,0 +1,61 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { useEffect } from 'react';
import { getDashboardRoute } from '@/lib/roles';
export default function DashboardPage() {
const { data: session, status } = useSession();
const router = useRouter();
useEffect(() => {
if (status === 'authenticated' && session) {
const route = getDashboardRoute(session);
router.push(route);
}
}, [status, session, router]);
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Nexus Console</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
<p className="text-sm text-gray-500 mt-4">
Development mode: Use any email/password
</p>
</div>
</div>
);
}
// Redirecting...
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Redirecting to your dashboard...</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,79 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { SystemHealthTile } from '@/components/dashboard/SystemHealthTile';
import { IntegrationStatusTile } from '@/components/dashboard/IntegrationStatusTile';
import { DataPipelineTile } from '@/components/dashboard/DataPipelineTile';
import { ResourceUtilizationTile } from '@/components/dashboard/ResourceUtilizationTile';
import { OptimizationEngine } from '@/components/ai/OptimizationEngine';
import { QuickActionsPanel } from '@/components/dashboard/QuickActionsPanel';
export default function TechnicalDashboardPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Nexus Console</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-6">
<h1 className="text-3xl font-bold text-white">Technical Admin Dashboard</h1>
<p className="text-gray-400 mt-2">Operational view of infrastructure, integrations, and system health</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-6">
<div className="lg:col-span-2">
<SystemHealthTile />
</div>
<div>
<IntegrationStatusTile />
</div>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<DataPipelineTile />
<ResourceUtilizationTile />
</div>
<div className="mb-6">
<OptimizationEngine />
</div>
<QuickActionsPanel
actions={[
{ label: 'Add Integration', href: '/integrations/new', icon: 'Plus' },
{ label: 'Create Connection', href: '/connections/new', icon: 'Link' },
{ label: 'View Logs', href: '/monitoring/logs', icon: 'FileText' },
{ label: 'System Settings', href: '/settings/system', icon: 'Settings' },
]}
/>
</div>
);
}

View File

@@ -0,0 +1,59 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
// Metrics dashboard - component to be implemented
// import Dashboard from '@/components/dashboards/Dashboard';
export default function DashboardsPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Access Denied</h1>
<p className="text-gray-400 mb-6">Please sign in to view dashboards</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6 text-white">Metrics Dashboards</h1>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">CPU Usage</h2>
<p className="text-gray-400">CPU metrics dashboard coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Memory Usage</h2>
<p className="text-gray-400">Memory metrics dashboard coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Network Throughput</h2>
<p className="text-gray-400">Network metrics dashboard coming soon</p>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,127 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Key, Book, TestTube, BarChart3, Webhook, Download, ArrowRight } from 'lucide-react';
import Link from 'next/link';
export default function DeveloperPortalPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Developer Portal</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
const developerSections = [
{
title: 'API Key Management',
description: 'Create, manage, and revoke API keys for your applications',
icon: Key,
href: '/developer/api-keys',
features: ['Create API keys', 'View usage', 'Revoke keys'],
},
{
title: 'API Documentation',
description: 'Interactive API documentation with examples and code snippets',
icon: Book,
href: '/developer/docs',
features: ['REST API docs', 'GraphQL schema', 'Code examples'],
},
{
title: 'Test Environments',
description: 'Provision and manage sandbox environments for testing',
icon: TestTube,
href: '/developer/environments',
features: ['Create environments', 'Sandbox access', 'Auto-cleanup'],
},
{
title: 'Usage Analytics',
description: 'Monitor API usage, quotas, and performance metrics',
icon: BarChart3,
href: '/developer/analytics',
features: ['API usage stats', 'Quota monitoring', 'Performance metrics'],
},
{
title: 'Webhook Configuration',
description: 'Configure webhooks for real-time event notifications',
icon: Webhook,
href: '/developer/webhooks',
features: ['Create webhooks', 'Event subscriptions', 'Delivery logs'],
},
{
title: 'SDK Downloads',
description: 'Download SDKs and client libraries for popular languages',
icon: Download,
href: '/developer/sdks',
features: ['TypeScript SDK', 'Python SDK', 'Go SDK'],
},
];
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Developer Portal</h1>
<p className="text-gray-400">API keys, documentation, test environments, and developer tools</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{developerSections.map((section) => {
const Icon = section.icon;
return (
<Card key={section.href} className="bg-gray-800 border-gray-700 hover:border-orange-500 transition-colors">
<CardHeader>
<div className="flex items-center gap-3 mb-2">
<Icon className="h-6 w-6 text-orange-500" />
<CardTitle className="text-white">{section.title}</CardTitle>
</div>
<p className="text-sm text-gray-400">{section.description}</p>
</CardHeader>
<CardContent>
<ul className="space-y-2 mb-4">
{section.features.map((feature) => (
<li key={feature} className="flex items-center gap-2 text-sm text-gray-300">
<span className="text-orange-500"></span>
<span>{feature}</span>
</li>
))}
</ul>
<Link
href={section.href}
className="inline-flex items-center gap-2 text-sm text-orange-500 hover:text-orange-400 transition-colors"
>
Access <ArrowRight className="h-4 w-4" />
</Link>
</CardContent>
</Card>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,191 @@
'use client';
import { useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Send, CheckCircle, XCircle, Clock } from 'lucide-react';
export default function WebhookTestingPage() {
const [url, setUrl] = useState('');
const [method, setMethod] = useState('POST');
const [headers, setHeaders] = useState('{"Content-Type": "application/json"}');
const [payload, setPayload] = useState('{"event": "test", "data": {}}');
const [testResult, setTestResult] = useState<any>(null);
const [isTesting, setIsTesting] = useState(false);
const testWebhook = async () => {
setIsTesting(true);
setTestResult(null);
try {
const startTime = Date.now();
const response = await fetch('/api/webhooks/test', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url,
method,
headers: JSON.parse(headers),
payload: JSON.parse(payload),
}),
});
const endTime = Date.now();
const data = await response.json();
setTestResult({
success: response.ok,
status: response.status,
statusText: response.statusText,
responseTime: endTime - startTime,
response: data,
timestamp: new Date().toISOString(),
});
} catch (error: any) {
setTestResult({
success: false,
error: error.message,
timestamp: new Date().toISOString(),
});
} finally {
setIsTesting(false);
}
};
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Webhook Testing Tool</h1>
<p className="text-gray-400">Test your webhook endpoints before configuring them</p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card className="bg-gray-800 border-gray-700">
<CardHeader>
<CardTitle className="text-white">Webhook Configuration</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<label className="block text-sm text-gray-300 mb-2">Webhook URL</label>
<input
type="url"
value={url}
onChange={(e) => setUrl(e.target.value)}
placeholder="https://example.com/webhook"
className="w-full px-4 py-2 bg-gray-900 border border-gray-700 rounded text-white"
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-2">HTTP Method</label>
<select
value={method}
onChange={(e) => setMethod(e.target.value)}
className="w-full px-4 py-2 bg-gray-900 border border-gray-700 rounded text-white"
>
<option value="POST">POST</option>
<option value="GET">GET</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div>
<label className="block text-sm text-gray-300 mb-2">Headers (JSON)</label>
<textarea
value={headers}
onChange={(e) => setHeaders(e.target.value)}
className="w-full h-24 px-4 py-2 bg-gray-900 border border-gray-700 rounded text-white font-mono text-sm"
placeholder='{"Content-Type": "application/json"}'
/>
</div>
<div>
<label className="block text-sm text-gray-300 mb-2">Payload (JSON)</label>
<textarea
value={payload}
onChange={(e) => setPayload(e.target.value)}
className="w-full h-32 px-4 py-2 bg-gray-900 border border-gray-700 rounded text-white font-mono text-sm"
placeholder='{"event": "test", "data": {}}'
/>
</div>
<button
onClick={testWebhook}
disabled={!url || isTesting}
className="w-full px-6 py-3 bg-orange-500 text-white rounded-lg hover:bg-orange-600 transition-colors disabled:opacity-50 flex items-center justify-center gap-2"
>
<Send className="h-4 w-4" />
{isTesting ? 'Testing...' : 'Test Webhook'}
</button>
</CardContent>
</Card>
<Card className="bg-gray-800 border-gray-700">
<CardHeader>
<CardTitle className="text-white">Test Results</CardTitle>
</CardHeader>
<CardContent>
{!testResult ? (
<div className="text-center py-12 text-gray-400">
<p>Test results will appear here</p>
</div>
) : (
<div className="space-y-4">
<div className="flex items-center gap-2">
{testResult.success ? (
<CheckCircle className="h-5 w-5 text-green-400" />
) : (
<XCircle className="h-5 w-5 text-red-400" />
)}
<span className={`font-semibold ${testResult.success ? 'text-green-400' : 'text-red-400'}`}>
{testResult.success ? 'Success' : 'Failed'}
</span>
</div>
{testResult.status && (
<div>
<p className="text-sm text-gray-400 mb-1">Status</p>
<p className="text-white font-mono">
{testResult.status} {testResult.statusText}
</p>
</div>
)}
{testResult.responseTime && (
<div>
<p className="text-sm text-gray-400 mb-1">Response Time</p>
<p className="text-white font-mono">{testResult.responseTime}ms</p>
</div>
)}
{testResult.timestamp && (
<div>
<p className="text-sm text-gray-400 mb-1">Timestamp</p>
<p className="text-white text-sm">{new Date(testResult.timestamp).toLocaleString()}</p>
</div>
)}
{testResult.response && (
<div>
<p className="text-sm text-gray-400 mb-1">Response</p>
<pre className="p-3 bg-gray-900 rounded text-white font-mono text-xs overflow-auto max-h-64">
{JSON.stringify(testResult.response, null, 2)}
</pre>
</div>
)}
{testResult.error && (
<div>
<p className="text-sm text-gray-400 mb-1">Error</p>
<p className="text-red-400 font-mono text-sm">{testResult.error}</p>
</div>
)}
</div>
)}
</CardContent>
</Card>
</div>
</div>
);
}

50
portal/src/app/error.tsx Normal file
View File

@@ -0,0 +1,50 @@
'use client'
import { useEffect } from 'react'
import Link from 'next/link'
import { Home, RefreshCw, AlertCircle } from 'lucide-react'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
useEffect(() => {
// Log error to error reporting service
console.error('Application error:', error)
}, [error])
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<AlertCircle className="h-16 w-16 text-red-500 mx-auto mb-4" />
<h1 className="text-3xl font-bold text-white mb-4">Something went wrong!</h1>
<p className="text-gray-400 mb-2">
An unexpected error occurred. Please try again.
</p>
{error.digest && (
<p className="text-sm text-gray-500 mb-8">Error ID: {error.digest}</p>
)}
<div className="flex gap-4 justify-center">
<button
onClick={reset}
className="flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<RefreshCw className="h-5 w-5" />
Try Again
</button>
<Link
href="/"
className="flex items-center gap-2 px-6 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
>
<Home className="h-5 w-5" />
Go Home
</Link>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,28 @@
'use client'
import { useSession } from 'next-auth/react'
import { redirect } from 'next/navigation'
import KubernetesClusters from '@/components/kubernetes/KubernetesClusters'
export default function KubernetesPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)
}
if (!session) {
redirect('/api/auth/signin')
}
return (
<div className="container mx-auto px-4 py-8">
<KubernetesClusters />
</div>
)
}

View File

@@ -1,26 +1,44 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { Providers } from './providers';
'use client'
const inter = Inter({ subsets: ['latin'] });
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import { Providers } from './providers'
import { PortalHeader } from '@/components/layout/PortalHeader'
import { PortalSidebar } from '@/components/layout/PortalSidebar'
import { PortalBreadcrumbs } from '@/components/layout/PortalBreadcrumbs'
import { MobileNavigation } from '@/components/layout/MobileNavigation'
import { KeyboardShortcutsProvider } from '@/components/KeyboardShortcutsProvider'
import { SessionProvider } from 'next-auth/react'
export const metadata: Metadata = {
title: 'Hybrid Cloud Control Plane',
description: 'Unified management portal for hybrid cloud infrastructure',
};
const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({
children,
}: {
children: React.ReactNode;
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>{children}</Providers>
<SessionProvider>
<Providers>
<KeyboardShortcutsProvider>
<div className="flex min-h-screen flex-col">
<PortalHeader />
<PortalBreadcrumbs />
<div className="flex flex-1">
<PortalSidebar />
<main className="flex-1 ml-0 md:ml-64">
{children}
</main>
</div>
<MobileNavigation />
</div>
</KeyboardShortcutsProvider>
</Providers>
</SessionProvider>
</body>
</html>
);
)
}

View File

@@ -0,0 +1,29 @@
'use client'
import { useSession } from 'next-auth/react'
export default function MLPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (status === 'unauthenticated') {
return <div>Please sign in</div>
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">AI Foundry</h1>
<p className="text-muted-foreground mb-4">
ML platform for model training, inference, and pipeline management
</p>
{/* ML platform UI will be implemented here */}
<div className="border rounded-lg p-4">
<p className="text-sm text-muted-foreground">ML platform interface coming soon</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,62 @@
'use client'
import { useSession } from 'next-auth/react'
import { redirect } from 'next/navigation'
import GrafanaPanel from '@/components/monitoring/GrafanaPanel'
import LokiLogViewer from '@/components/monitoring/LokiLogViewer'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/Tabs'
export default function MonitoringPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
</div>
)
}
if (!session) {
redirect('/api/auth/signin')
}
return (
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-6">Monitoring</h1>
<Tabs defaultValue="grafana" className="space-y-4">
<TabsList>
<TabsTrigger value="grafana">Grafana Dashboards</TabsTrigger>
<TabsTrigger value="logs">Logs (Loki)</TabsTrigger>
</TabsList>
<TabsContent value="grafana" className="space-y-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
<div>
<h2 className="text-xl font-semibold mb-4">System Metrics</h2>
<GrafanaPanel
dashboardUID="system"
panelId={1}
height="400px"
/>
</div>
<div>
<h2 className="text-xl font-semibold mb-4">Application Metrics</h2>
<GrafanaPanel
dashboardUID="application"
panelId={2}
height="400px"
/>
</div>
</div>
</TabsContent>
<TabsContent value="logs">
<LokiLogViewer />
</TabsContent>
</Tabs>
</div>
)
}

View File

@@ -0,0 +1,51 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
// Network topology view - component to be implemented
// import { NetworkTopologyView } from '@/components/network/NetworkTopologyView';
export default function NetworkPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Access Denied</h1>
<p className="text-gray-400 mb-6">Please sign in to view network topology</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6 text-white">Network Topology</h1>
<div className="border rounded-lg p-4 bg-gray-800">
<p className="text-gray-400">Network topology visualization coming soon</p>
<p className="text-sm text-gray-500 mt-2">
This will display the network graph showing relationships between resources across Proxmox, Kubernetes, and Cloudflare.
</p>
</div>
</div>
);
}

View File

@@ -0,0 +1,35 @@
'use client'
import Link from 'next/link'
import { Home, ArrowLeft } from 'lucide-react'
export default function NotFound() {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-6xl font-bold text-white mb-4">404</h1>
<h2 className="text-2xl font-semibold text-gray-300 mb-4">Page Not Found</h2>
<p className="text-gray-400 mb-8">
The page you're looking for doesn't exist or has been moved.
</p>
<div className="flex gap-4 justify-center">
<Link
href="/"
className="flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Home className="h-5 w-5" />
Go Home
</Link>
<button
onClick={() => window.history.back()}
className="flex items-center gap-2 px-6 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
>
<ArrowLeft className="h-5 w-5" />
Go Back
</button>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,37 @@
'use client';
import { OnboardingWizard } from '@/components/onboarding/OnboardingWizard';
import { WelcomeStep } from '@/components/onboarding/steps/WelcomeStep';
import { ProfileStep } from '@/components/onboarding/steps/ProfileStep';
import { PreferencesStep } from '@/components/onboarding/steps/PreferencesStep';
const onboardingSteps = [
{
id: 'welcome',
title: 'Welcome',
description: 'Get started with Nexus Console',
component: WelcomeStep,
},
{
id: 'profile',
title: 'Profile',
description: 'Set up your profile information',
component: ProfileStep,
},
{
id: 'preferences',
title: 'Preferences',
description: 'Configure your preferences',
component: PreferencesStep,
},
];
export default function OnboardingPage() {
const handleComplete = () => {
// Mark onboarding as complete
localStorage.setItem('onboarding-complete', 'true');
};
return <OnboardingWizard steps={onboardingSteps} onComplete={handleComplete} />;
}

View File

@@ -0,0 +1,113 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Handshake, TrendingUp, BookOpen, Package, ArrowRight } from 'lucide-react';
import Link from 'next/link';
export default function PartnerPortalPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Welcome to Partner Portal</h1>
<p className="text-gray-400 mb-6">Please sign in to continue</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
const partnerSections = [
{
title: 'Co-Sell Deal Management',
description: 'Register and manage co-sell deals with Sankofa',
icon: Handshake,
href: '/partner/deals',
features: ['Deal registration', 'Deal tracking', 'Revenue sharing'],
},
{
title: 'Technical Onboarding',
description: 'Access technical resources and onboarding materials',
icon: BookOpen,
href: '/partner/onboarding',
features: ['Technical training', 'Architecture guides', 'Best practices'],
},
{
title: 'Solution Registration',
description: 'Register your solutions in the marketplace',
icon: Package,
href: '/partner/solutions',
features: ['Solution listing', 'Certification', 'Marketplace access'],
},
{
title: 'Partner Resources',
description: 'Marketing materials, sales enablement, and training resources',
icon: TrendingUp,
href: '/partner/resources',
features: ['Marketing assets', 'Sales materials', 'Training programs'],
},
];
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Partner Portal</h1>
<p className="text-gray-400">Co-sell deals, technical onboarding, and solution registration</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{partnerSections.map((section) => {
const Icon = section.icon;
return (
<Card key={section.href} className="bg-gray-800 border-gray-700 hover:border-orange-500 transition-colors">
<CardHeader>
<div className="flex items-center gap-3 mb-2">
<Icon className="h-6 w-6 text-orange-500" />
<CardTitle className="text-white">{section.title}</CardTitle>
</div>
<p className="text-sm text-gray-400">{section.description}</p>
</CardHeader>
<CardContent>
<ul className="space-y-2 mb-4">
{section.features.map((feature) => (
<li key={feature} className="flex items-center gap-2 text-sm text-gray-300">
<span className="text-orange-500"></span>
<span>{feature}</span>
</li>
))}
</ul>
<Link
href={section.href}
className="inline-flex items-center gap-2 text-sm text-orange-500 hover:text-orange-400 transition-colors"
>
Access <ArrowRight className="h-4 w-4" />
</Link>
</CardContent>
</Card>
);
})}
</div>
</div>
);
}

View File

@@ -0,0 +1,29 @@
'use client'
import { useSession } from 'next-auth/react'
export default function PoliciesPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (status === 'unauthenticated') {
return <div>Please sign in</div>
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">Policy Management</h1>
<p className="text-muted-foreground mb-4">
Manage compliance, security, and cost optimization policies
</p>
{/* Policy management UI will be implemented here */}
<div className="border rounded-lg p-4">
<p className="text-sm text-muted-foreground">Policy management interface coming soon</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,29 @@
'use client'
import { useSession } from 'next-auth/react'
export default function ResourceGraphPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (status === 'unauthenticated') {
return <div>Please sign in</div>
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">Resource Graph</h1>
<p className="text-muted-foreground mb-4">
Visualize resource relationships and dependencies
</p>
{/* React Flow graph visualization will be implemented here */}
<div className="border rounded-lg p-4 h-96">
<p className="text-sm text-muted-foreground">Resource graph visualization coming soon</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,30 @@
'use client'
import { useState } from 'react'
import { useSession } from 'next-auth/react'
export default function ResourcesPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (status === 'unauthenticated') {
return <div>Please sign in</div>
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">Resource Inventory</h1>
<p className="text-muted-foreground mb-4">
Unified view of all resources across Proxmox, Kubernetes, and Cloudflare
</p>
{/* Resource inventory UI will be implemented here */}
<div className="border rounded-lg p-4">
<p className="text-sm text-muted-foreground">Resource inventory table coming soon</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,157 @@
'use client';
import { useSession } from 'next-auth/react';
import { useState } from 'react';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/Card';
import { Shield, CheckCircle, XCircle } from 'lucide-react';
import QRCode from 'qrcode';
export default function TwoFactorAuthPage() {
const { data: session } = useSession();
const [isEnabled, setIsEnabled] = useState(false);
const [qrCode, setQrCode] = useState<string | null>(null);
const [secret, setSecret] = useState<string | null>(null);
const [verificationCode, setVerificationCode] = useState('');
const [isVerifying, setIsVerifying] = useState(false);
const enable2FA = async () => {
try {
// In production, this would call your backend API
const response = await fetch('/api/auth/2fa/setup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
});
const data = await response.json();
setSecret(data.secret);
const qr = await QRCode.toDataURL(data.qrCodeUrl);
setQrCode(qr);
} catch (error) {
console.error('Failed to enable 2FA:', error);
}
};
const verifyAndEnable = async () => {
setIsVerifying(true);
try {
const response = await fetch('/api/auth/2fa/verify', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: verificationCode, secret }),
});
if (response.ok) {
setIsEnabled(true);
setQrCode(null);
setSecret(null);
}
} catch (error) {
console.error('Verification failed:', error);
} finally {
setIsVerifying(false);
}
};
const disable2FA = async () => {
try {
await fetch('/api/auth/2fa/disable', { method: 'POST' });
setIsEnabled(false);
} catch (error) {
console.error('Failed to disable 2FA:', error);
}
};
return (
<div className="container mx-auto px-4 py-8">
<div className="mb-8">
<h1 className="text-3xl font-bold text-white mb-2">Two-Factor Authentication</h1>
<p className="text-gray-400">Add an extra layer of security to your account</p>
</div>
<Card className="bg-gray-800 border-gray-700 max-w-2xl">
<CardHeader>
<div className="flex items-center gap-3">
<Shield className="h-6 w-6 text-orange-500" />
<div>
<CardTitle className="text-white">2FA Status</CardTitle>
<CardDescription className="text-gray-400">
{isEnabled ? 'Two-factor authentication is enabled' : 'Two-factor authentication is disabled'}
</CardDescription>
</div>
</div>
</CardHeader>
<CardContent>
{!isEnabled && !qrCode && (
<div>
<p className="text-gray-300 mb-4">
Enable two-factor authentication to protect your account with an additional security layer.
</p>
<button
onClick={enable2FA}
className="px-6 py-3 bg-orange-500 text-white rounded-lg hover:bg-orange-600 transition-colors"
>
Enable 2FA
</button>
</div>
)}
{qrCode && secret && (
<div className="space-y-4">
<div>
<p className="text-gray-300 mb-2">Scan this QR code with your authenticator app:</p>
<div className="flex justify-center p-4 bg-white rounded">
<img src={qrCode} alt="2FA QR Code" className="w-48 h-48" />
</div>
</div>
<div>
<p className="text-gray-300 mb-2">Or enter this secret manually:</p>
<code className="block p-3 bg-gray-900 rounded text-white font-mono text-sm break-all">
{secret}
</code>
</div>
<div>
<label className="block text-sm text-gray-300 mb-2">
Enter verification code from your app:
</label>
<input
type="text"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
placeholder="000000"
maxLength={6}
className="w-full px-4 py-2 bg-gray-900 border border-gray-700 rounded text-white"
/>
</div>
<button
onClick={verifyAndEnable}
disabled={verificationCode.length !== 6 || isVerifying}
className="px-6 py-3 bg-orange-500 text-white rounded-lg hover:bg-orange-600 transition-colors disabled:opacity-50"
>
{isVerifying ? 'Verifying...' : 'Verify and Enable'}
</button>
</div>
)}
{isEnabled && (
<div className="space-y-4">
<div className="flex items-center gap-2 text-green-400">
<CheckCircle className="h-5 w-5" />
<span>Two-factor authentication is active</span>
</div>
<button
onClick={disable2FA}
className="px-6 py-3 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
>
Disable 2FA
</button>
</div>
)}
</CardContent>
</Card>
</div>
);
}

View File

@@ -0,0 +1,132 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card';
import { Settings, User, Bell, Shield, Key } from 'lucide-react';
export default function SettingsPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Access Denied</h1>
<p className="text-gray-400 mb-6">Please sign in to access settings</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6 text-white">Settings</h1>
<div className="grid gap-6 md:grid-cols-2">
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<User className="h-5 w-5" />
<CardTitle>Profile Settings</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div>
<label className="text-sm font-medium text-gray-300">Email</label>
<p className="text-white">{session?.user?.email || 'Not available'}</p>
</div>
<div>
<label className="text-sm font-medium text-gray-300">Name</label>
<p className="text-white">{session?.user?.name || 'Not available'}</p>
</div>
<button className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Edit Profile
</button>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Bell className="h-5 w-5" />
<CardTitle>Notifications</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<label className="flex items-center gap-2">
<input type="checkbox" className="rounded" />
<span className="text-white">Email notifications</span>
</label>
<label className="flex items-center gap-2">
<input type="checkbox" className="rounded" />
<span className="text-white">Alert notifications</span>
</label>
<label className="flex items-center gap-2">
<input type="checkbox" className="rounded" />
<span className="text-white">Weekly reports</span>
</label>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Shield className="h-5 w-5" />
<CardTitle>Security</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<button className="w-full px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Change Password
</button>
<button className="w-full px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700">
Enable Two-Factor Authentication
</button>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<div className="flex items-center gap-3">
<Key className="h-5 w-5" />
<CardTitle>API Keys</CardTitle>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
<p className="text-sm text-gray-400">Manage your API keys and tokens</p>
<button className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
Create API Key
</button>
</div>
</CardContent>
</Card>
</div>
</div>
);
}

View File

@@ -0,0 +1,38 @@
'use client'
import Link from 'next/link'
import { Shield, Home, Lock } from 'lucide-react'
export default function Unauthorized() {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<Shield className="h-16 w-16 text-yellow-500 mx-auto mb-4" />
<h1 className="text-3xl font-bold text-white mb-4">Access Denied</h1>
<p className="text-gray-400 mb-2">
You don't have permission to access this resource.
</p>
<p className="text-sm text-gray-500 mb-8">
Please contact your administrator if you believe this is an error.
</p>
<div className="flex gap-4 justify-center">
<Link
href="/"
className="flex items-center gap-2 px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
<Home className="h-5 w-5" />
Go Home
</Link>
<button
onClick={() => window.history.back()}
className="flex items-center gap-2 px-6 py-3 bg-gray-600 text-white rounded-lg hover:bg-gray-700 transition-colors"
>
<Lock className="h-5 w-5" />
Go Back
</button>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,29 @@
'use client'
import { useSession } from 'next-auth/react'
export default function VMScaleSetsPage() {
const { data: session, status } = useSession()
if (status === 'loading') {
return <div>Loading...</div>
}
if (status === 'unauthenticated') {
return <div>Please sign in</div>
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6">VM Scale Sets</h1>
<p className="text-muted-foreground mb-4">
Manage auto-scaling VM groups with metrics and scaling policies
</p>
{/* VM Scale Sets UI will be implemented here */}
<div className="border rounded-lg p-4">
<p className="text-sm text-muted-foreground">VM Scale Sets management interface coming soon</p>
</div>
</div>
)
}

View File

@@ -0,0 +1,71 @@
'use client';
import { useSession } from 'next-auth/react';
import { signIn } from 'next-auth/react';
// Well-Architected Framework dashboard - component to be implemented
// import WAFDashboard from '@/components/well-architected/WAFDashboard';
export default function WellArchitectedPage() {
const { data: session, status } = useSession();
if (status === 'loading') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center">
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-blue-600 mx-auto"></div>
<p className="text-gray-400">Loading...</p>
</div>
</div>
);
}
if (status === 'unauthenticated') {
return (
<div className="flex min-h-screen items-center justify-center bg-gray-900">
<div className="text-center max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold text-white mb-4">Access Denied</h1>
<p className="text-gray-400 mb-6">Please sign in to view Well-Architected Framework</p>
<button
onClick={() => signIn()}
className="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Sign In
</button>
</div>
</div>
);
}
return (
<div className="container mx-auto p-6">
<h1 className="text-3xl font-bold mb-6 text-white">Well-Architected Framework</h1>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Security</h2>
<p className="text-gray-400">Security pillar assessment coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Reliability</h2>
<p className="text-gray-400">Reliability pillar assessment coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Performance</h2>
<p className="text-gray-400">Performance pillar assessment coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Cost Optimization</h2>
<p className="text-gray-400">Cost optimization assessment coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Operational Excellence</h2>
<p className="text-gray-400">Operational excellence assessment coming soon</p>
</div>
<div className="border rounded-lg p-4 bg-gray-800">
<h2 className="text-xl font-semibold mb-2 text-white">Sustainability</h2>
<p className="text-gray-400">Sustainability assessment coming soon</p>
</div>
</div>
</div>
);
}