Files
ai-wpa/components/profile/ProfileSettings.tsx
2025-08-30 18:18:57 +05:30

1171 lines
44 KiB
TypeScript

'use client'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import { useAuth } from '@/contexts/AuthContext'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Badge } from '@/components/ui/badge'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { BalanceDisplay, TransactionHistory } from '@/components/balance'
import { Separator } from '@/components/ui/separator'
import {
AlertCircle,
Save,
Plus,
Wallet,
CreditCard,
Download,
Eye,
Server,
FileText,
Activity,
X,
Settings,
Database,
Cloud,
Lock,
LogIn,
User,
} from 'lucide-react'
import { useProfileData } from '@/hooks/useProfileData'
import { Skeleton } from '@/components/ui/skeleton'
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog'
const ProfileUpdateSchema = z.object({
name: z.string().min(1, 'Name is required'),
email: z.string().email('Invalid email format'),
phone: z.string().optional(),
})
const PasswordChangeSchema = z
.object({
currentPassword: z.string().min(1, 'Current password is required'),
newPassword: z.string().min(6, 'Password must be at least 6 characters'),
confirmPassword: z.string().min(6, 'Password confirmation is required'),
})
.refine((data) => data.newPassword === data.confirmPassword, {
message: "Passwords don't match",
path: ['confirmPassword'],
})
type ProfileUpdateData = z.infer<typeof ProfileUpdateSchema>
type PasswordChangeData = z.infer<typeof PasswordChangeSchema>
interface ProfileSettingsProps {
activeTab?: string
onTabChange?: (tab: string) => void
}
export function ProfileSettings({ activeTab = 'profile', onTabChange }: ProfileSettingsProps) {
const { user, logout } = useAuth()
const { profileStats, isLoading } = useProfileData()
const [isUpdating, setIsUpdating] = useState(false)
const [isChangingPassword, setIsChangingPassword] = useState(false)
const [updateMessage, setUpdateMessage] = useState<{
type: 'success' | 'error'
text: string
} | null>(null)
const [passwordMessage, setPasswordMessage] = useState<{
type: 'success' | 'error'
text: string
} | null>(null)
// Add Balance Modal State
const [isBalanceModalOpen, setIsBalanceModalOpen] = useState(false)
const [selectedCurrency, setSelectedCurrency] = useState<'INR' | 'USD'>('INR')
const [selectedAmount, setSelectedAmount] = useState<number | null>(null)
const [customAmount, setCustomAmount] = useState('')
const [balanceError, setBalanceError] = useState('')
const [isProcessingPayment, setIsProcessingPayment] = useState(false)
// Billing Details Modal State
const [isBillingModalOpen, setIsBillingModalOpen] = useState(false)
const [selectedBilling, setSelectedBilling] = useState<any>(null)
const profileForm = useForm<ProfileUpdateData>({
resolver: zodResolver(ProfileUpdateSchema),
defaultValues: {
name: user?.name || '',
email: user?.email || '',
phone: '',
},
})
const passwordForm = useForm<PasswordChangeData>({
resolver: zodResolver(PasswordChangeSchema),
})
const onProfileSubmit = async (data: ProfileUpdateData) => {
try {
setIsUpdating(true)
setUpdateMessage(null)
const response = await fetch('/api/user/profile', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
})
const result = await response.json()
if (result.success) {
setUpdateMessage({ type: 'success', text: 'Profile updated successfully!' })
// Refresh user data
window.location.reload()
} else {
setUpdateMessage({
type: 'error',
text: result.error?.message || 'Failed to update profile',
})
}
} catch (error) {
setUpdateMessage({ type: 'error', text: 'An error occurred while updating your profile' })
} finally {
setIsUpdating(false)
}
}
const onPasswordSubmit = async (data: PasswordChangeData) => {
try {
setIsChangingPassword(true)
setPasswordMessage(null)
const response = await fetch('/api/user/change-password', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
currentPassword: data.currentPassword,
newPassword: data.newPassword,
}),
})
const result = await response.json()
if (result.success) {
setPasswordMessage({ type: 'success', text: 'Password changed successfully!' })
passwordForm.reset()
} else {
setPasswordMessage({
type: 'error',
text: result.error?.message || 'Failed to change password',
})
}
} catch (error) {
setPasswordMessage({ type: 'error', text: 'An error occurred while changing your password' })
} finally {
setIsChangingPassword(false)
}
}
const handleDeleteAccount = async () => {
if (
window.confirm('Are you sure you want to delete your account? This action cannot be undone.')
) {
try {
const response = await fetch('/api/user/delete', {
method: 'DELETE',
})
if (response.ok) {
await logout()
} else {
alert('Failed to delete account. Please try again.')
}
} catch (error) {
alert('An error occurred while deleting your account.')
}
}
}
// Add Balance Modal Functions
const amountOptions = {
INR: [100, 500, 1000, 2000, 5000, 10000],
USD: [2, 10, 20, 50, 100, 200],
}
const handleOpenBalanceModal = () => {
setIsBalanceModalOpen(true)
setSelectedAmount(null)
setCustomAmount('')
setBalanceError('')
}
const handleCloseBalanceModal = () => {
setIsBalanceModalOpen(false)
setSelectedAmount(null)
setCustomAmount('')
setBalanceError('')
}
const handleCurrencyChange = (currency: 'INR' | 'USD') => {
setSelectedCurrency(currency)
setSelectedAmount(null)
setCustomAmount('')
setBalanceError('')
}
const handleAmountSelect = (amount: number) => {
setSelectedAmount(amount)
setCustomAmount(amount.toString())
setBalanceError('')
}
const handleCustomAmountChange = (value: string) => {
// Only allow numbers
const cleanValue = value.replace(/[^0-9]/g, '')
setCustomAmount(cleanValue)
setSelectedAmount(null)
setBalanceError('')
}
const validateAmount = () => {
if (!customAmount) {
setBalanceError('Please enter an amount')
return false
}
const amount = parseFloat(customAmount)
if (isNaN(amount) || amount <= 0) {
setBalanceError('Please enter a valid amount')
return false
}
// Validate amount limits (matching API validation)
const minAmount = selectedCurrency === 'INR' ? 100 : 2
const maxAmount = selectedCurrency === 'INR' ? 100000 : 1500
if (amount < minAmount) {
setBalanceError(`Minimum amount is ${selectedCurrency === 'INR' ? '₹' : '$'}${minAmount}`)
return false
}
if (amount > maxAmount) {
setBalanceError(`Maximum amount is ${selectedCurrency === 'INR' ? '₹' : '$'}${maxAmount}`)
return false
}
setBalanceError('')
return true
}
const handleSubmitBalance = async () => {
if (!validateAmount()) return
setIsProcessingPayment(true)
try {
const amount = parseFloat(customAmount)
// Call Add Balance API
const response = await fetch('/api/balance/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount,
currency: selectedCurrency,
}),
})
const result = await response.json()
if (!response.ok) {
throw new Error(result.message || 'Failed to initiate payment')
}
if (result.success) {
// Close modal before redirecting to payment gateway
handleCloseBalanceModal()
// Create and submit PayU form automatically
const form = document.createElement('form')
form.method = 'POST'
form.action = result.payment_url
// Add all PayU form fields
Object.entries(result.form_data).forEach(([key, value]) => {
const input = document.createElement('input')
input.type = 'hidden'
input.name = key
input.value = value as string
form.appendChild(input)
})
// Append form to body and submit
document.body.appendChild(form)
form.submit()
// Remove form after submission
document.body.removeChild(form)
} else {
throw new Error(result.message || 'Payment initiation failed')
}
} catch (error) {
console.error('Payment error:', error)
const errorMessage =
error instanceof Error ? error.message : 'Failed to initiate payment. Please try again.'
setBalanceError(errorMessage)
} finally {
setIsProcessingPayment(false)
}
}
// Billing Details Modal Functions
const handleViewBilling = (billing: any) => {
setSelectedBilling(billing)
setIsBillingModalOpen(true)
}
const getServiceIcon = (serviceType: string) => {
const iconMap: Record<string, any> = {
hosting: Server,
kubernetes: Cloud,
database: Database,
domain: Server,
vps: Server,
cloud: Cloud,
}
const IconComponent = iconMap[serviceType.toLowerCase()] || Server
return <IconComponent className="w-5 h-5" />
}
const getStatusColor = (status: string) => {
switch (status.toLowerCase()) {
case 'paid':
return 'default'
case 'pending':
return 'secondary'
case 'failed':
return 'destructive'
default:
return 'secondary'
}
}
if (!user) return null
return (
<>
<Tabs value={activeTab} onValueChange={onTabChange} className="space-y-6">
<TabsList className="grid w-full grid-cols-6">
<TabsTrigger value="profile">Profile</TabsTrigger>
<TabsTrigger value="balance">Balance</TabsTrigger>
<TabsTrigger value="billing">Billing</TabsTrigger>
<TabsTrigger value="services">Services</TabsTrigger>
<TabsTrigger value="security">Security</TabsTrigger>
<TabsTrigger value="danger">Danger Zone</TabsTrigger>
</TabsList>
<TabsContent value="profile">
<Card>
<CardHeader>
<CardTitle>Profile Information</CardTitle>
<CardDescription>Update your profile information and email address.</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={profileForm.handleSubmit(onProfileSubmit)} className="space-y-4">
{updateMessage && (
<div
className={`p-3 text-sm rounded-md ${
updateMessage.type === 'success'
? 'text-green-600 bg-green-50 border border-green-200'
: 'text-red-600 bg-red-50 border border-red-200'
}`}
>
{updateMessage.text}
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="name">Full Name</Label>
<Input id="name" {...profileForm.register('name')} disabled={isUpdating} />
{profileForm.formState.errors.name && (
<p className="text-sm text-red-600">
{profileForm.formState.errors.name.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="email">Email Address</Label>
<Input
id="email"
type="email"
{...profileForm.register('email')}
disabled={isUpdating}
/>
{profileForm.formState.errors.email && (
<p className="text-sm text-red-600">
{profileForm.formState.errors.email.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="phone">Phone Number (Optional)</Label>
<Input
id="phone"
type="tel"
placeholder="+1 (555) 123-4567"
{...profileForm.register('phone')}
disabled={isUpdating}
/>
{profileForm.formState.errors.phone && (
<p className="text-sm text-red-600">
{profileForm.formState.errors.phone.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="siliconId">Silicon ID</Label>
<Input
id="siliconId"
value={user.siliconId || ''}
disabled
className="bg-muted"
/>
<p className="text-xs text-muted-foreground">
Your unique identifier in our system
</p>
</div>
</div>
<Button type="submit" disabled={isUpdating}>
<Save className="w-4 h-4 mr-2" />
{isUpdating ? 'Updating...' : 'Update Profile'}
</Button>
</form>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="balance" className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Wallet className="w-5 h-5" />
Account Balance
</CardTitle>
<CardDescription>Manage your account funds and transactions.</CardDescription>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-r from-blue-500 to-purple-600 rounded-lg p-6 text-white mb-6">
<div className="flex items-center justify-between">
<div>
<p className="text-blue-100">Current Balance</p>
{isLoading ? (
<Skeleton className="h-9 w-32 bg-white/20" />
) : (
<div className="flex items-center gap-2">
<Wallet className="w-5 h-5" />
<BalanceDisplay
className="text-3xl font-bold"
variant="default"
showIcon={false}
/>
</div>
// <p className="text-3xl font-bold">
// ₹
// {profileStats?.balance?.toLocaleString('en-IN', {
// minimumFractionDigits: 2,
// }) || '1,250.00'}
// </p>
)}
</div>
<div className="text-right">
<Button
className="bg-white/20 hover:bg-white/30 text-white border-white/30"
onClick={handleOpenBalanceModal}
>
<Plus className="w-4 h-4 mr-2" />
Add Funds
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Activity className="w-5 h-5" />
Transaction History
</CardTitle>
<CardDescription>View all your transactions and balance changes</CardDescription>
</CardHeader>
<CardContent>
<TransactionHistory />
</CardContent>
</Card>
</TabsContent>
<TabsContent value="billing">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<FileText className="w-5 h-5" />
Billing History
</CardTitle>
<CardDescription>
View and manage your billing information and invoices.
</CardDescription>
</div>
<Button>
<Plus className="w-4 h-4 mr-2" />
New Service
</Button>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
{[
{
id: 'INV-001',
service: 'VPS Hosting Plan',
amount: '599.00',
date: 'Dec 15, 2024',
dueDate: 'Dec 15, 2024',
status: 'paid',
serviceType: 'hosting',
billingCycle: 'Monthly',
description: 'VPS hosting service with 4GB RAM, 2 vCPUs, and 80GB SSD storage',
},
{
id: 'INV-002',
service: 'Kubernetes Cluster',
amount: '299.00',
date: 'Dec 5, 2024',
dueDate: 'Dec 5, 2024',
status: 'paid',
serviceType: 'kubernetes',
billingCycle: 'Monthly',
description: 'Managed Kubernetes cluster with 3 nodes and load balancer',
},
{
id: 'INV-003',
service: 'Domain Registration',
amount: '199.00',
date: 'Nov 28, 2024',
dueDate: 'Dec 5, 2024',
status: 'pending',
serviceType: 'domain',
billingCycle: 'Annual',
description: 'Domain registration for .com domain with DNS management',
},
].map((invoice) => (
<div
key={invoice.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-muted rounded-lg flex items-center justify-center">
{getServiceIcon(invoice.serviceType)}
</div>
<div>
<p className="font-medium">{invoice.service}</p>
<p className="text-sm text-muted-foreground">ID: {invoice.id}</p>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-right">
<p className="font-semibold">{invoice.amount}</p>
<p className="text-sm text-muted-foreground">{invoice.date}</p>
</div>
<Badge variant={getStatusColor(invoice.status)}>
{invoice.status.charAt(0).toUpperCase() + invoice.status.slice(1)}
</Badge>
<div className="flex gap-1">
<Button
size="sm"
variant="ghost"
onClick={() => handleViewBilling(invoice)}
title="View Details"
>
<Eye className="w-4 h-4" />
</Button>
{invoice.status === 'pending' && (
<Button size="sm" variant="ghost" title="Pay Now">
<CreditCard className="w-4 h-4" />
</Button>
)}
<Button size="sm" variant="ghost" title="Download PDF">
<Download className="w-4 h-4" />
</Button>
</div>
</div>
</div>
))}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="services">
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="flex items-center gap-2">
<Server className="w-5 h-5" />
Active Services
</CardTitle>
<CardDescription>
Manage your active hosting services and configurations.
</CardDescription>
</div>
<Button>
<Plus className="w-4 h-4 mr-2" />
Add Service
</Button>
</div>
</CardHeader>
<CardContent>
<div className="space-y-4">
{(() => {
const activeServices = [
{
name: 'VPS Hosting Plan',
id: 'VPS-001',
serviceId: 'vps_12345',
billingId: 'bill_67890',
clusterId: null,
startDate: 'Dec 15, 2024',
nextBilling: 'Jan 15, 2025',
status: 'active',
type: 'hosting',
},
{
name: 'Kubernetes Cluster',
id: 'K8S-001',
serviceId: 'k8s_54321',
billingId: 'bill_98765',
clusterId: 'cluster_abc123',
startDate: 'Dec 5, 2024',
nextBilling: 'Jan 5, 2025',
status: 'active',
type: 'kubernetes',
},
{
name: 'Database Service',
id: 'DB-001',
serviceId: 'db_11111',
billingId: 'bill_22222',
clusterId: null,
startDate: 'Nov 20, 2024',
nextBilling: 'Dec 20, 2024',
status: 'active',
type: 'database',
},
]
return activeServices.length > 0 ? (
activeServices.map((service) => (
<div
key={service.id}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-muted rounded-lg flex items-center justify-center">
{getServiceIcon(service.type)}
</div>
<div>
<p className="font-medium">{service.name}</p>
<p className="text-sm text-muted-foreground">
Service ID: {service.serviceId}
</p>
</div>
</div>
<div className="flex items-center gap-4">
<div className="text-right text-sm">
<p>Started: {service.startDate}</p>
<p className="text-muted-foreground">
Next billing: {service.nextBilling}
</p>
</div>
<Badge variant="default">Active</Badge>
<div className="flex gap-1">
<Button
size="sm"
variant="outline"
title="Service Settings"
onClick={() => alert(`Configure ${service.name}`)}
>
<Settings className="w-4 h-4" />
</Button>
{service.type === 'kubernetes' && service.clusterId && (
<Button
size="sm"
variant="outline"
title="Download Kubernetes Config"
onClick={() => {
window.open(
`/api/services/download-kubernetes?cluster_id=${service.clusterId}`,
'_blank'
)
}}
>
<Download className="w-4 h-4" />
</Button>
)}
{service.type === 'hosting' && service.billingId && (
<Button
size="sm"
variant="outline"
title="Download Hosting Config"
onClick={() => {
window.open(
`/api/services/download-hosting-conf?billing_id=${service.billingId}`,
'_blank'
)
}}
>
<Download className="w-4 h-4" />
</Button>
)}
</div>
</div>
</div>
))
) : (
<div className="text-center py-12">
<Server className="w-16 h-16 text-muted-foreground/50 mx-auto mb-4" />
<h3 className="text-lg font-semibold mb-2">No Active Services</h3>
<p className="text-muted-foreground mb-4">
You don't have any active services at the moment
</p>
<Button>
<Plus className="w-4 h-4 mr-2" />
Browse Services
</Button>
</div>
)
})()}
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="security">
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle>Change Password</CardTitle>
<CardDescription>
{user.provider === 'local'
? 'Update your password to keep your account secure.'
: `You're signed in with ${user.provider}. Password changes are not available for OAuth accounts.`}
</CardDescription>
</CardHeader>
<CardContent>
{user.provider === 'local' ? (
<form
onSubmit={passwordForm.handleSubmit(onPasswordSubmit)}
className="space-y-4"
>
{passwordMessage && (
<div
className={`p-3 text-sm rounded-md ${
passwordMessage.type === 'success'
? 'text-green-600 bg-green-50 border border-green-200'
: 'text-red-600 bg-red-50 border border-red-200'
}`}
>
{passwordMessage.text}
</div>
)}
<div className="space-y-2">
<Label htmlFor="currentPassword">Current Password</Label>
<Input
id="currentPassword"
type="password"
{...passwordForm.register('currentPassword')}
disabled={isChangingPassword}
/>
{passwordForm.formState.errors.currentPassword && (
<p className="text-sm text-red-600">
{passwordForm.formState.errors.currentPassword.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="newPassword">New Password</Label>
<Input
id="newPassword"
type="password"
{...passwordForm.register('newPassword')}
disabled={isChangingPassword}
/>
{passwordForm.formState.errors.newPassword && (
<p className="text-sm text-red-600">
{passwordForm.formState.errors.newPassword.message}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword">Confirm New Password</Label>
<Input
id="confirmPassword"
type="password"
{...passwordForm.register('confirmPassword')}
disabled={isChangingPassword}
/>
{passwordForm.formState.errors.confirmPassword && (
<p className="text-sm text-red-600">
{passwordForm.formState.errors.confirmPassword.message}
</p>
)}
</div>
<Button type="submit" disabled={isChangingPassword}>
{isChangingPassword ? 'Changing Password...' : 'Change Password'}
</Button>
</form>
) : (
<div className="text-muted-foreground">
Password management is handled by your OAuth provider ({user.provider}).
</div>
)}
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Activity className="w-5 h-5" />
Security Activity
</CardTitle>
<CardDescription>Recent account activity and login history.</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
{(() => {
const securityActivities = [
{
action: 'Successful login',
date: 'Today at 10:30 AM',
ip: '192.168.1.1',
location: 'New York, US',
icon: 'login',
type: 'success',
},
{
action: 'Profile updated',
date: '2 days ago',
ip: '192.168.1.1',
location: 'New York, US',
icon: 'user',
type: 'info',
},
{
action: 'Password changed',
date: '3 days ago',
ip: '192.168.1.1',
location: 'New York, US',
icon: 'lock',
type: 'success',
},
{
action: 'Failed login attempt',
date: '1 week ago',
ip: '203.0.113.1',
location: 'Unknown',
icon: 'warning',
type: 'warning',
},
{
action: 'Account accessed',
date: '2 weeks ago',
ip: '192.168.1.2',
location: 'Boston, US',
icon: 'login',
type: 'info',
},
{
action: 'Email verification',
date: '3 weeks ago',
ip: '192.168.1.1',
location: 'New York, US',
icon: 'user',
type: 'success',
},
]
return securityActivities.map((activity, index) => (
<div
key={index}
className="flex items-center justify-between p-4 border rounded-lg"
>
<div className="flex items-center gap-3">
<div
className={`w-8 h-8 rounded-full flex items-center justify-center ${
activity.type === 'warning'
? 'bg-red-100'
: activity.type === 'success'
? 'bg-green-100'
: 'bg-muted'
}`}
>
{activity.icon === 'lock' && (
<Lock
className={`w-4 h-4 ${activity.type === 'success' ? 'text-green-600' : ''}`}
/>
)}
{activity.icon === 'login' && (
<LogIn
className={`w-4 h-4 ${activity.type === 'success' ? 'text-green-600' : ''}`}
/>
)}
{activity.icon === 'user' && (
<User
className={`w-4 h-4 ${activity.type === 'success' ? 'text-green-600' : ''}`}
/>
)}
{activity.icon === 'warning' && (
<AlertCircle className="w-4 h-4 text-red-600" />
)}
</div>
<div>
<p className="font-medium">{activity.action}</p>
<p className="text-sm text-muted-foreground">{activity.date}</p>
</div>
</div>
<div className="text-right text-sm text-muted-foreground">
<p>{activity.location}</p>
<p className="text-xs">{activity.ip}</p>
</div>
</div>
))
})()}
</div>
</CardContent>
</Card>
</div>
</TabsContent>
<TabsContent value="danger">
<Card className="border-red-200">
<CardHeader>
<CardTitle className="text-red-600">Danger Zone</CardTitle>
<CardDescription>
These actions are irreversible. Please proceed with caution.
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="flex items-start justify-between p-4 border rounded-md">
<div className="flex-1">
<h4 className="text-sm font-medium">Export Account Data</h4>
<p className="text-sm text-muted-foreground mt-1">
Download all your data in a portable format. This includes your profile
information, billing history, and service configurations.
</p>
</div>
<Button variant="outline" className="ml-4">
<Download className="w-4 h-4 mr-2" />
Export Data
</Button>
</div>
<div className="flex items-start space-x-3 p-4 border border-red-200 rounded-md bg-red-50">
<AlertCircle className="w-5 h-5 text-red-600 mt-0.5" />
<div className="flex-1">
<h4 className="text-sm font-medium text-red-800">Delete Account</h4>
<p className="text-sm text-red-700 mt-1">
Once you delete your account, there is no going back. This will permanently
delete your account and all associated data including active services.
</p>
<Button variant="destructive" className="mt-3" onClick={handleDeleteAccount}>
Delete Account
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
</TabsContent>
</Tabs>
{/* Add Balance Modal */}
<Dialog open={isBalanceModalOpen} onOpenChange={setIsBalanceModalOpen}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="text-2xl">Add Balance</DialogTitle>
<DialogDescription>
Choose an amount or enter a custom amount to add to your balance
</DialogDescription>
{balanceError && <div className="text-sm text-red-600 mt-2">{balanceError}</div>}
</DialogHeader>
<div className="space-y-4">
{/* Currency Selection */}
<div className="flex gap-3">
<Button
type="button"
variant={selectedCurrency === 'INR' ? 'default' : 'outline'}
onClick={() => handleCurrencyChange('INR')}
>
INR (₹)
</Button>
<Button
type="button"
variant={selectedCurrency === 'USD' ? 'default' : 'outline'}
onClick={() => handleCurrencyChange('USD')}
>
USD ($)
</Button>
</div>
{/* Amount Selection */}
<div className="grid grid-cols-3 gap-3">
{amountOptions[selectedCurrency].map((amount) => (
<Button
key={amount}
type="button"
variant={selectedAmount === amount ? 'default' : 'outline'}
onClick={() => handleAmountSelect(amount)}
className="text-sm"
>
{selectedCurrency === 'INR' ? '₹' : '$'}
{amount}
</Button>
))}
</div>
{/* Custom Amount Input */}
<div className="flex items-center gap-2 border rounded-md p-3 bg-muted/20">
<span className="text-sm text-muted-foreground">You've selected</span>
<span className="text-lg font-bold">{selectedCurrency === 'INR' ? '₹' : '$'}</span>
<Input
type="text"
placeholder="Amount"
value={customAmount}
onChange={(e) => handleCustomAmountChange(e.target.value)}
className="text-lg font-bold text-center border-0 bg-transparent p-0 focus:ring-0"
/>
<span className="text-sm text-muted-foreground">to top up your balance.</span>
</div>
{/* Modal Actions */}
<div className="flex gap-3 pt-4">
<Button
type="button"
variant="outline"
onClick={handleCloseBalanceModal}
className="flex-1"
>
Cancel
</Button>
<Button
type="button"
onClick={handleSubmitBalance}
disabled={!customAmount || isProcessingPayment}
className="flex-1"
>
{isProcessingPayment ? 'Processing...' : 'Next'}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
{/* Billing Details Modal */}
<Dialog open={isBillingModalOpen} onOpenChange={setIsBillingModalOpen}>
<DialogContent className="sm:max-w-lg">
<DialogHeader>
<DialogTitle className="text-xl">Billing Details</DialogTitle>
<DialogDescription>Invoice and billing information</DialogDescription>
</DialogHeader>
{selectedBilling && (
<div className="space-y-4">
<div className="flex justify-between items-center">
<h4 className="text-lg font-semibold">Invoice #{selectedBilling.id}</h4>
<Badge variant={getStatusColor(selectedBilling.status)}>
{selectedBilling.status.charAt(0).toUpperCase() + selectedBilling.status.slice(1)}
</Badge>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-muted-foreground">Date Created</p>
<p className="font-medium">{selectedBilling.date}</p>
</div>
<div>
<p className="text-sm text-muted-foreground">Due Date</p>
<p className="font-medium">{selectedBilling.dueDate}</p>
</div>
</div>
<Separator />
<div>
<p className="text-sm text-muted-foreground">Service</p>
<p className="font-medium">{selectedBilling.service}</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<p className="text-sm text-muted-foreground">Amount</p>
<p className="text-xl font-bold">₹{selectedBilling.amount}</p>
</div>
<div>
<p className="text-sm text-muted-foreground">Billing Cycle</p>
<p className="font-medium">{selectedBilling.billingCycle}</p>
</div>
</div>
{selectedBilling.description && (
<div>
<p className="text-sm text-muted-foreground">Description</p>
<p className="text-sm">{selectedBilling.description}</p>
</div>
)}
{selectedBilling.status === 'pending' && (
<div className="flex gap-3 pt-4">
<Button className="flex-1">
<CreditCard className="w-4 h-4 mr-2" />
Pay Now
</Button>
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Download
</Button>
</div>
)}
{selectedBilling.status === 'paid' && (
<div className="flex justify-center pt-4">
<Button variant="outline">
<Download className="w-4 h-4 mr-2" />
Download PDF
</Button>
</div>
)}
</div>
)}
</DialogContent>
</Dialog>
</>
)
}