initial commit
This commit is contained in:
182
app/profile/page.tsx
Normal file
182
app/profile/page.tsx
Normal file
@@ -0,0 +1,182 @@
|
||||
'use client'
|
||||
import { useSearchParams, useRouter } from 'next/navigation'
|
||||
import { useEffect, useState, useMemo } from 'react'
|
||||
import { RequireAuth } from '@/components/auth/RequireAuth'
|
||||
import { ProfileCard } from '@/components/profile/ProfileCard'
|
||||
import { ProfileSettings } from '@/components/profile/ProfileSettings'
|
||||
import { Header } from '@/components/header'
|
||||
import { Footer } from '@/components/footer'
|
||||
|
||||
function ProfileContent() {
|
||||
const searchParams = useSearchParams()
|
||||
const router = useRouter()
|
||||
const [activeTab, setActiveTab] = useState('profile')
|
||||
const [paymentResult, setPaymentResult] = useState<{
|
||||
type: 'success' | 'failed'
|
||||
message: string
|
||||
amount?: string
|
||||
txn?: string
|
||||
paymentType?: string
|
||||
} | null>(null)
|
||||
|
||||
const validTabs = useMemo(() => ['profile', 'balance', 'billing', 'services', 'security', 'danger'], [])
|
||||
|
||||
useEffect(() => {
|
||||
const tab = searchParams.get('tab')
|
||||
if (tab && validTabs.includes(tab)) {
|
||||
setActiveTab(tab)
|
||||
} else if (tab && !validTabs.includes(tab)) {
|
||||
// Handle invalid tab parameter by redirecting to default
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
params.delete('tab')
|
||||
router.replace(`/profile${params.toString() ? `?${params.toString()}` : ''}`)
|
||||
}
|
||||
|
||||
// Handle payment result notifications
|
||||
const payment = searchParams.get('payment')
|
||||
const paymentType = searchParams.get('type')
|
||||
const amount = searchParams.get('amount')
|
||||
const txn = searchParams.get('txn')
|
||||
const reason = searchParams.get('reason')
|
||||
const message = searchParams.get('message')
|
||||
|
||||
if (payment === 'success') {
|
||||
setPaymentResult({
|
||||
type: 'success',
|
||||
message: paymentType === 'balance'
|
||||
? `Balance successfully added! ₹${amount ? parseFloat(amount).toLocaleString('en-IN') : '0'} has been credited to your account.`
|
||||
: `Payment successful! Your service payment has been processed.`,
|
||||
amount,
|
||||
txn,
|
||||
paymentType
|
||||
})
|
||||
|
||||
// If it's balance addition, switch to balance tab
|
||||
if (paymentType === 'balance') {
|
||||
setActiveTab('balance')
|
||||
}
|
||||
} else if (payment === 'failed') {
|
||||
const failureMessages: Record<string, string> = {
|
||||
'insufficient-funds': 'Payment failed due to insufficient funds.',
|
||||
'card-declined': 'Your card was declined. Please try a different payment method.',
|
||||
'timeout': 'Payment timed out. Please try again.',
|
||||
'user-cancelled': 'Payment was cancelled.',
|
||||
'invalid-details': 'Invalid payment details. Please check and try again.',
|
||||
'unknown': 'Payment failed due to an unexpected error.'
|
||||
}
|
||||
|
||||
setPaymentResult({
|
||||
type: 'failed',
|
||||
message: message ? decodeURIComponent(message) : (failureMessages[reason || 'unknown'] || 'Payment failed. Please try again.'),
|
||||
amount,
|
||||
txn,
|
||||
paymentType
|
||||
})
|
||||
}
|
||||
|
||||
// Clean up URL parameters after handling payment result
|
||||
if (payment) {
|
||||
setTimeout(() => {
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
params.delete('payment')
|
||||
params.delete('type')
|
||||
params.delete('amount')
|
||||
params.delete('txn')
|
||||
params.delete('reason')
|
||||
params.delete('message')
|
||||
params.delete('warning')
|
||||
|
||||
const queryString = params.toString()
|
||||
router.replace(`/profile${queryString ? `?${queryString}` : ''}`, { scroll: false })
|
||||
}, 100) // Small delay to ensure state is set
|
||||
}
|
||||
}, [searchParams, router, validTabs])
|
||||
|
||||
const handleTabChange = (tab: string) => {
|
||||
if (!validTabs.includes(tab)) return
|
||||
|
||||
setActiveTab(tab)
|
||||
const params = new URLSearchParams(searchParams.toString())
|
||||
|
||||
if (tab === 'profile') {
|
||||
// Remove tab parameter for default tab to keep URLs clean
|
||||
params.delete('tab')
|
||||
} else {
|
||||
params.set('tab', tab)
|
||||
}
|
||||
|
||||
const queryString = params.toString()
|
||||
router.push(`/profile${queryString ? `?${queryString}` : ''}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="container max-w-4xl pt-24 pb-8">
|
||||
<div className="mb-8">
|
||||
<h1 className="text-3xl font-bold">Profile</h1>
|
||||
<p className="text-muted-foreground">Manage your account settings and preferences</p>
|
||||
</div>
|
||||
|
||||
{/* Payment Result Notification */}
|
||||
{paymentResult && (
|
||||
<div className={`mb-6 p-4 rounded-lg border ${
|
||||
paymentResult.type === 'success'
|
||||
? 'bg-green-50 border-green-200 text-green-800 dark:bg-green-900/10 dark:border-green-700 dark:text-green-200'
|
||||
: 'bg-red-50 border-red-200 text-red-800 dark:bg-red-900/10 dark:border-red-700 dark:text-red-200'
|
||||
}`}>
|
||||
<div className="flex items-start">
|
||||
<div className={`mr-3 ${
|
||||
paymentResult.type === 'success' ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'
|
||||
}`}>
|
||||
{paymentResult.type === 'success' ? (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clipRule="evenodd" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clipRule="evenodd" />
|
||||
</svg>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="font-medium">{paymentResult.message}</p>
|
||||
{paymentResult.txn && (
|
||||
<p className="mt-1 text-sm opacity-75">
|
||||
Transaction ID: <code className="font-mono">{paymentResult.txn}</code>
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setPaymentResult(null)}
|
||||
className="ml-3 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="grid gap-6 md:grid-cols-1 lg:grid-cols-3">
|
||||
<div className="lg:col-span-1">
|
||||
<ProfileCard />
|
||||
</div>
|
||||
<div className="lg:col-span-2">
|
||||
<ProfileSettings activeTab={activeTab} onTabChange={handleTabChange} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Header />
|
||||
<RequireAuth>
|
||||
<ProfileContent />
|
||||
</RequireAuth>
|
||||
<Footer />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user