414 lines
19 KiB
TypeScript
414 lines
19 KiB
TypeScript
import React, { useState, useEffect } from "react";
|
|
import { apiPatchFetcher } from "@/lib/fetcher";
|
|
import { withCache } from "@/components/mutual/context/cache/withCache";
|
|
import { Input } from "@/components/mutual/ui/input";
|
|
import { Label } from "@/components/mutual/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/mutual/ui/select";
|
|
import { Textarea } from "@/components/mutual/ui/textarea";
|
|
import { Checkbox } from "@/components/mutual/ui/checkbox";
|
|
import { RadioGroup, RadioGroupItem } from "@/components/mutual/ui/radio-group";
|
|
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/mutual/ui/dropdown-menu";
|
|
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/mutual/ui/form";
|
|
import { Button } from "@/components/mutual/ui/button";
|
|
import { useForm } from "react-hook-form";
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import * as z from "zod";
|
|
import { getCacheData, setCacheData, clearCacheData } from "@/components/mutual/context/cache/context";
|
|
import { useRouter } from "next/navigation";
|
|
|
|
// Define form schema with zod
|
|
const zodSchema = z.object({
|
|
name: z.string().min(2, { message: "Name must be at least 2 characters." }),
|
|
email: z.string().email({ message: "Please enter a valid email address." }),
|
|
description: z.string().min(10, { message: "Description must be at least 10 characters." }),
|
|
category: z.string({ required_error: "Please select a category." }),
|
|
priority: z.string({ required_error: "Please select a priority level." }),
|
|
notifications: z.boolean(),
|
|
terms: z.boolean().refine(val => val === true, { message: "You must accept the terms." }),
|
|
attachments: z.string().optional()
|
|
});
|
|
|
|
// Define the form type
|
|
type FormValues = z.infer<typeof zodSchema>;
|
|
|
|
function UpdateFromBuildComponentBase({
|
|
cacheData,
|
|
cacheLoading,
|
|
cacheError,
|
|
refreshCache,
|
|
updateCache,
|
|
clearCache,
|
|
activePageUrl
|
|
}: {
|
|
cacheData?: { [url: string]: any } | null;
|
|
cacheLoading?: boolean;
|
|
cacheError?: string | null;
|
|
refreshCache?: (url?: string) => Promise<void>;
|
|
updateCache?: (url: string, data: any) => Promise<void>;
|
|
clearCache?: (url: string) => Promise<void>;
|
|
activePageUrl?: string;
|
|
}) {
|
|
const [open, setOpen] = useState<boolean>(false);
|
|
const [cacheLoaded, setCacheLoaded] = useState<boolean>(false);
|
|
const router = useRouter();
|
|
const listUrl = `/panel/${activePageUrl?.replace("/update", "")}`;
|
|
|
|
const form = useForm<FormValues>({
|
|
resolver: zodResolver(zodSchema),
|
|
defaultValues: {
|
|
name: "",
|
|
email: "",
|
|
description: "",
|
|
category: "",
|
|
priority: "",
|
|
notifications: false,
|
|
terms: false,
|
|
attachments: ""
|
|
}
|
|
});
|
|
|
|
// Load cached form values only once when component mounts
|
|
useEffect(() => {
|
|
const fetchCacheDirectly = async () => {
|
|
if (activePageUrl && !cacheLoaded) {
|
|
try {
|
|
|
|
// Directly fetch cache data using the imported function
|
|
const cachedData = await getCacheData(activePageUrl);
|
|
|
|
if (cachedData) {
|
|
const formValues = form.getValues();
|
|
|
|
// Create a merged data object with proper typing
|
|
// Convert boolean values explicitly to handle potential string representations
|
|
const notificationsValue = typeof cachedData.notifications === 'string'
|
|
? cachedData.notifications === 'true'
|
|
: Boolean(cachedData.notifications);
|
|
|
|
const termsValue = typeof cachedData.terms === 'string'
|
|
? cachedData.terms === 'true'
|
|
: Boolean(cachedData.terms);
|
|
const mergedData: FormValues = {
|
|
name: cachedData.name || formValues.name || "",
|
|
email: cachedData.email || formValues.email || "",
|
|
description: cachedData.description || formValues.description || "",
|
|
category: cachedData.category || formValues.category || "",
|
|
priority: cachedData.priority || formValues.priority || "",
|
|
notifications: notificationsValue,
|
|
terms: termsValue,
|
|
attachments: cachedData.attachments || formValues.attachments || ""
|
|
};
|
|
|
|
form.reset(mergedData);
|
|
}
|
|
setCacheLoaded(true);
|
|
if (refreshCache) {
|
|
refreshCache(activePageUrl);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching cache directly:", error);
|
|
}
|
|
}
|
|
};
|
|
|
|
fetchCacheDirectly();
|
|
}, []); // Empty dependency array since we only want to run once on mount
|
|
|
|
// Function to handle input blur events
|
|
const handleFieldBlur = async (fieldName: keyof FormValues, value: any) => {
|
|
// Only update if the value is not empty
|
|
if (value && activePageUrl) {
|
|
try {
|
|
// Get current form values
|
|
const currentValues = form.getValues();
|
|
|
|
// Prepare updated data
|
|
const updatedData = {
|
|
...currentValues,
|
|
[fieldName]: value
|
|
};
|
|
|
|
console.log("Directly updating cache with:", updatedData);
|
|
|
|
// Directly update cache using imported function
|
|
await setCacheData(activePageUrl, updatedData);
|
|
|
|
// Also use the context method if available (for state consistency)
|
|
if (updateCache) {
|
|
updateCache(activePageUrl, updatedData);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error updating cache:", error);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Type-safe submit handler
|
|
const onSubmit = async (data: FormValues) => {
|
|
console.log("Form submitted with data:", data);
|
|
|
|
try {
|
|
// Submit form data to API using PATCH for updates
|
|
const response = await apiPatchFetcher<any>({ url: "/api/test/update", isNoCache: true, body: data });
|
|
console.log("API response:", response);
|
|
|
|
// Clear cache on successful submission
|
|
if (activePageUrl) {
|
|
try {
|
|
console.log("Directly clearing cache for URL:", activePageUrl);
|
|
|
|
// Directly clear cache using imported function
|
|
await clearCacheData(activePageUrl);
|
|
|
|
// Also use the context method if available (for state consistency)
|
|
if (clearCache) {
|
|
clearCache(activePageUrl);
|
|
}
|
|
} catch (error) {
|
|
console.error("Error clearing cache:", error);
|
|
}
|
|
}
|
|
|
|
// Reset form with empty values
|
|
const emptyValues: FormValues = {
|
|
name: "",
|
|
email: "",
|
|
description: "",
|
|
category: "",
|
|
priority: "",
|
|
notifications: false,
|
|
terms: false,
|
|
attachments: ""
|
|
};
|
|
form.reset(emptyValues);
|
|
|
|
} catch (error) {
|
|
console.error("Error submitting form:", error);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="w-full max-w-2xl mx-auto p-6 bg-white rounded-lg shadow-md">
|
|
{/* back to list button */}
|
|
<div className="flex justify-end">
|
|
<Button onClick={() => router.push(listUrl)}>Back to List</Button>
|
|
</div>
|
|
|
|
<h2 className="text-2xl font-bold mb-6">Update Form</h2>
|
|
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
|
{/* Input example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Name</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
placeholder="Enter your name"
|
|
{...field}
|
|
onBlur={(e) => { field.onBlur(); handleFieldBlur("name", e.target.value) }}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* Email input example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
type="email"
|
|
placeholder="your@email.com"
|
|
{...field}
|
|
onBlur={(e) => { field.onBlur(); handleFieldBlur("email", e.target.value) }}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* Textarea example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="description"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Description</FormLabel>
|
|
<FormControl>
|
|
<Textarea
|
|
placeholder="Enter a detailed description"
|
|
className="min-h-[100px]"
|
|
{...field}
|
|
onBlur={(e) => { field.onBlur(); handleFieldBlur("description", e.target.value) }}
|
|
/>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* Select example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="category"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Category</FormLabel>
|
|
<FormControl>
|
|
<Select
|
|
onValueChange={(value) => { field.onChange(value); handleFieldBlur("category", value) }}
|
|
value={field.value}
|
|
>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select a category" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
<SelectItem value="general">General</SelectItem>
|
|
<SelectItem value="billing">Billing</SelectItem>
|
|
<SelectItem value="technical">Technical</SelectItem>
|
|
<SelectItem value="other">Other</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* RadioGroup example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="priority"
|
|
render={({ field }) => (
|
|
<FormItem className="space-y-3">
|
|
<FormLabel>Priority Level</FormLabel>
|
|
<FormControl>
|
|
<RadioGroup
|
|
onValueChange={(value) => { field.onChange(value); handleFieldBlur("priority", value) }}
|
|
value={field.value}
|
|
className="flex flex-col space-y-1"
|
|
>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="low" id="priority-low" />
|
|
<Label htmlFor="priority-low">Low</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="medium" id="priority-medium" />
|
|
<Label htmlFor="priority-medium">Medium</Label>
|
|
</div>
|
|
<div className="flex items-center space-x-2">
|
|
<RadioGroupItem value="high" id="priority-high" />
|
|
<Label htmlFor="priority-high">High</Label>
|
|
</div>
|
|
</RadioGroup>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* Checkbox example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="notifications"
|
|
render={({ field }) => (
|
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0 rounded-md p-4 border">
|
|
<FormControl>
|
|
<Checkbox
|
|
checked={Boolean(field.value)}
|
|
onCheckedChange={(checked) => {
|
|
console.log("Notification checkbox changed to:", checked);
|
|
field.onChange(checked);
|
|
handleFieldBlur("notifications", checked);
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<div className="space-y-1 leading-none">
|
|
<FormLabel>Email notifications</FormLabel>
|
|
<p className="text-sm text-gray-500">Receive email notifications when updates occur</p>
|
|
</div>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* Terms checkbox example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="terms"
|
|
render={({ field }) => (
|
|
<FormItem className="flex flex-row items-start space-x-3 space-y-0">
|
|
<FormControl>
|
|
<Checkbox
|
|
checked={Boolean(field.value)}
|
|
onCheckedChange={(checked) => {
|
|
console.log("Terms checkbox changed to:", checked);
|
|
field.onChange(checked);
|
|
handleFieldBlur("terms", checked);
|
|
}}
|
|
/>
|
|
</FormControl>
|
|
<div className="space-y-1 leading-none">
|
|
<FormLabel>I accept the terms and conditions</FormLabel>
|
|
</div>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
{/* DropdownMenu example */}
|
|
<FormField
|
|
control={form.control}
|
|
name="attachments"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Attachments</FormLabel>
|
|
<FormControl>
|
|
<div>
|
|
<DropdownMenu open={open} onOpenChange={(isOpen: boolean) => setOpen(isOpen)}>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="outline" className="w-full justify-start text-left font-normal">
|
|
<span>{field.value || "Select attachment type"}</span>
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent className="w-56">
|
|
<DropdownMenuItem onClick={() => { field.onChange("document"); handleFieldBlur("attachments", "document"); setOpen(false) }}>
|
|
Document
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => { field.onChange("image"); handleFieldBlur("attachments", "image"); setOpen(false) }}>
|
|
Image
|
|
</DropdownMenuItem>
|
|
<DropdownMenuItem onClick={() => { field.onChange("video"); handleFieldBlur("attachments", "video"); setOpen(false) }}>
|
|
Video
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
</div>
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
|
|
<Button type="submit" className="w-full">Update</Button>
|
|
</form>
|
|
</Form>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const UpdateFromBuildComponent = withCache(UpdateFromBuildComponentBase);
|
|
|
|
// Add default export to maintain compatibility with existing imports
|
|
export default withCache(UpdateFromBuildComponentBase);
|