first commit

This commit is contained in:
2026-01-17 14:17:42 +05:30
commit 0f194eb9e7
328 changed files with 73544 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
'use client'
import { useState } from 'react'
import { Card, CardContent, 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 { Textarea } from '@/components/ui/textarea'
import { Switch } from '@/components/ui/switch'
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '@/components/ui/dialog'
import { Save, Upload, X, Plus, Edit } from 'lucide-react'
import { toast } from 'sonner'
import Image from 'next/image'
interface Category {
id?: string
name: string
description: string
image: string | null
isActive: boolean
}
interface CategoryFormDialogProps {
category?: Category
onSuccess?: () => void
trigger?: React.ReactNode
mode?: 'create' | 'edit'
}
export function CategoryFormDialog({
category,
onSuccess,
trigger,
mode = category ? 'edit' : 'create'
}: CategoryFormDialogProps) {
const [open, setOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [formData, setFormData] = useState<Category>({
name: '',
description: '',
image: null,
isActive: true,
...category
})
const handleInputChange = (field: keyof Category, value: any) => {
setFormData(prev => ({ ...prev, [field]: value }))
}
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) return
// Mock image upload - replace with actual upload logic
const imageUrl = URL.createObjectURL(file)
setFormData(prev => ({ ...prev, image: imageUrl }))
}
const removeImage = () => {
setFormData(prev => ({ ...prev, image: null }))
}
const resetForm = () => {
setFormData({
name: '',
description: '',
image: null,
isActive: true,
...category
})
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
try {
const url = mode === 'edit' && category?.id
? `/api/admin/categories/${category.id}`
: '/api/admin/categories'
const method = mode === 'edit' ? 'PATCH' : 'POST'
const response = await fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(formData)
})
if (!response.ok) {
const errorData = await response.json()
throw new Error(errorData.error || 'Failed to save category')
}
toast.success(`Category ${mode === 'edit' ? 'updated' : 'created'} successfully`)
setOpen(false)
resetForm()
onSuccess?.()
} catch (error: any) {
toast.error(error.message || `Failed to ${mode === 'edit' ? 'update' : 'create'} category`)
} finally {
setLoading(false)
}
}
const defaultTrigger = mode === 'edit' ? (
<Button variant="ghost" size="sm">
<Edit className="h-4 w-4 mr-2" />
Edit
</Button>
) : (
<Button>
<Plus className="h-4 w-4 mr-2" />
Add Category
</Button>
)
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
{trigger || defaultTrigger}
</DialogTrigger>
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>
{mode === 'edit' ? 'Edit Category' : 'Create New Category'}
</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<Card>
<CardHeader>
<CardTitle>Category Information</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="name">Category Name</Label>
<Input
id="name"
value={formData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
required
/>
</div>
<div>
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
value={formData.description}
onChange={(e) => handleInputChange('description', e.target.value)}
rows={4}
placeholder="Describe this category..."
/>
</div>
<div className="flex items-center justify-between">
<Label htmlFor="isActive">Active Status</Label>
<Switch
id="isActive"
checked={formData.isActive}
onCheckedChange={(checked) => handleInputChange('isActive', checked)}
/>
</div>
<p className="text-sm text-gray-500">
{formData.isActive ? 'Category is visible to customers' : 'Category is hidden from customers'}
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Category Image</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-4">
<div className="flex items-center space-x-2">
<Label htmlFor="image" className="cursor-pointer">
<div className="flex items-center space-x-2 px-4 py-2 border border-dashed border-gray-300 rounded-lg hover:border-gray-400">
<Upload className="h-4 w-4" />
<span>Upload Image</span>
</div>
</Label>
<Input
id="image"
type="file"
accept="image/*"
onChange={handleImageUpload}
className="hidden"
/>
</div>
{formData.image && (
<div className="relative group">
<Image
src={formData.image}
alt="Category image"
width={200}
height={200}
className="rounded-lg object-cover w-full h-48"
/>
<Button
type="button"
variant="destructive"
size="sm"
className="absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={removeImage}
>
<X className="h-3 w-3" />
</Button>
</div>
)}
</div>
</CardContent>
</Card>
</div>
<div className="flex justify-end space-x-3">
<Button
type="button"
variant="outline"
onClick={() => setOpen(false)}
disabled={loading}
>
Cancel
</Button>
<Button type="submit" disabled={loading}>
<Save className="h-4 w-4 mr-2" />
{loading ? 'Saving...' : mode === 'edit' ? 'Update Category' : 'Create Category'}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
)
}