initial commit

This commit is contained in:
Kar k1
2025-08-30 18:18:57 +05:30
commit 7219108342
270 changed files with 70221 additions and 0 deletions

View File

@@ -0,0 +1,719 @@
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Checkbox } from '@/components/ui/checkbox'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import {
Cloud,
Server,
HardDrive,
Shield,
AlertCircle,
Check,
Cpu,
MapPin,
ExternalLink,
Terminal,
} from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
import { checkSufficientBalance, formatCurrency } from '@/lib/balance-service'
interface Plan {
id: string
name: string
price: {
daily: number
monthly: number
}
}
interface OSImage {
id: string
name: string
price?: {
daily: number
monthly: number
}
}
interface DataCenter {
id: string
name: string
}
interface DeploymentResult {
status: string
cloudid?: string
message?: string
password?: string
ipv4?: string
server_id?: string
error?: string
[key: string]: any
}
export default function CloudInstancePage() {
const router = useRouter()
const { user } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const [deploymentResult, setDeploymentResult] = useState<DeploymentResult | null>(null)
const [billingCycle, setBillingCycle] = useState<'daily' | 'monthly'>('monthly')
// Form state
const [formData, setFormData] = useState({
hostname: '',
planId: '',
image: '',
password: '',
dcslug: '',
enableBackup: false,
enablePublicIp: true,
})
// Available plans
const plans: Plan[] = [
{ id: '10027', name: '2 vCPU, 4GB RAM, 80GB SSD', price: { daily: 60, monthly: 1600 } },
{ id: '10028', name: '4 vCPU, 8GB RAM, 160GB SSD', price: { daily: 80, monthly: 2200 } },
{ id: '10029', name: '8 vCPU, 32GB RAM, 480GB SSD', price: { daily: 100, monthly: 2800 } },
{ id: '10030', name: '16 vCPU, 64GB RAM, 960GB SSD', price: { daily: 120, monthly: 3200 } },
]
// Available OS images
const images: OSImage[] = [
{ id: 'almalinux-9.2-x86_64', name: 'Alma Linux 9.2' },
{ id: 'centos-7.9-x86_64', name: 'CentOS 7.9' },
{ id: 'debian-12-x86_64', name: 'Debian 12' },
{ id: 'ubuntu-22.04-x86_64', name: 'Ubuntu 22.04 LTS' },
{ id: 'windows-2022', name: 'Windows Server 2022', price: { daily: 200, monthly: 5000 } },
]
// Data centers
const dataCenters: DataCenter[] = [
{ id: 'inmumbaizone2', name: 'Mumbai, India' },
{ id: 'inbangalore', name: 'Bangalore, India' },
]
// Get user balance from auth context
const userBalance = user?.balance || 0
// Calculate total price
const calculatePrice = () => {
const selectedPlan = plans.find((p) => p.id === formData.planId)
const selectedImage = images.find((i) => i.id === formData.image)
if (!selectedPlan) return 0
let total = selectedPlan.price[billingCycle]
// Add Windows license cost if applicable
if (selectedImage?.price) {
total += selectedImage.price[billingCycle]
}
// Add backup cost (20% of plan price)
if (formData.enableBackup) {
total += selectedPlan.price[billingCycle] * 0.2
}
return total
}
const handleDeploy = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setSuccess('')
setDeploymentResult(null)
if (!user) {
router.push('/auth?redirect=/services/cloud-instance')
return
}
// Validate form
if (
!formData.hostname ||
!formData.planId ||
!formData.image ||
!formData.password ||
!formData.dcslug
) {
setError('All fields are required')
return
}
// Check password strength
if (formData.password.length < 8) {
setError('Password must be at least 8 characters long')
return
}
// Check balance
const totalPrice = calculatePrice()
const balanceCheck = await checkSufficientBalance(totalPrice)
if (!balanceCheck.sufficient) {
setError(
balanceCheck.error ||
`Insufficient balance. You need ${formatCurrency(totalPrice)} but only have ${formatCurrency(balanceCheck.currentBalance || 0)}`
)
return
}
setLoading(true)
try {
const response = await fetch('/api/services/deploy-cloude', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
hostname: formData.hostname,
planid: formData.planId,
image: formData.image,
dclocation: formData.dcslug,
password: formData.password,
cycle: billingCycle,
amount: totalPrice,
backup: formData.enableBackup,
publicip: formData.enablePublicIp,
}),
})
const result: DeploymentResult = await response.json()
if (!response.ok) {
throw new Error(result.message || `Deployment failed with status ${response.status}`)
}
if (result.status === 'success') {
setDeploymentResult(result)
// Create success message with server details
let successMessage = 'Cloud instance deployed successfully!'
if (result.cloudid) {
successMessage += ` Server ID: ${result.cloudid}`
}
if (result.ipv4) {
successMessage += ` | IP: ${result.ipv4}`
}
setSuccess(successMessage)
// Reset form after successful deployment
setFormData({
hostname: '',
planId: '',
image: '',
password: '',
dcslug: '',
enableBackup: false,
enablePublicIp: true,
})
} else {
throw new Error(result.message || 'Deployment failed')
}
} catch (err) {
console.error('Deployment error:', err)
setError(err instanceof Error ? err.message : 'Failed to deploy cloud instance')
} finally {
setLoading(false)
}
}
const resetForm = () => {
setDeploymentResult(null)
setSuccess('')
setError('')
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-4xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Cloud className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">Deploy Cloud Instance</h1>
<p className="text-xl text-muted-foreground">
High-performance cloud servers with instant deployment
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to deploy a cloud instance.{' '}
<a
href="/auth?redirect=/services/cloud-instance"
className="text-primary underline"
>
Click here to login
</a>
</AlertDescription>
</Alert>
)}
{success && (
<Alert className="mb-8 bg-green-50 border-green-200">
<Check className="h-4 w-4 text-green-600" />
<AlertDescription className="text-green-800">
<div className="space-y-3">
<div className="font-semibold">{success}</div>
{deploymentResult?.ipv4 && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mt-3">
<div>
<Label className="text-sm font-medium text-green-700">
Server IP Address
</Label>
<div className="flex items-center mt-1">
<Input
className="font-mono text-sm bg-green-100 border-green-300"
value={deploymentResult.ipv4}
readOnly
/>
<Button
variant="outline"
size="sm"
className="ml-2"
onClick={() => {
navigator.clipboard.writeText(deploymentResult.ipv4!)
// You can add a toast notification here
}}
>
<ExternalLink className="h-4 w-4" />
</Button>
</div>
</div>
<div>
<Label className="text-sm font-medium text-green-700">Server ID</Label>
<p className="text-sm font-mono bg-green-100 p-2 rounded border border-green-300 mt-1">
{deploymentResult.cloudid || deploymentResult.server_id}
</p>
</div>
</div>
)}
{deploymentResult?.password && (
<div className="mt-3">
<Label className="text-sm font-medium text-green-700">Root Password</Label>
<div className="flex items-center mt-1">
<Input
type="password"
className="font-mono text-sm bg-green-100 border-green-300"
value={deploymentResult.password}
readOnly
/>
<Button
variant="outline"
size="sm"
className="ml-2"
onClick={() => {
navigator.clipboard.writeText(deploymentResult.password!)
// You can add a toast notification here
}}
>
Copy
</Button>
</div>
<p className="text-xs text-green-600 mt-1">
Please change this password after first login
</p>
</div>
)}
{deploymentResult?.message && (
<div className="mt-3 p-3 bg-green-100 rounded-md">
<p className="text-sm text-green-700">{deploymentResult.message}</p>
</div>
)}
<div className="flex gap-3 mt-4 pt-3 border-t border-green-200">
<Button
onClick={() => router.push('/dashboard/instances')}
className="bg-green-600 hover:bg-green-700"
>
<Server className="w-4 h-4 mr-2" />
View All Instances
</Button>
<Button
variant="outline"
onClick={() => {
// SSH connection button - you can implement this
if (deploymentResult?.ipv4) {
const sshCommand = `ssh root@${deploymentResult.ipv4}`
navigator.clipboard.writeText(sshCommand)
// Show toast notification
}
}}
>
<Terminal className="w-4 h-4 mr-2" />
Copy SSH Command
</Button>
<Button variant="outline" onClick={resetForm}>
Deploy Another
</Button>
</div>
<div className="mt-4 text-xs text-green-600">
<p> Deployment successful! Your server is being provisioned.</p>
{/* <p>📧 Login details will be sent to your email once ready.</p> */}
</div>
</div>
</AlertDescription>
</Alert>
)}
{deploymentResult ? (
<Card>
<CardHeader>
<CardTitle>Deployment Successful!</CardTitle>
<CardDescription>
Your cloud instance has been deployed successfully.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Server ID</Label>
<p className="text-sm font-mono bg-muted p-2 rounded">
{deploymentResult.server_id}
</p>
</div>
<div>
<Label>Status</Label>
<p className="text-sm capitalize">{deploymentResult.status}</p>
</div>
</div>
<div className="flex gap-4">
<Button onClick={() => router.push('/dashboard/instances')}>
View Instances
</Button>
<Button variant="outline" onClick={resetForm}>
Deploy Another
</Button>
</div>
</CardContent>
</Card>
) : (
<form onSubmit={handleDeploy}>
<div className="space-y-8">
{/* Billing Cycle Selection */}
<Card>
<CardHeader>
<CardTitle>Billing Cycle</CardTitle>
<CardDescription>Choose your preferred billing period</CardDescription>
</CardHeader>
<CardContent>
<Tabs
value={billingCycle}
onValueChange={(v) => setBillingCycle(v as 'daily' | 'monthly')}
>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="daily">Daily</TabsTrigger>
<TabsTrigger value="monthly">Monthly (Save 20%)</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
{/* Server Configuration */}
<Card>
<CardHeader>
<CardTitle>Server Configuration</CardTitle>
<CardDescription>Configure your cloud instance specifications</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="hostname">Hostname</Label>
<Input
id="hostname"
placeholder="my-server.example.com"
value={formData.hostname}
onChange={(e) => setFormData({ ...formData, hostname: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="plan">Server Plan</Label>
<Select
value={formData.planId}
onValueChange={(value) => setFormData({ ...formData, planId: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select a plan" />
</SelectTrigger>
<SelectContent>
{plans.map((plan) => (
<SelectItem key={plan.id} value={plan.id}>
<div className="flex items-center justify-between w-full">
<span>{plan.name}</span>
<span className="ml-4 text-primary">
{plan.price[billingCycle]}/
{billingCycle === 'daily' ? 'day' : 'month'}
</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="image">Operating System</Label>
<Select
value={formData.image}
onValueChange={(value) => setFormData({ ...formData, image: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select an OS" />
</SelectTrigger>
<SelectContent>
{images.map((image) => (
<SelectItem key={image.id} value={image.id}>
<div className="flex items-center justify-between w-full">
<span>{image.name}</span>
{image.price && (
<span className="ml-4 text-primary">
+{image.price[billingCycle]}/
{billingCycle === 'daily' ? 'day' : 'month'}
</span>
)}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="datacenter">Data Center</Label>
<Select
value={formData.dcslug}
onValueChange={(value) => setFormData({ ...formData, dcslug: value })}
>
<SelectTrigger>
<SelectValue placeholder="Select location" />
</SelectTrigger>
<SelectContent>
{dataCenters.map((dc) => (
<SelectItem key={dc.id} value={dc.id}>
<div className="flex items-center gap-2">
<MapPin className="w-4 h-4" />
{dc.name}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="password">Root Password</Label>
<Input
id="password"
type="password"
placeholder="Strong password (min 8 characters)"
value={formData.password}
onChange={(e) => setFormData({ ...formData, password: e.target.value })}
required
/>
<p className="text-xs text-muted-foreground mt-1">
Minimum 8 characters required
</p>
</div>
<div className="space-y-3">
<div className="flex items-center space-x-2">
<Checkbox
id="backup"
checked={formData.enableBackup}
onCheckedChange={(checked) =>
setFormData({ ...formData, enableBackup: checked as boolean })
}
/>
<Label htmlFor="backup" className="cursor-pointer">
Enable automatic backups (+20% of plan cost)
</Label>
</div>
<div className="flex items-center space-x-2">
<Checkbox
id="publicip"
checked={formData.enablePublicIp}
onCheckedChange={(checked) =>
setFormData({ ...formData, enablePublicIp: checked as boolean })
}
/>
<Label htmlFor="publicip" className="cursor-pointer">
Enable public IP address
</Label>
</div>
</div>
</CardContent>
</Card>
{/* Pricing Summary */}
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">Base Plan</span>
<span>
{plans.find((p) => p.id === formData.planId)?.price[billingCycle] || 0}
</span>
</div>
{images.find((i) => i.id === formData.image)?.price && (
<div className="flex justify-between">
<span className="text-muted-foreground">Windows License</span>
<span>
{images.find((i) => i.id === formData.image)?.price?.[billingCycle] ||
0}
</span>
</div>
)}
{formData.enableBackup && (
<div className="flex justify-between">
<span className="text-muted-foreground">Backup Service</span>
<span>
{(plans.find((p) => p.id === formData.planId)?.price[billingCycle] ||
0) * 0.2}
</span>
</div>
)}
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Total</span>
<span className="text-primary">
{calculatePrice()}/{billingCycle === 'daily' ? 'day' : 'month'}
</span>
</div>
</div>
<div className="pt-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Your Balance</span>
<span
className={
userBalance >= calculatePrice() ? 'text-green-600' : 'text-red-600'
}
>
{userBalance.toFixed(2)}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Deploy Button */}
<Button type="submit" className="w-full" size="lg" disabled={loading || !user}>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Deploying...
</>
) : (
'Deploy Cloud Instance'
)}
</Button>
</div>
</form>
)}
{/* Features */}
{!deploymentResult && (
<div className="mt-16 grid md:grid-cols-3 gap-8">
<Card>
<CardHeader>
<Server className="w-8 h-8 text-primary mb-2" />
<CardTitle>High Performance</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Enterprise-grade hardware with NVMe SSD storage
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<Shield className="w-8 h-8 text-primary mb-2" />
<CardTitle>Secure & Reliable</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
DDoS protection and 99.99% uptime SLA
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<HardDrive className="w-8 h-8 text-primary mb-2" />
<CardTitle>Instant Deployment</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground">
Your server ready in less than 60 seconds
</p>
</CardContent>
</Card>
</div>
)}
</div>
</div>
<Footer />
</div>
)
}
// jRIfuQspDJlWdgXxLmqVFSUthwBOkezibPCyoTvHYcEANraGKMnZ
// {
// "status": "success",
// "cloudid": "1645972",
// "message": "Cloud Server deploy in process and as soon it get ready to use system will send you login detail over the email.",
// "password": "00000000",
// "ipv4": "103.189.89.32"
// }

View File

@@ -0,0 +1,426 @@
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Server, Shield, HardDrive, Globe, Mail, AlertCircle, Check, Settings } from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
interface HostingPlan {
id: string
controlPanel: string
billingCycle: string[]
price: {
monthly: number
yearly: number
}
storage: string
bandwidth: string
domainsAllowed: string
ssl: boolean
support: string
popular?: boolean
}
export default function HostingControlPanelPage() {
const router = useRouter()
const { user } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly')
const [selectedPlan, setSelectedPlan] = useState('')
const [domain, setDomain] = useState('')
// Available hosting plans
const plans: HostingPlan[] = [
{
id: 'plan_001',
controlPanel: 'cPanel',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 500, yearly: 5000 },
storage: '100 GB SSD',
bandwidth: 'Unlimited',
domainsAllowed: 'Unlimited',
ssl: true,
support: '24/7 Priority Support',
popular: true,
},
{
id: 'plan_002',
controlPanel: 'Plesk',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 450, yearly: 4500 },
storage: '80 GB SSD',
bandwidth: 'Unlimited',
domainsAllowed: '50',
ssl: true,
support: '24/7 Support',
},
{
id: 'plan_003',
controlPanel: 'DirectAdmin',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 350, yearly: 3500 },
storage: '60 GB SSD',
bandwidth: '2TB/month',
domainsAllowed: '25',
ssl: true,
support: 'Email & Chat',
},
{
id: 'plan_004',
controlPanel: 'HestiaCP',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 199, yearly: 1999 },
storage: '50 GB SSD',
bandwidth: 'Unlimited',
domainsAllowed: 'Unlimited',
ssl: true,
support: 'Email & Chat',
},
{
id: 'plan_005',
controlPanel: 'Webmin',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 149, yearly: 1499 },
storage: '40 GB SSD',
bandwidth: 'Unlimited',
domainsAllowed: '10',
ssl: true,
support: 'Email Only',
},
{
id: 'plan_006',
controlPanel: 'VestaCP',
billingCycle: ['monthly', 'yearly'],
price: { monthly: 129, yearly: 1299 },
storage: '30 GB SSD',
bandwidth: '500 GB/month',
domainsAllowed: '5',
ssl: true,
support: 'Email Only',
},
]
// Mock user balance
const userBalance = 10000
const handlePurchase = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
if (!user) {
router.push('/auth?redirect=/services/hosting-control-panel')
return
}
// Validate form
if (!domain || !selectedPlan) {
setError('Please fill in all required fields')
return
}
// Validate domain format
const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/
if (!domainRegex.test(domain)) {
setError('Please enter a valid domain name')
return
}
// Get selected plan price
const plan = plans.find(p => p.id === selectedPlan)
if (!plan) {
setError('Please select a valid plan')
return
}
const totalPrice = plan.price[billingCycle]
// Check balance
if (userBalance < totalPrice) {
setError(`Insufficient balance. You need ₹${totalPrice.toFixed(2)} but only have ₹${userBalance.toFixed(2)}`)
return
}
setLoading(true)
// Simulate API call
setTimeout(() => {
setLoading(false)
alert('Hosting package purchased successfully! (This is a demo - no actual purchase)')
}, 2000)
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-6xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Settings className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">Hosting Control Panels</h1>
<p className="text-xl text-muted-foreground">
Professional web hosting with your preferred control panel
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to purchase hosting.{' '}
<a href="/auth?redirect=/services/hosting-control-panel" className="text-primary underline">
Click here to login
</a>
</AlertDescription>
</Alert>
)}
<form onSubmit={handlePurchase}>
<div className="space-y-8">
{/* Billing Cycle Selection */}
<Card>
<CardHeader>
<CardTitle>Billing Cycle</CardTitle>
<CardDescription>Choose your preferred billing period</CardDescription>
</CardHeader>
<CardContent>
<Tabs value={billingCycle} onValueChange={(v) => setBillingCycle(v as 'monthly' | 'yearly')}>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="monthly">Monthly</TabsTrigger>
<TabsTrigger value="yearly">Yearly (Save 17%)</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
{/* Available Plans */}
<div>
<h2 className="text-2xl font-semibold mb-6">Choose Your Control Panel</h2>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{plans.map((plan) => (
<Card
key={plan.id}
className={`relative cursor-pointer transition-all ${
selectedPlan === plan.id ? 'ring-2 ring-primary' : ''
} ${plan.popular ? 'border-primary' : ''}`}
onClick={() => setSelectedPlan(plan.id)}
>
{plan.popular && (
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
<span className="bg-gradient-to-r from-blue-500 to-purple-600 text-white px-3 py-1 rounded-full text-xs font-semibold">
Most Popular
</span>
</div>
)}
<CardHeader>
<CardTitle className="text-xl">{plan.controlPanel}</CardTitle>
<div className="text-2xl font-bold text-primary">
{plan.price[billingCycle]}
<span className="text-sm font-normal text-muted-foreground">
/{billingCycle === 'monthly' ? 'month' : 'year'}
</span>
</div>
</CardHeader>
<CardContent className="space-y-3">
<div className="space-y-2">
<div className="flex items-center gap-2">
<HardDrive className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{plan.storage}</span>
</div>
<div className="flex items-center gap-2">
<Globe className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{plan.bandwidth} Bandwidth</span>
</div>
<div className="flex items-center gap-2">
<Server className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{plan.domainsAllowed} Domains</span>
</div>
{plan.ssl && (
<div className="flex items-center gap-2">
<Shield className="w-4 h-4 text-green-500" />
<span className="text-sm">Free SSL Certificate</span>
</div>
)}
<div className="flex items-center gap-2">
<Mail className="w-4 h-4 text-muted-foreground" />
<span className="text-sm">{plan.support}</span>
</div>
</div>
<RadioGroup value={selectedPlan}>
<div className="flex items-center justify-center pt-2">
<RadioGroupItem value={plan.id} id={plan.id} />
<Label htmlFor={plan.id} className="ml-2 cursor-pointer">
Select this plan
</Label>
</div>
</RadioGroup>
</CardContent>
</Card>
))}
</div>
</div>
{/* Domain Configuration */}
<Card>
<CardHeader>
<CardTitle>Domain Configuration</CardTitle>
<CardDescription>Enter the primary domain for your hosting account</CardDescription>
</CardHeader>
<CardContent>
<div>
<Label htmlFor="domain">Primary Domain</Label>
<Input
id="domain"
type="text"
placeholder="example.com"
value={domain}
onChange={(e) => setDomain(e.target.value)}
required
/>
<p className="text-xs text-muted-foreground mt-1">
You can add more domains later from your control panel
</p>
</div>
</CardContent>
</Card>
{/* Order Summary */}
{selectedPlan && (
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">Control Panel</span>
<span>{plans.find(p => p.id === selectedPlan)?.controlPanel}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Billing Cycle</span>
<span className="capitalize">{billingCycle}</span>
</div>
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Total</span>
<span className="text-primary">
{plans.find(p => p.id === selectedPlan)?.price[billingCycle]}
</span>
</div>
</div>
<div className="pt-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Your Balance</span>
<span className={userBalance >= (plans.find(p => p.id === selectedPlan)?.price[billingCycle] || 0) ? 'text-green-600' : 'text-red-600'}>
{userBalance.toFixed(2)}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Purchase Button */}
<Button
type="submit"
className="w-full"
size="lg"
disabled={loading || !user || !selectedPlan}
>
{loading ? 'Processing...' : 'Purchase Hosting Package'}
</Button>
</div>
</form>
{/* Features Grid */}
<div className="mt-16">
<h3 className="text-2xl font-semibold mb-8 text-center">All Plans Include</h3>
<div className="grid md:grid-cols-4 gap-6">
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Check className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">One-Click Apps</h4>
<p className="text-sm text-muted-foreground">
Install WordPress, Joomla, and more with one click
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Shield className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Free SSL</h4>
<p className="text-sm text-muted-foreground">
Secure your website with free SSL certificates
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Server className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Daily Backups</h4>
<p className="text-sm text-muted-foreground">
Automatic daily backups with easy restore
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Mail className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Email Accounts</h4>
<p className="text-sm text-muted-foreground">
Create professional email addresses
</p>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
<Footer />
</div>
)
}

View File

@@ -0,0 +1,582 @@
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Textarea } from '@/components/ui/textarea'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Badge } from '@/components/ui/badge'
import {
Clock,
Calendar,
CalendarDays,
Users,
Code,
Briefcase,
CheckCircle,
AlertCircle,
Star,
} from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
import { useToast } from '@/hooks/use-toast'
import { formatCurrency, validateBalance } from '@/lib/balance-service'
interface DeveloperPlan {
id: string
name: string
price: string
value: number
icon: React.ReactNode
popular: boolean
features: string[]
description: string
}
export default function HumanDeveloperPage() {
const router = useRouter()
const { user, balance, refreshBalance } = useAuth()
const { toast } = useToast()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
// Form state
const [selectedPlan, setSelectedPlan] = useState('')
const [requirements, setRequirements] = useState('')
const [contactInfo, setContactInfo] = useState({
name: user?.name || '',
email: user?.email || '',
phone: '',
})
// Available developer plans
const plans: DeveloperPlan[] = [
{
id: 'hourly',
name: 'Hourly',
price: '₹2,000',
value: 2000,
icon: <Clock className="w-5 h-5" />,
popular: false,
description: 'Perfect for quick fixes and consultations',
features: [
'Flexible hours',
'Pay as you go',
'Minimum 2 hours',
'Quick fixes & bug fixes',
'Technical consultation',
],
},
{
id: 'daily',
name: 'Daily',
price: '₹5,000',
value: 5000,
icon: <Calendar className="w-5 h-5" />,
popular: true,
description: 'Best for short-term projects and rapid development',
features: [
'8 hours per day',
'Priority support',
'Daily progress reports',
'Dedicated developer',
'Progress tracking',
],
},
{
id: 'monthly',
name: 'Monthly',
price: '₹1,00,000',
value: 100000,
icon: <CalendarDays className="w-5 h-5" />,
popular: false,
description: 'Complete solution for large projects',
features: [
'Full-time developer (160+ hours)',
'Dedicated project manager',
'Weekly sprint planning',
'Complete project planning',
'Priority feature development',
],
},
]
// Technology expertise areas
const techStack = [
'React & Next.js',
'Node.js',
'Python',
'PHP',
'Laravel',
'Vue.js',
'Angular',
'React Native',
'Flutter',
'WordPress',
'Shopify',
'Database Design',
'API Development',
'DevOps',
'UI/UX Design',
'Mobile Apps',
'E-commerce',
'CMS Development',
]
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setSuccess('')
if (!user) {
router.push('/auth?redirect=/services/human-developer')
return
}
// Validate form
if (!selectedPlan) {
setError('Please select a plan')
return
}
if (!requirements.trim()) {
setError('Please describe your project requirements')
return
}
if (!contactInfo.name || !contactInfo.email) {
setError('Please fill in all contact information')
return
}
const selectedPlanData = plans.find((p) => p.id === selectedPlan)
if (!selectedPlanData) {
setError('Invalid plan selected')
return
}
// Check balance before proceeding
const minimumDeposit = selectedPlanData.value * 0.5
if (!validateBalance(balance || 0, minimumDeposit)) {
setError(
`Insufficient balance. Minimum deposit required: ${formatCurrency(minimumDeposit)}, Available: ${formatCurrency(balance || 0)}`
)
return
}
setLoading(true)
try {
const response = await fetch('/api/services/hire-developer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
planId: selectedPlan,
planName: selectedPlanData.name,
planPrice: selectedPlanData.value,
requirements: requirements.trim(),
contactInfo: {
name: contactInfo.name.trim(),
email: contactInfo.email.trim(),
phone: contactInfo.phone?.trim() || undefined,
},
}),
})
const data = await response.json()
if (!response.ok) {
throw new Error(data.error?.message || 'Failed to submit request')
}
if (data.success) {
setSuccess(data.data.message)
toast({
title: 'Request Submitted Successfully!',
description: `Deposit of ${formatCurrency(minimumDeposit)} has been deducted. Request ID: ${data.data.requestId}`,
})
// Refresh user balance
await refreshBalance()
// Reset form
setSelectedPlan('')
setRequirements('')
setContactInfo({
name: user?.name || '',
email: user?.email || '',
phone: '',
})
} else {
throw new Error(data.error?.message || 'Failed to submit request')
}
} catch (err) {
console.error('Developer hire error:', err)
const errorMessage = err instanceof Error ? err.message : 'Failed to submit request'
setError(errorMessage)
toast({
title: 'Request Failed',
description: errorMessage,
variant: 'destructive',
})
} finally {
setLoading(false)
}
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-6xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Users className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">Hire Expert Developers</h1>
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
Get skilled developers for your projects. From quick fixes to full-scale applications.
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to hire developers.{' '}
<a
href="/auth?redirect=/services/human-developer"
className="text-primary underline"
>
Click here to login
</a>
</AlertDescription>
</Alert>
)}
{success && (
<Alert className="mb-8">
<CheckCircle className="h-4 w-4" />
<AlertDescription>{success}</AlertDescription>
</Alert>
)}
<form onSubmit={handleSubmit}>
<div className="space-y-8">
{/* Plan Selection */}
<div>
<h2 className="text-2xl font-semibold mb-6">Choose Your Plan</h2>
<RadioGroup value={selectedPlan} onValueChange={setSelectedPlan}>
<div className="grid md:grid-cols-3 gap-6">
{plans.map((plan) => (
<div key={plan.id} className="relative">
<RadioGroupItem value={plan.id} id={plan.id} className="peer sr-only" />
<Label
htmlFor={plan.id}
className={`block cursor-pointer transition-all rounded-xl hover:shadow-lg ${
plan.popular ? 'ring-2 ring-primary' : ''
}`}
>
<Card
className={`h-full transition-all ${
selectedPlan === plan.id
? 'border-primary border-2 shadow-lg bg-primary/5 ring-2 ring-primary/20'
: 'hover:border-primary/50'
}`}
>
{plan.popular && (
<div className="absolute -top-3 left-1/2 transform -translate-x-1/2">
<Badge className="bg-gradient-to-r from-blue-500 to-purple-600">
<Star className="w-3 h-3 mr-1" />
Most Popular
</Badge>
</div>
)}
{selectedPlan === plan.id && (
<div className="absolute -top-3 left-4">
<Badge className="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
<CheckCircle className="w-3 h-3 mr-1" />
Selected
</Badge>
</div>
)}
<CardHeader className="text-center pb-4">
<div className="relative">
<div className="w-12 h-12 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center mx-auto mb-3 text-white">
{plan.icon}
</div>
{/* {selectedPlan === plan.id && (
<div className="absolute -top-1 -right-1 w-6 h-6 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full flex items-center justify-center border-2 border-white">
<CheckCircle className="w-4 h-4 text-white" />
</div>
)} */}
</div>
<CardTitle className="text-xl">{plan.name}</CardTitle>
<div className="text-3xl font-bold text-primary">
{plan.price}
<span className="text-sm font-normal text-muted-foreground">
/{plan.id}
</span>
</div>
<CardDescription className="mt-2">{plan.description}</CardDescription>
</CardHeader>
<CardContent>
<ul className="space-y-2">
{plan.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<CheckCircle className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm">{feature}</span>
</li>
))}
</ul>
</CardContent>
</Card>
</Label>
</div>
))}
</div>
</RadioGroup>
</div>
{/* Project Requirements */}
<Card>
<CardHeader>
<CardTitle>Project Requirements</CardTitle>
<CardDescription>
Describe your project in detail to help us match you with the right developer
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="requirements">Project Description *</Label>
<Textarea
id="requirements"
rows={6}
placeholder="Please describe your project requirements, technology stack, timeline, and any specific needs..."
value={requirements}
onChange={(e) => setRequirements(e.target.value)}
required
/>
</div>
{/* Technology Stack */}
<div>
<Label>Our Technology Expertise</Label>
<div className="flex flex-wrap gap-2 mt-2">
{techStack.map((tech, idx) => (
<Badge key={idx} variant="secondary" className="text-xs">
{tech}
</Badge>
))}
</div>
</div>
</CardContent>
</Card>
{/* Contact Information */}
<Card>
<CardHeader>
<CardTitle>Contact Information</CardTitle>
<CardDescription>We'll use this to get in touch with you</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid md:grid-cols-2 gap-4">
<div>
<Label htmlFor="name">Full Name *</Label>
<Input
id="name"
value={contactInfo.name}
onChange={(e) => setContactInfo({ ...contactInfo, name: e.target.value })}
required
/>
</div>
<div>
<Label htmlFor="email">Email Address *</Label>
<Input
id="email"
type="email"
value={contactInfo.email}
onChange={(e) => setContactInfo({ ...contactInfo, email: e.target.value })}
required
/>
</div>
</div>
<div>
<Label htmlFor="phone">Phone Number</Label>
<Input
id="phone"
type="tel"
placeholder="+91 98765 43210"
value={contactInfo.phone}
onChange={(e) => setContactInfo({ ...contactInfo, phone: e.target.value })}
/>
</div>
</CardContent>
</Card>
{/* Balance Check & Order Summary */}
{selectedPlan && (
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">Plan Selected</span>
<span>{plans.find((p) => p.id === selectedPlan)?.name}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Rate</span>
<span>{plans.find((p) => p.id === selectedPlan)?.price}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Minimum Deposit (50%)</span>
<span className="text-orange-600">
{formatCurrency(
(plans.find((p) => p.id === selectedPlan)?.value || 0) * 0.5
)}
</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Your Balance</span>
<span
className={
balance &&
balance >= (plans.find((p) => p.id === selectedPlan)?.value || 0) * 0.5
? 'text-green-600'
: 'text-red-600'
}
>
{formatCurrency(balance || 0)}
</span>
</div>
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Deposit Required</span>
<span className="text-primary">
{formatCurrency(
(plans.find((p) => p.id === selectedPlan)?.value || 0) * 0.5
)}
</span>
</div>
</div>
</div>
<div className="mt-4 p-3 bg-muted/50 rounded-lg">
<p className="text-xs text-muted-foreground">
* A 50% deposit is required to start. Final pricing will be discussed based
on your specific requirements and project scope.
</p>
</div>
{balance &&
balance < (plans.find((p) => p.id === selectedPlan)?.value || 0) * 0.5 && (
<Alert className="mt-4">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
Insufficient balance. Please{' '}
<a href="/balance" className="text-primary underline">
add funds to your account
</a>{' '}
before proceeding.
</AlertDescription>
</Alert>
)}
</CardContent>
</Card>
)}
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Submit Button */}
<Button type="submit" className="w-full" size="lg" disabled={loading || !user}>
{loading ? 'Submitting Request...' : 'Submit Request & Proceed to Payment'}
</Button>
</div>
</form>
{/* Why Choose Us */}
<div className="mt-16">
<h3 className="text-2xl font-semibold mb-8 text-center">Why Choose Our Developers?</h3>
<div className="grid md:grid-cols-4 gap-6">
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Code className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Expert Skills</h4>
<p className="text-sm text-muted-foreground">
5+ years experience in modern tech stacks
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<CheckCircle className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Quality Assured</h4>
<p className="text-sm text-muted-foreground">
Code reviews and testing included
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Clock className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">On-Time Delivery</h4>
<p className="text-sm text-muted-foreground">
Meeting deadlines is our priority
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<div className="inline-flex items-center justify-center w-12 h-12 bg-primary/10 rounded-full mb-3">
<Briefcase className="w-6 h-6 text-primary" />
</div>
<h4 className="font-semibold mb-2">Full Support</h4>
<p className="text-sm text-muted-foreground">
Post-delivery support and maintenance
</p>
</div>
</CardContent>
</Card>
</div>
</div>
</div>
</div>
<Footer />
</div>
)
}

View File

@@ -0,0 +1,642 @@
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import {
Container,
Server,
HardDrive,
Shield,
AlertCircle,
Check,
Plus,
Minus,
Cpu,
GitBranch,
Download,
ExternalLink,
} from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
interface NodePool {
id: string
name: string
size: string
count: number
autoscale: boolean
minNodes?: number
maxNodes?: number
}
interface ClusterSize {
id: string
name: string
price: {
daily: number
monthly: number
}
}
interface DeploymentResult {
status: string
clusterId?: string
message?: string
id?: string
error?: string
[key: string]: any
}
export default function KubernetesPage() {
const router = useRouter()
const { user } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const [deploymentResult, setDeploymentResult] = useState<DeploymentResult | null>(null)
const [billingCycle, setBillingCycle] = useState<'daily' | 'monthly'>('monthly')
// Form state
const [clusterName, setClusterName] = useState('')
const [k8sVersion, setK8sVersion] = useState('1.30.0-utho')
const [nodePools, setNodePools] = useState<NodePool[]>([
{
id: '1',
name: 'default-pool',
size: '10215',
count: 2,
autoscale: false,
minNodes: 2,
maxNodes: 4,
},
])
// Available cluster sizes
const clusterSizes: ClusterSize[] = [
{ id: '10215', name: '2 vCPU, 4GB RAM', price: { daily: 200, monthly: 4000 } },
{ id: '10216', name: '4 vCPU, 8GB RAM', price: { daily: 300, monthly: 7000 } },
{ id: '10217', name: '8 vCPU, 16GB RAM', price: { daily: 500, monthly: 12000 } },
{ id: '10218', name: '16 vCPU, 32GB RAM', price: { daily: 800, monthly: 20000 } },
]
// Kubernetes versions
const k8sVersions = ['1.30.0-utho', '1.29.0-utho', '1.28.0-utho']
// Mock user balance
const userBalance = 15000
// Calculate total price
const calculatePrice = () => {
let total = 0
nodePools.forEach((pool) => {
const size = clusterSizes.find((s) => s.id === pool.size)
if (size) {
total += size.price[billingCycle] * pool.count
}
})
return total
}
// Add node pool
const addNodePool = () => {
const newPool: NodePool = {
id: Date.now().toString(),
name: `pool-${nodePools.length + 1}`,
size: '10215',
count: 1,
autoscale: false,
minNodes: 1,
maxNodes: 3,
}
setNodePools([...nodePools, newPool])
}
// Remove node pool
const removeNodePool = (id: string) => {
if (nodePools.length > 1) {
setNodePools(nodePools.filter((pool) => pool.id !== id))
}
}
// Update node pool
const updateNodePool = (id: string, field: keyof NodePool, value: any) => {
setNodePools(nodePools.map((pool) => (pool.id === id ? { ...pool, [field]: value } : pool)))
}
// Format node pools for API
const formatNodePools = () => {
return nodePools.map((pool) => ({
name: pool.name,
size: pool.size,
count: pool.count,
autoscale: pool.autoscale,
min_nodes: pool.minNodes || pool.count,
max_nodes: pool.maxNodes || pool.count + 2,
}))
}
const handleDeploy = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
setSuccess('')
setDeploymentResult(null)
if (!user) {
router.push('/auth?redirect=/services/kubernetes')
return
}
// Validate form
if (!clusterName) {
setError('Cluster name is required')
return
}
// Validate cluster name
if (!/^[a-z0-9-]+$/.test(clusterName)) {
setError('Cluster name can only contain lowercase letters, numbers, and hyphens')
return
}
// Check balance
const totalPrice = calculatePrice()
if (userBalance < totalPrice) {
setError(
`Insufficient balance. You need ₹${totalPrice.toFixed(2)} but only have ₹${userBalance.toFixed(2)}`
)
return
}
setLoading(true)
try {
const response = await fetch('/api/services/deploy-kubernetes', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
cluster_label: clusterName,
cluster_version: k8sVersion,
nodepools: formatNodePools(),
cycle: billingCycle,
amount: totalPrice,
}),
})
const result: DeploymentResult = await response.json()
if (!response.ok) {
throw new Error(result.message || `Deployment failed with status ${response.status}`)
}
if (result.status === 'success') {
setDeploymentResult(result)
setSuccess(
`Kubernetes cluster "${clusterName}" deployed successfully! Cluster ID: ${result.clusterId || result.id}`
)
// Reset form after successful deployment
setClusterName('')
setNodePools([
{
id: '1',
name: 'default-pool',
size: '10215',
count: 2,
autoscale: false,
minNodes: 2,
maxNodes: 4,
},
])
} else {
throw new Error(result.message || 'Deployment failed')
}
} catch (err) {
console.error('Deployment error:', err)
setError(err instanceof Error ? err.message : 'Failed to deploy Kubernetes cluster')
} finally {
setLoading(false)
}
}
const downloadKubeconfig = async (clusterId: string) => {
try {
const response = await fetch(`/api/services/deploy-kubernetes?clusterId=${clusterId}`)
if (response.ok) {
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const a = document.createElement('a')
a.style.display = 'none'
a.href = url
a.download = `kubeconfig-${clusterId}.yaml`
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} else {
const errorData = await response.json()
setError(errorData.message || 'Failed to download kubeconfig')
}
} catch (err) {
console.error('Kubeconfig download error:', err)
setError('Failed to download kubeconfig')
}
}
const resetForm = () => {
setDeploymentResult(null)
setSuccess('')
setError('')
}
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-5xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Container className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">Kubernetes Edge</h1>
<p className="text-xl text-muted-foreground">
Managed Kubernetes clusters with auto-scaling and enterprise-grade security
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to deploy a Kubernetes cluster.{' '}
<a href="/auth?redirect=/services/kubernetes" className="text-primary underline">
Click here to login
</a>
</AlertDescription>
</Alert>
)}
{deploymentResult ? (
<Card>
<CardHeader>
<CardTitle>Deployment Successful!</CardTitle>
<CardDescription>
Your Kubernetes cluster has been deployed successfully.
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Cluster ID</Label>
<p className="text-sm font-mono bg-muted p-2 rounded">
{deploymentResult.clusterId || deploymentResult.id}
</p>
</div>
<div>
<Label>Status</Label>
<p className="text-sm capitalize">{deploymentResult.status}</p>
</div>
</div>
<div className="flex gap-4">
{deploymentResult.clusterId && (
<Button onClick={() => downloadKubeconfig(deploymentResult.clusterId!)}>
<Download className="w-4 h-4 mr-2" />
Download Kubeconfig
</Button>
)}
<Button variant="outline" onClick={() => router.push('/dashboard/clusters')}>
View Clusters
</Button>
<Button variant="outline" onClick={resetForm}>
Deploy Another
</Button>
</div>
</CardContent>
</Card>
) : (
<form onSubmit={handleDeploy}>
<div className="space-y-8">
{/* Billing Cycle */}
<Card>
<CardHeader>
<CardTitle>Billing Cycle</CardTitle>
<CardDescription>Choose your preferred billing period</CardDescription>
</CardHeader>
<CardContent>
<Tabs
value={billingCycle}
onValueChange={(v) => setBillingCycle(v as 'daily' | 'monthly')}
>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="daily">Daily</TabsTrigger>
<TabsTrigger value="monthly">Monthly (Save 25%)</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
{/* Cluster Configuration */}
<Card>
<CardHeader>
<CardTitle>Cluster Configuration</CardTitle>
<CardDescription>Configure your Kubernetes cluster settings</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="clusterName">Cluster Name</Label>
<Input
id="clusterName"
placeholder="my-k8s-cluster"
value={clusterName}
onChange={(e) => setClusterName(e.target.value.toLowerCase())}
pattern="^[a-z0-9-]+$"
required
/>
<p className="text-xs text-muted-foreground mt-1">
Only lowercase letters, numbers, and hyphens allowed
</p>
</div>
<div>
<Label htmlFor="k8sVersion">Kubernetes Version</Label>
<Select value={k8sVersion} onValueChange={setK8sVersion}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{k8sVersions.map((version) => (
<SelectItem key={version} value={version}>
<div className="flex items-center gap-2">
<GitBranch className="w-4 h-4" />v{version.replace('-utho', '')}
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="p-3 bg-blue-50 rounded-md border border-blue-200">
<p className="text-sm text-blue-700">
<strong>Location:</strong> Mumbai, India (Fixed)
</p>
<p className="text-xs text-blue-600 mt-1">
All Kubernetes clusters are deployed in our Mumbai datacenter for optimal
performance.
</p>
</div>
</CardContent>
</Card>
{/* Node Pools */}
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Node Pools
<Button type="button" size="sm" onClick={addNodePool} variant="outline">
<Plus className="w-4 h-4 mr-1" />
Add Pool
</Button>
</CardTitle>
<CardDescription>Configure worker node pools for your cluster</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
{nodePools.map((pool, index) => (
<Card key={pool.id}>
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<Input
value={pool.name}
onChange={(e) => updateNodePool(pool.id, 'name', e.target.value)}
className="max-w-xs"
placeholder="Pool name"
required
/>
{nodePools.length > 1 && (
<Button
type="button"
size="sm"
variant="ghost"
onClick={() => removeNodePool(pool.id)}
>
<Minus className="w-4 h-4" />
</Button>
)}
</div>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<Label>Node Size</Label>
<Select
value={pool.size}
onValueChange={(value) => updateNodePool(pool.id, 'size', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{clusterSizes.map((size) => (
<SelectItem key={size.id} value={size.id}>
<div className="flex items-center justify-between w-full">
<span>{size.name}</span>
<span className="ml-4 text-primary text-sm">
{size.price[billingCycle]}/
{billingCycle === 'daily' ? 'day' : 'mo'}
</span>
</div>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div>
<Label>Node Count</Label>
<div className="flex items-center gap-2">
<Button
type="button"
size="sm"
variant="outline"
onClick={() =>
updateNodePool(pool.id, 'count', Math.max(1, pool.count - 1))
}
>
<Minus className="w-4 h-4" />
</Button>
<Input
type="number"
value={pool.count}
onChange={(e) =>
updateNodePool(pool.id, 'count', parseInt(e.target.value) || 1)
}
className="w-20 text-center"
min="1"
max="10"
/>
<Button
type="button"
size="sm"
variant="outline"
onClick={() =>
updateNodePool(pool.id, 'count', Math.min(10, pool.count + 1))
}
>
<Plus className="w-4 h-4" />
</Button>
</div>
</div>
</div>
<div className="text-sm text-muted-foreground">
Pool cost:
{(clusterSizes.find((s) => s.id === pool.size)?.price[billingCycle] ||
0) * pool.count}
/{billingCycle === 'daily' ? 'day' : 'month'}
</div>
</CardContent>
</Card>
))}
</CardContent>
</Card>
{/* Pricing Summary */}
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
{nodePools.map((pool) => {
const size = clusterSizes.find((s) => s.id === pool.size)
const cost = (size?.price[billingCycle] || 0) * pool.count
return (
<div key={pool.id} className="flex justify-between text-sm">
<span className="text-muted-foreground">
{pool.name} ({pool.count} nodes × {size?.name})
</span>
<span>{cost}</span>
</div>
)
})}
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Total</span>
<span className="text-primary">
{calculatePrice()}/{billingCycle === 'daily' ? 'day' : 'month'}
</span>
</div>
</div>
<div className="pt-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Your Balance</span>
<span
className={
userBalance >= calculatePrice() ? 'text-green-600' : 'text-red-600'
}
>
{userBalance.toFixed(2)}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Deploy Button */}
<Button type="submit" className="w-full" size="lg" disabled={loading || !user}>
{loading ? (
<>
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2"></div>
Deploying Cluster...
</>
) : (
'Deploy Kubernetes Cluster'
)}
</Button>
</div>
</form>
)}
{/* Features */}
{!deploymentResult && (
<div className="mt-16 grid md:grid-cols-4 gap-6">
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Cpu className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Auto-scaling</h4>
<p className="text-sm text-muted-foreground">
Automatically scale nodes based on workload
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Shield className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Secure by Default</h4>
<p className="text-sm text-muted-foreground">
RBAC, network policies, and encryption
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Server className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Load Balancing</h4>
<p className="text-sm text-muted-foreground">
Built-in load balancers for your services
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<HardDrive className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Persistent Storage</h4>
<p className="text-sm text-muted-foreground">SSD-backed persistent volumes</p>
</div>
</CardContent>
</Card>
</div>
)}
</div>
</div>
<Footer />
</div>
)
}

202
app/services/page.tsx Normal file
View File

@@ -0,0 +1,202 @@
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { CustomSolutionCTA } from '@/components/ui/custom-solution-cta'
import {
Server,
Database,
Mail,
Shield,
Container,
Cog,
ArrowRight,
Check,
Users,
Cloud,
} from 'lucide-react'
import Image from 'next/image'
import Link from 'next/link'
export default function ServicesPage() {
// Simulating database fetch - in production this would be from API/database
const services = [
{
id: 1,
title: 'Cloud Instance',
description: 'High-performance cloud servers with instant deployment and scalable resources',
price: 'Starting at ₹200/day',
imageUrl: '/assets/images/services/cloud-instance.jpg',
features: ['Instant Deployment', 'Auto Scaling', 'Load Balancing', '99.99% Uptime SLA'],
learnMoreUrl: '/services/cloud-instance',
buyButtonUrl: '/services/cloud-instance',
buyButtonText: 'Deploy Now',
popular: false,
icon: Cloud,
},
{
id: 2,
title: 'Kubernetes',
description: 'Managed Kubernetes clusters with auto-scaling and enterprise-grade security',
price: 'Starting at ₹4000/month',
imageUrl: '/assets/images/services/kubernetes-edge.png',
features: ['Auto-scaling', 'Load Balancing', 'CI/CD Ready', 'Built-in Monitoring'],
learnMoreUrl: '/services/kubernetes',
buyButtonUrl: '/services/kubernetes',
buyButtonText: 'Create Cluster',
popular: true,
icon: Container,
},
{
id: 3,
title: 'Hosting Control Panel',
description: 'Professional web hosting with cPanel, Plesk, or DirectAdmin',
price: 'Starting at ₹500/month',
imageUrl: '/assets/images/services/hosting-cp.jpg',
features: ['cPanel/Plesk/DirectAdmin', 'One-Click Apps', 'SSL Certificates', 'Daily Backups'],
learnMoreUrl: '/services/hosting-control-panel',
buyButtonUrl: '/services/hosting-control-panel',
buyButtonText: 'Get Started',
popular: false,
icon: Cog,
},
{
id: 4,
title: 'VPN Services',
description: 'Secure WireGuard VPN with QR code setup and multiple locations',
price: 'Starting at ₹300/month',
imageUrl: '/assets/images/services/wiregurd-thumb.jpg',
features: ['WireGuard Protocol', 'QR Code Setup', 'Multiple Locations', 'No Logs Policy'],
learnMoreUrl: '/services/vpn',
buyButtonUrl: '/services/vpn',
buyButtonText: 'Setup VPN',
popular: false,
icon: Shield,
},
{
id: 5,
title: 'Human Developer',
description: 'Skilled developers for your custom projects and requirements',
price: 'Custom Quote',
imageUrl: '/assets/images/services/human-developer.jpg',
features: ['Expert Developers', 'Custom Solutions', 'Project Management', '24/7 Support'],
learnMoreUrl: '/services/human-developer',
buyButtonUrl: '/services/human-developer',
buyButtonText: 'Hire Developer',
popular: false,
icon: Users,
},
{
id: 6,
title: 'VPS Hosting',
description: 'Virtual private servers with full root access and dedicated resources',
price: 'Starting at ₹800/month',
imageUrl: '/assets/images/services/vps_thumb.jpg',
features: ['Full Root Access', 'SSD Storage', 'Multiple OS Options', 'IPv6 Support'],
learnMoreUrl: '/services/vps',
buyButtonUrl: '/services/vps',
buyButtonText: 'Order VPS',
popular: false,
icon: Server,
},
]
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
{/* Page Header */}
<div className="text-center mb-12">
<h1 className="text-4xl font-bold mb-4">Our Premium Services</h1>
<p className="text-xl text-muted-foreground max-w-3xl mx-auto">
Tailored solutions to accelerate your business growth
</p>
<div className="w-24 h-1 bg-gradient-to-r from-blue-500 to-purple-600 mx-auto mt-6"></div>
</div>
{/* Services Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{services.map((service) => {
const Icon = service.icon
return (
<Card
key={service.id}
className={`relative overflow-hidden hover:shadow-lg transition-shadow ${service.popular ? 'ring-2 ring-primary' : ''}`}
>
{/* Most Popular Badge */}
{service.popular && (
<div className="absolute top-4 right-4 z-10">
<span className="bg-gradient-to-r from-blue-500 to-purple-600 text-white px-3 py-1 rounded-full text-xs font-semibold">
Most Popular
</span>
</div>
)}
{/* Service Thumbnail */}
<div className="h-48 bg-gradient-to-br from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-900 flex items-center justify-center">
{service.imageUrl ? (
<div className="relative w-full h-full">
{/* Using div with background for now since images aren't available */}
<div className="w-full h-full flex items-center justify-center bg-gradient-to-br from-blue-50 to-purple-50 dark:from-gray-800 dark:to-gray-900">
<Icon className="w-16 h-16 text-primary opacity-20" />
</div>
</div>
) : (
<Icon className="w-16 h-16 text-muted-foreground" />
)}
</div>
{/* Service Content */}
<CardHeader className="text-center">
<CardTitle className="text-xl mb-2">{service.title}</CardTitle>
<div className="text-lg font-semibold text-primary mb-2">{service.price}</div>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground text-center">{service.description}</p>
{/* Features List */}
<ul className="space-y-2">
{service.features.map((feature, idx) => (
<li key={idx} className="flex items-start gap-2">
<Check className="w-4 h-4 text-green-500 mt-0.5 flex-shrink-0" />
<span className="text-sm text-muted-foreground">{feature}</span>
</li>
))}
</ul>
{/* Action Buttons */}
<div className="flex gap-3 pt-4">
{service.learnMoreUrl && (
<Link href={service.learnMoreUrl} className="flex-1">
<Button variant="outline" className="w-full">
Learn More
</Button>
</Link>
)}
{service.buyButtonUrl && service.buyButtonText && (
<Link href={service.buyButtonUrl} className="flex-1">
<Button className="w-full">
{service.buyButtonText}
<ArrowRight className="w-4 h-4 ml-1" />
</Button>
</Link>
)}
</div>
</CardContent>
</Card>
)
})}
</div>
{/* Additional Services Note */}
<CustomSolutionCTA
description="We offer tailored solutions for enterprise clients with specific requirements"
buttonText="Contact Sales"
buttonHref="/contact"
/>
</div>
<Footer />
</div>
)
}

431
app/services/vpn/page.tsx Normal file
View File

@@ -0,0 +1,431 @@
'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Header } from '@/components/header'
import { Footer } from '@/components/footer'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'
import { Label } from '@/components/ui/label'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Shield, MapPin, Download, QrCode, AlertCircle, Lock, Globe, Zap, Eye } from 'lucide-react'
import { useAuth } from '@/contexts/AuthContext'
import { env } from 'process'
interface VPNLocation {
code: string
name: string
flag: string
popular?: boolean
endpoint: string
}
export default function VPNPage() {
const router = useRouter()
const { user } = useAuth()
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [billingCycle, setBillingCycle] = useState<'monthly' | 'yearly'>('monthly')
const [selectedLocation, setSelectedLocation] = useState('')
const [showConfig, setShowConfig] = useState(false)
const [apiEndpoint, setApiEndpoint] = useState('')
const [mockConfig, setMockConfig] = useState()
// VPN pricing
const pricing = {
monthly: 300,
yearly: 4023, // Save 17%
}
// Available VPN locations
const locations: VPNLocation[] = [
// { code: 'india', name: 'Mumbai, India', flag: '🇮🇳', popular: true },
{
code: 'america',
name: 'America, USA',
flag: '🇺🇸',
popular: true,
endpoint: 'https://wireguard-vpn.3027622.siliconpin.com/vpn',
},
{
code: 'europe',
name: 'Europe, UK',
flag: '🇬🇧',
popular: true,
endpoint: 'https://wireguard.vps20.siliconpin.com/vpn',
},
// { code: 'singapore', name: 'Singapore', flag: '🇸🇬' },
// { code: 'japan', name: 'Tokyo, Japan', flag: '🇯🇵' },
// { code: 'canada', name: 'Toronto, Canada', flag: '🇨🇦' },
]
// Mock user balance
const userBalance = 8000
const handleSetupVPN = async (e: React.FormEvent) => {
e.preventDefault()
setError('')
if (!user) {
router.push('/auth?redirect=/services/vpn')
return
}
if (!selectedLocation) {
setError('Please select a VPN location')
return
}
const totalPrice = pricing[billingCycle]
if (userBalance < totalPrice) {
setError(`Insufficient balance. You need ₹${totalPrice} but only have ₹${userBalance}`)
return
}
setLoading(true)
try {
// Generate a unique order ID (you might want to use a proper ID generation)
const orderId = `vpn-${Date.now()}`
// const orderId = `vpn-${Date.now()}-${user.id.slice(0, 8)}`
// Make API request to our backend
const response = await fetch('/api/services/deploy-vpn', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
orderId,
location: selectedLocation,
plan: billingCycle,
}),
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to setup VPN service')
}
const { data } = await response.json()
// Set the config content and show the config section
setMockConfig(data.config)
setShowConfig(true)
} catch (err) {
setError(err.message || 'Failed to setup VPN service')
} finally {
setLoading(false)
}
}
const downloadConfig = () => {
const blob = new Blob([mockConfig], { type: 'application/octet-stream' }) // force binary
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
// quote filename properly
a.setAttribute('download', `siliconpin-vpn-${selectedLocation}.conf`)
a.setAttribute('type', 'application/octet-stream')
document.body.appendChild(a)
a.click()
document.body.removeChild(a)
URL.revokeObjectURL(url)
}
console.log('selectedLocation', selectedLocation)
return (
<div className="min-h-screen bg-background">
<Header />
<div className="container mx-auto px-4 pt-24 pb-16">
<div className="max-w-4xl mx-auto">
{/* Page Header */}
<div className="text-center mb-12">
<div className="inline-flex items-center justify-center w-16 h-16 bg-gradient-to-br from-blue-500 to-purple-600 rounded-full mb-4">
<Shield className="w-8 h-8 text-white" />
</div>
<h1 className="text-4xl font-bold mb-4">WireGuard VPN</h1>
<p className="text-xl text-muted-foreground">
Secure, fast, and private VPN service with WireGuard protocol
</p>
</div>
{!user && (
<Alert className="mb-8">
<AlertCircle className="h-4 w-4" />
<AlertDescription>
You need to be logged in to setup VPN.{' '}
<a href="/auth?redirect=/services/vpn" className="text-primary underline">
Click here to login
</a>
</AlertDescription>
</Alert>
)}
{!showConfig ? (
<form onSubmit={handleSetupVPN}>
<div className="space-y-8">
{/* Billing Cycle */}
<Card>
<CardHeader>
<CardTitle>Billing Plan</CardTitle>
<CardDescription>Choose your preferred billing cycle</CardDescription>
</CardHeader>
<CardContent>
<Tabs
value={billingCycle}
onValueChange={(v) => setBillingCycle(v as 'monthly' | 'yearly')}
>
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="monthly">Monthly - {pricing.monthly}</TabsTrigger>
<TabsTrigger value="yearly">
Yearly - {pricing.yearly} (Save 17%)
</TabsTrigger>
</TabsList>
</Tabs>
</CardContent>
</Card>
{/* Location Selection */}
<Card>
<CardHeader>
<CardTitle>Select VPN Location</CardTitle>
<CardDescription>
Choose the server location for your VPN connection
</CardDescription>
</CardHeader>
<CardContent>
<RadioGroup value={selectedLocation} onValueChange={setSelectedLocation}>
<div className="grid md:grid-cols-2 gap-4">
{locations.map((location) => (
<div key={location.code} className="relative">
<RadioGroupItem
value={location.code}
id={location.code}
className="peer sr-only"
/>
<Label
htmlFor={location.code}
className={`flex items-center justify-between p-4 border-2 rounded-lg cursor-pointer transition-all hover:border-primary/50 peer-checked:border-primary peer-checked:bg-primary/5 ${
location.popular ? 'border-primary/30' : 'border-border'
}`}
>
<div className="flex items-center gap-3">
<span className="text-2xl">{location.flag}</span>
<div>
<div className="font-medium">{location.name}</div>
{location.popular && (
<div className="text-xs text-primary font-medium">
Most Popular
</div>
)}
</div>
</div>
<MapPin className="w-4 h-4 text-muted-foreground" />
</Label>
</div>
))}
</div>
</RadioGroup>
</CardContent>
</Card>
{/* Order Summary */}
<Card>
<CardHeader>
<CardTitle>Order Summary</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-muted-foreground">WireGuard VPN</span>
<span>{pricing[billingCycle]}</span>
</div>
<div className="flex justify-between">
<span className="text-muted-foreground">Billing Cycle</span>
<span className="capitalize">{billingCycle}</span>
</div>
<div className="border-t pt-2">
<div className="flex justify-between font-semibold">
<span>Total</span>
<span className="text-primary">{pricing[billingCycle]}</span>
</div>
</div>
<div className="pt-2">
<div className="flex justify-between text-sm">
<span className="text-muted-foreground">Your Balance</span>
<span
className={
userBalance >= pricing[billingCycle]
? 'text-green-600'
: 'text-red-600'
}
>
{userBalance.toFixed(2)}
</span>
</div>
</div>
</div>
</CardContent>
</Card>
{error && (
<Alert variant="destructive">
<AlertCircle className="h-4 w-4" />
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Setup Button */}
<Button type="submit" className="w-full" size="lg" disabled={loading || !user}>
{loading ? 'Setting up VPN...' : 'Setup VPN Service'}
</Button>
</div>
</form>
) : (
/* VPN Configuration */
<div className="space-y-8">
<Alert>
<Shield className="h-4 w-4" />
<AlertDescription>
Your VPN service has been activated! Download the configuration file below.
</AlertDescription>
</Alert>
<Card>
<CardHeader>
<CardTitle>VPN Configuration</CardTitle>
<CardDescription>
Use this configuration with any WireGuard client
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
{/* Config File */}
<div>
<Label>Configuration File</Label>
<div className="mt-2 p-4 bg-muted rounded-lg">
<pre className="text-xs font-mono whitespace-pre-wrap text-muted-foreground">
{mockConfig}
</pre>
</div>
</div>
{/* Download Buttons */}
<div className="flex flex-col sm:flex-row gap-4">
<Button onClick={downloadConfig} className="flex-1">
<Download className="w-4 h-4 mr-2" />
Download Config File
</Button>
<Button
variant="outline"
className="flex-1"
onClick={() => alert('QR Code functionality would be implemented here')}
>
<QrCode className="w-4 h-4 mr-2" />
Show QR Code
</Button>
</div>
{/* Reset */}
<div className="pt-4 border-t">
<Button
variant="outline"
onClick={() => setShowConfig(false)}
className="w-full"
>
Setup New VPN
</Button>
</div>
</CardContent>
</Card>
{/* Setup Instructions */}
<Card>
<CardHeader>
<CardTitle>Setup Instructions</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<h4 className="font-medium">For Desktop/Laptop:</h4>
<ol className="text-sm space-y-1 list-decimal list-inside text-muted-foreground">
<li>Download and install WireGuard client from wireguard.com</li>
<li>Import the downloaded configuration file</li>
<li>Click "Activate" to connect to the VPN</li>
</ol>
</div>
<div className="space-y-3">
<h4 className="font-medium">For Mobile:</h4>
<ol className="text-sm space-y-1 list-decimal list-inside text-muted-foreground">
<li>Install WireGuard app from App Store/Play Store</li>
<li>Scan the QR code or import the config file</li>
<li>Toggle the connection on</li>
</ol>
</div>
</CardContent>
</Card>
</div>
)}
{/* Features */}
{!showConfig && (
<div className="mt-16 grid md:grid-cols-4 gap-6">
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Zap className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Lightning Fast</h4>
<p className="text-sm text-muted-foreground">
WireGuard protocol for maximum speed
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Eye className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">No Logs Policy</h4>
<p className="text-sm text-muted-foreground">
We don't track or store your activity
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Lock className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Strong Encryption</h4>
<p className="text-sm text-muted-foreground">
ChaCha20 encryption for security
</p>
</div>
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<div className="text-center">
<Globe className="w-8 h-8 text-primary mx-auto mb-3" />
<h4 className="font-semibold mb-2">Multiple Locations</h4>
<p className="text-sm text-muted-foreground">Servers across the globe</p>
</div>
</CardContent>
</Card>
</div>
)}
</div>
</div>
<Footer />
</div>
)
}