codenuk_frontend_mine/src/components/admin/template-edit-dialog.tsx
2025-09-09 11:23:58 +05:30

320 lines
10 KiB
TypeScript

"use client"
import { useState, useEffect } from 'react'
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Label } from '@/components/ui/label'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
import { Alert, AlertDescription } from '@/components/ui/alert'
import { Loader2, Save, AlertTriangle } from 'lucide-react'
import { adminApi, AdminApiError } from '@/lib/api/admin'
import { AdminTemplate } from '@/types/admin.types'
import { useToast } from '@/components/ui/toast'
interface TemplateEditDialogProps {
template: AdminTemplate
open: boolean
onOpenChange: (open: boolean) => void
onUpdate: (templateId: string, updatedTemplate: AdminTemplate) => void
}
export function TemplateEditDialog({
template,
open,
onOpenChange,
onUpdate
}: TemplateEditDialogProps) {
const { show } = useToast()
const [formData, setFormData] = useState({
title: '',
description: '',
category: '',
type: '',
icon: '',
gradient: '',
border: '',
text: '',
subtext: ''
})
const [loading, setLoading] = useState(false)
const [error, setError] = useState<string | null>(null)
const [conflictInfo, setConflictInfo] = useState<{ title?: string; type?: string } | null>(null)
const categories = [
"Food Delivery",
"E-commerce",
"SaaS Platform",
"Mobile App",
"Dashboard",
"CRM System",
"Learning Platform",
"Healthcare",
"Real Estate",
"Travel",
"Entertainment",
"Finance",
"Social Media",
"Marketplace",
"Other"
]
// Initialize form data when template changes
useEffect(() => {
if (template) {
setFormData({
title: template.title || '',
description: template.description || '',
category: template.category || '',
type: template.type || '',
icon: template.icon || '',
gradient: template.gradient || '',
border: template.border || '',
text: template.text || '',
subtext: template.subtext || ''
})
setError(null)
setConflictInfo(null)
}
}, [template])
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }))
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!formData.title.trim()) {
setError('Template title is required')
return
}
if (!formData.type.trim()) {
setError('Template type is required')
return
}
try {
setLoading(true)
setError(null)
setConflictInfo(null)
// Prepare update data
const updateData = {
title: formData.title.trim(),
description: formData.description.trim() || undefined,
category: formData.category || undefined,
type: formData.type.trim(),
icon: formData.icon.trim() || undefined,
gradient: formData.gradient.trim() || undefined,
border: formData.border.trim() || undefined,
text: formData.text.trim() || undefined,
subtext: formData.subtext.trim() || undefined
}
const newTemplate = await adminApi.createTemplateFromEdit(template.id, updateData)
// Update the template in the parent component with the new template data
onUpdate(template.id, { ...template, ...newTemplate, status: 'pending' })
// Close dialog
onOpenChange(false)
} catch (error) {
if (error instanceof AdminApiError) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: any = (error as any).data || {}
if (data?.existing_template) {
setConflictInfo({ title: data.existing_template.title, type: data.existing_template.type })
}
const message = data?.message || error.message || 'Failed to create template'
setError(message)
show({
title: 'Template creation failed',
description: data?.existing_template
? `${message} — Existing: ${data.existing_template.title} (${data.existing_template.type}).`
: message,
variant: 'error',
})
} else {
const message = error instanceof Error ? error.message : 'Failed to create template'
setError(message)
show({ title: 'Template creation failed', description: message, variant: 'error' })
}
console.error('Error creating template:', error)
} finally {
setLoading(false)
}
}
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-3xl max-h-[90vh] overflow-y-auto">
<DialogHeader>
<DialogTitle>Create New Template from Edit</DialogTitle>
<p className="text-sm text-gray-600">
We&apos;ll help you create a comprehensive template. based on: <strong>{template.title}</strong>
</p>
<p className="text-xs text-gray-500">
The new template will be created with &apos;pending&apos; status for review.
</p>
</DialogHeader>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="type">Template Type *</Label>
<Input
id="type"
placeholder="e.g., multi_restaurant_food_delivery"
value={formData.type}
onChange={(e) => handleInputChange('type', e.target.value)}
required
/>
<p className="text-xs text-gray-500">Unique identifier for the template</p>
</div>
<div className="space-y-2">
<Label htmlFor="title">Title *</Label>
<Input
id="title"
placeholder="e.g., Multi-Restaurant Food Delivery App"
value={formData.title}
onChange={(e) => handleInputChange('title', e.target.value)}
required
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="description">Description *</Label>
<Textarea
id="description"
placeholder="Describe your template and its key features..."
value={formData.description}
onChange={(e) => handleInputChange('description', e.target.value)}
className="min-h-[100px]"
required
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="category">Category *</Label>
<Select value={formData.category} onValueChange={(value) => handleInputChange('category', value)}>
<SelectTrigger>
<SelectValue placeholder="Select a category" />
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category} value={category}>
{category}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="icon">Icon (optional)</Label>
<Input
id="icon"
placeholder="e.g., restaurant, shopping-cart, users"
value={formData.icon}
onChange={(e) => handleInputChange('icon', e.target.value)}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="space-y-2">
<Label htmlFor="gradient">Gradient (optional)</Label>
<Input
id="gradient"
placeholder="e.g., from-orange-400 to-red-500"
value={formData.gradient}
onChange={(e) => handleInputChange('gradient', e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="border">Border (optional)</Label>
<Input
id="border"
placeholder="e.g., border-orange-500"
value={formData.border}
onChange={(e) => handleInputChange('border', e.target.value)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="text">Text Color (optional)</Label>
<Input
id="text"
placeholder="e.g., text-orange-500"
value={formData.text}
onChange={(e) => handleInputChange('text', e.target.value)}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="subtext">Subtext (optional)</Label>
<Input
id="subtext"
placeholder="e.g., Perfect for food delivery startups"
value={formData.subtext}
onChange={(e) => handleInputChange('subtext', e.target.value)}
/>
</div>
{/* Error Display */}
{error && (
<Alert>
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
{error}
{conflictInfo && (
<span className="block mt-1 text-xs text-gray-600">
Existing: {conflictInfo.title} ({conflictInfo.type}). Try a different title.
</span>
)}
</AlertDescription>
</Alert>
)}
{/* Action Buttons */}
<div className="flex justify-end space-x-2 pt-4">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={loading}
>
Cancel
</Button>
<Button
type="submit"
disabled={loading || !formData.title.trim() || !formData.type.trim()}
>
{loading ? (
<>
<Loader2 className="h-4 w-4 animate-spin mr-2" />
Creating...
</>
) : (
<>
<Save className="h-4 w-4 mr-2" />
Create New Template
</>
)}
</Button>
</div>
</form>
</DialogContent>
</Dialog>
)
}