165 lines
5.8 KiB
TypeScript
165 lines
5.8 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
|
|
export interface ProfileFormData {
|
|
username: string
|
|
email: string
|
|
bio?: string
|
|
location?: string
|
|
website?: string
|
|
}
|
|
|
|
export interface ProfileFormProps {
|
|
initialData: ProfileFormData
|
|
onSubmit: (data: ProfileFormData) => void | Promise<void>
|
|
isLoading?: boolean
|
|
}
|
|
|
|
export function ProfileForm({
|
|
initialData,
|
|
onSubmit,
|
|
isLoading = false
|
|
}: ProfileFormProps) {
|
|
const [formData, setFormData] = useState<ProfileFormData>(initialData)
|
|
const [hasChanges, setHasChanges] = useState(false)
|
|
|
|
const handleChange = (field: keyof ProfileFormData, value: string) => {
|
|
setFormData({ ...formData, [field]: value })
|
|
setHasChanges(true)
|
|
}
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault()
|
|
onSubmit(formData)
|
|
}
|
|
|
|
const handleReset = () => {
|
|
setFormData(initialData)
|
|
setHasChanges(false)
|
|
}
|
|
|
|
return (
|
|
<form onSubmit={handleSubmit} className="space-y-6">
|
|
{/* Username */}
|
|
<div>
|
|
<label htmlFor="username" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Username
|
|
</label>
|
|
<input
|
|
type="text"
|
|
id="username"
|
|
value={formData.username}
|
|
onChange={(e) => handleChange('username', e.target.value)}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
required
|
|
/>
|
|
<p className="mt-2 text-xs text-zinc-500">
|
|
This is how other users will see you
|
|
</p>
|
|
</div>
|
|
|
|
{/* Email */}
|
|
<div>
|
|
<label htmlFor="email" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Email
|
|
</label>
|
|
<input
|
|
type="email"
|
|
id="email"
|
|
value={formData.email}
|
|
onChange={(e) => handleChange('email', e.target.value)}
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{/* Bio */}
|
|
<div>
|
|
<label htmlFor="bio" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Bio
|
|
</label>
|
|
<textarea
|
|
id="bio"
|
|
value={formData.bio || ''}
|
|
onChange={(e) => handleChange('bio', e.target.value)}
|
|
rows={4}
|
|
maxLength={160}
|
|
placeholder="Tell us a bit about yourself..."
|
|
className="w-full px-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500 resize-none"
|
|
/>
|
|
<div className="mt-2 flex justify-between text-xs text-zinc-500">
|
|
<span>Brief description for your profile</span>
|
|
<span>{formData.bio?.length || 0}/160</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Location */}
|
|
<div>
|
|
<label htmlFor="location" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Location
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none">
|
|
<svg className="w-5 h-5 text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="text"
|
|
id="location"
|
|
value={formData.location || ''}
|
|
onChange={(e) => handleChange('location', e.target.value)}
|
|
placeholder="City, Country"
|
|
className="w-full pl-12 pr-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Website */}
|
|
<div>
|
|
<label htmlFor="website" className="block text-sm font-medium text-zinc-300 mb-2">
|
|
Website
|
|
</label>
|
|
<div className="relative">
|
|
<div className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none">
|
|
<svg className="w-5 h-5 text-zinc-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9" />
|
|
</svg>
|
|
</div>
|
|
<input
|
|
type="url"
|
|
id="website"
|
|
value={formData.website || ''}
|
|
onChange={(e) => handleChange('website', e.target.value)}
|
|
placeholder="https://yourwebsite.com"
|
|
className="w-full pl-12 pr-4 py-3 bg-zinc-800 border border-zinc-700 rounded-lg text-white placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-purple-500"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Buttons */}
|
|
<div className="flex gap-4 pt-4">
|
|
{hasChanges && (
|
|
<button
|
|
type="button"
|
|
onClick={handleReset}
|
|
disabled={isLoading}
|
|
className="px-6 py-3 bg-zinc-800 hover:bg-zinc-700 disabled:bg-zinc-800 disabled:text-zinc-500 text-white font-medium rounded-lg transition"
|
|
>
|
|
Cancel
|
|
</button>
|
|
)}
|
|
<button
|
|
type="submit"
|
|
disabled={isLoading || !hasChanges}
|
|
className="flex-1 px-6 py-3 bg-purple-500 hover:bg-purple-600 disabled:bg-zinc-700 disabled:text-zinc-500 text-white font-medium rounded-lg transition"
|
|
>
|
|
{isLoading ? 'Saving...' : 'Save Changes'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
)
|
|
}
|