initial commit
This commit is contained in:
582
app/services/human-developer/page.tsx
Normal file
582
app/services/human-developer/page.tsx
Normal 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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user