+
+ );
+}
\ No newline at end of file
diff --git a/components/dashboard/promotions/NewPromotionForm.tsx b/components/dashboard/promotions/NewPromotionForm.tsx
new file mode 100644
index 0000000..a661690
--- /dev/null
+++ b/components/dashboard/promotions/NewPromotionForm.tsx
@@ -0,0 +1,340 @@
+'use client';
+
+import { useState } from 'react';
+import { z } from 'zod';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useForm } from 'react-hook-form';
+import { Plus, Save, X, Loader2 } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '@/components/ui/form';
+import { Input } from '@/components/ui/input';
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue
+} from '@/components/ui/select';
+import { Switch } from '@/components/ui/switch';
+import { Textarea } from '@/components/ui/textarea';
+import { toast } from '@/components/ui/use-toast';
+import { PromotionFormData } from '@/lib/types/promotion';
+import { fetchClient } from '@/lib/client-service';
+
+// Form schema validation with Zod
+const formSchema = z.object({
+ code: z.string()
+ .min(3, 'Code must be at least 3 characters')
+ .max(20, 'Code cannot exceed 20 characters')
+ .regex(/^[A-Za-z0-9]+$/, 'Only letters and numbers are allowed'),
+ discountType: z.enum(['percentage', 'fixed']),
+ discountValue: z.coerce.number()
+ .positive('Discount value must be positive')
+ .refine(val => val <= 100, {
+ message: 'Percentage discount cannot exceed 100%',
+ path: ['discountValue'],
+ // Only validate this rule if discount type is percentage
+ params: { type: 'percentage' },
+ }),
+ minOrderAmount: z.coerce.number()
+ .min(0, 'Minimum order amount cannot be negative'),
+ maxUsage: z.coerce.number()
+ .nullable()
+ .optional(),
+ isActive: z.boolean().default(true),
+ startDate: z.string().optional(),
+ endDate: z.string().nullable().optional(),
+ description: z.string().max(200, 'Description cannot exceed 200 characters').optional(),
+});
+
+interface NewPromotionFormProps {
+ onSuccess: () => void;
+ onCancel: () => void;
+}
+
+export default function NewPromotionForm({ onSuccess, onCancel }: NewPromotionFormProps) {
+ const [isSubmitting, setIsSubmitting] = useState(false);
+
+ // Initialize form with default values
+ const form = useForm