NeahNew/components/announcement/announcement-form.tsx
2025-05-04 22:42:54 +02:00

270 lines
8.8 KiB
TypeScript

"use client";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import { Button } from "@/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle
} from "@/components/ui/card";
import { CheckIcon, Loader2, AlertCircle } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { useToast } from "@/components/ui/use-toast";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
// Form schema
const formSchema = z.object({
title: z.string().min(5, { message: "Title must be at least 5 characters" }),
content: z.string().min(10, { message: "Content must be at least 10 characters" }),
targetRoles: z.array(z.string()).min(1, { message: "Select at least one target role" }),
});
interface AnnouncementFormProps {
userRole: string[];
}
export function AnnouncementForm({ userRole }: AnnouncementFormProps) {
const [selectedRoles, setSelectedRoles] = useState<string[]>([]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
const [errorMsg, setErrorMsg] = useState<string | null>(null);
const { toast } = useToast();
// Initialize form
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
title: "",
content: "",
targetRoles: [],
},
});
// Available roles for selection
const availableRoles = [
{ id: "all", name: "All Users" },
{ id: "admin", name: "Administrators" },
{ id: "entrepreneurship", name: "Entrepreneurship" },
{ id: "communication", name: "Communication" },
{ id: "expression", name: "Expression" },
{ id: "coding", name: "Coding" },
{ id: "dataintelligence", name: "Data Intelligence" },
{ id: "mediation", name: "Mediation" },
];
// Handle role selection
const handleRoleToggle = (roleId: string) => {
if (roleId === "all") {
// If "all" is selected, clear other selections
setSelectedRoles(["all"]);
form.setValue("targetRoles", ["all"]);
} else {
// Remove "all" if it was previously selected
const newSelection = selectedRoles.filter(id => id !== "all");
// Toggle the selected role
if (newSelection.includes(roleId)) {
const updatedSelection = newSelection.filter(id => id !== roleId);
setSelectedRoles(updatedSelection);
form.setValue("targetRoles", updatedSelection);
} else {
const updatedSelection = [...newSelection, roleId];
setSelectedRoles(updatedSelection);
form.setValue("targetRoles", updatedSelection);
}
}
};
// Form submission
const onSubmit = async (data: z.infer<typeof formSchema>) => {
setIsSubmitting(true);
setErrorMsg(null);
try {
// Send the data to the API
const response = await fetch('/api/announcements', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
// Parse response, even if it's an error
const result = await response.json();
if (!response.ok) {
let errorMessage = result.error || 'Failed to create announcement';
if (result.details) {
errorMessage += `: ${result.details}`;
}
console.error("API Error:", result);
setErrorMsg(errorMessage);
throw new Error(errorMessage);
}
// Reset form and show success message
form.reset();
setSelectedRoles([]);
setIsSuccess(true);
toast({
title: "Announcement created",
description: "The announcement has been created successfully.",
});
// Hide success message after a delay
setTimeout(() => setIsSuccess(false), 3000);
} catch (error) {
console.error("Error submitting announcement:", error);
toast({
title: "Error",
description: error instanceof Error ? error.message : "Failed to create the announcement. Please try again.",
variant: "destructive",
});
} finally {
setIsSubmitting(false);
}
};
return (
<Card className="bg-white">
<CardHeader className="bg-white text-gray-800">
<CardTitle>Create New Announcement</CardTitle>
<CardDescription>
Create an announcement to be displayed to specific user roles
</CardDescription>
</CardHeader>
<CardContent>
{errorMsg && (
<Alert variant="destructive" className="mb-6">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>{errorMsg}</AlertDescription>
</Alert>
)}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<FormField
control={form.control}
name="title"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-700">Title</FormLabel>
<FormControl>
<Input
placeholder="Enter announcement title"
className="bg-white text-gray-800 border-gray-300"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="content"
render={({ field }) => (
<FormItem>
<FormLabel className="text-gray-700">Content</FormLabel>
<FormControl>
<Textarea
placeholder="Enter announcement content"
rows={5}
className="bg-white text-gray-800 border-gray-300"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="targetRoles"
render={() => (
<FormItem>
<FormLabel className="text-gray-700">Target Audience</FormLabel>
<FormControl>
<div className="p-3 border border-gray-200 rounded-md bg-gray-50">
<p className="text-sm text-gray-500 mb-2">Select which roles can see this announcement:</p>
<div className="flex flex-wrap gap-2">
{availableRoles.map(role => (
<Badge
key={role.id}
variant={selectedRoles.includes(role.id) ? "default" : "outline"}
className={`cursor-pointer px-3 py-1 ${
selectedRoles.includes(role.id)
? "bg-blue-600 hover:bg-blue-700"
: "hover:bg-gray-100"
}`}
onClick={() => handleRoleToggle(role.id)}
>
{role.name}
{selectedRoles.includes(role.id) && (
<CheckIcon className="ml-1 h-3 w-3" />
)}
</Badge>
))}
</div>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-end">
<Button
type="submit"
disabled={isSubmitting || isSuccess}
className="px-4"
>
{isSubmitting ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Submitting...
</>
) : isSuccess ? (
<>
<CheckIcon className="mr-2 h-4 w-4" />
Announcement Created!
</>
) : (
"Create Announcement"
)}
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
);
}