Created
November 26, 2025 15:12
-
-
Save deltaepsilon/ff6c9eb59d90656b4be4810d2b23332d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| "use client"; | |
| import { Drawer } from "@repo/components"; | |
| import { Button } from "@repo/components/ui/button"; | |
| import { Input } from "@repo/components/ui/input"; | |
| import { Label } from "@repo/components/ui/label"; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@repo/components/ui/select"; | |
| import { useSearchParams } from "next/navigation"; | |
| import { useEffect, useState } from "react"; | |
| import { useCreateLocale, useDeleteLocale, useLocale, useUpdateLocale } from "../lib/hooks"; | |
| import { DeleteLocaleDialog } from "./delete-dialog"; | |
| export function LocaleSheet() { | |
| const searchParams = useSearchParams(); | |
| const drawerId = searchParams.get("drawerId"); | |
| const isNewLocale = searchParams.get("newLocale") === "open"; | |
| const isEditDrawer = searchParams.get("drawer") === "open" && Boolean(drawerId); | |
| const isOpen = Boolean(isNewLocale || isEditDrawer); | |
| const isCreate = isNewLocale; | |
| const createMutation = useCreateLocale(); | |
| const updateMutation = useUpdateLocale(); | |
| const deleteMutation = useDeleteLocale(); | |
| const localeQuery = useLocale({ locale: drawerId, enabled: isEditDrawer }); | |
| const [formData, setFormData] = useState({ | |
| locale: "", | |
| flag_icon: "", | |
| english_name: "", | |
| native_name: "", | |
| status: "ACTIVE" as "ACTIVE" | "INACTIVE", | |
| }); | |
| const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); | |
| // Load data when editing or reset for create | |
| useEffect(() => { | |
| if (isEditDrawer && localeQuery.data) { | |
| // Populate form with fetched locale data | |
| // eslint-disable-next-line react-hooks/set-state-in-effect | |
| setFormData({ | |
| locale: localeQuery.data.locale, | |
| flag_icon: localeQuery.data.flag_icon, | |
| english_name: localeQuery.data.english_name, | |
| native_name: localeQuery.data.native_name, | |
| status: localeQuery.data.status, | |
| }); | |
| } else if (isCreate) { | |
| // Reset form for create mode | |
| setFormData({ | |
| locale: "", | |
| flag_icon: "", | |
| english_name: "", | |
| native_name: "", | |
| status: "ACTIVE", | |
| }); | |
| } | |
| }, [isEditDrawer, localeQuery.data, isCreate]); | |
| const handleClose = () => { | |
| const url = new URL(window.location.href); | |
| url.searchParams.delete("drawer"); | |
| url.searchParams.delete("drawerId"); | |
| url.searchParams.delete("newLocale"); | |
| window.history.pushState({}, "", url); | |
| }; | |
| const handleSubmit = (e: React.FormEvent) => { | |
| e.preventDefault(); | |
| if (isCreate) { | |
| createMutation.mutate(formData, { | |
| onSuccess: () => { | |
| handleClose(); | |
| }, | |
| }); | |
| } else if (drawerId) { | |
| const { locale: _locale, ...updateData } = formData; | |
| updateMutation.mutate( | |
| { locale: drawerId, data: updateData }, | |
| { | |
| onSuccess: () => { | |
| handleClose(); | |
| }, | |
| } | |
| ); | |
| } | |
| }; | |
| const handleDelete = () => { | |
| if (drawerId) { | |
| deleteMutation.mutate(drawerId, { | |
| onSuccess: () => { | |
| setDeleteDialogOpen(false); | |
| handleClose(); | |
| }, | |
| }); | |
| } | |
| }; | |
| const isPending = createMutation.isPending || updateMutation.isPending; | |
| const drawerTitle = isCreate ? "New Locale" : "Edit Locale"; | |
| // Show loading state while fetching locale data | |
| const isLoading = isEditDrawer && localeQuery.isLoading; | |
| return ( | |
| <> | |
| <Drawer open={isOpen} onClose={handleClose} position="right" title={drawerTitle}> | |
| <div className="flex flex-col h-full"> | |
| {isLoading ? ( | |
| <div className="space-y-4"> | |
| <div className="h-20 animate-pulse rounded-md bg-muted" /> | |
| <div className="h-20 animate-pulse rounded-md bg-muted" /> | |
| <div className="h-20 animate-pulse rounded-md bg-muted" /> | |
| <div className="h-20 animate-pulse rounded-md bg-muted" /> | |
| </div> | |
| ) : ( | |
| <> | |
| <form onSubmit={handleSubmit} className="flex flex-col gap-4 flex-1 p-6"> | |
| <div className="space-y-2"> | |
| <Label htmlFor="locale"> | |
| Locale Code <span className="text-destructive">*</span> | |
| </Label> | |
| <Input | |
| id="locale" | |
| value={formData.locale} | |
| onChange={(e) => setFormData({ ...formData, locale: e.target.value })} | |
| placeholder="en, es, en-US, fr-FR" | |
| disabled={!isCreate} | |
| required | |
| pattern="^[a-z]{2}(-[A-Z]{2})?$" | |
| title="Must be ISO format (e.g., 'en', 'en-US')" | |
| /> | |
| <p className="text-xs text-muted-foreground"> | |
| ISO format: lowercase language code, optional uppercase country | |
| </p> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="flag_icon"> | |
| Flag Emoji <span className="text-destructive">*</span> | |
| </Label> | |
| <Input | |
| id="flag_icon" | |
| value={formData.flag_icon} | |
| onChange={(e) => setFormData({ ...formData, flag_icon: e.target.value })} | |
| placeholder="🇺🇸" | |
| required | |
| maxLength={4} | |
| /> | |
| <p className="text-xs text-muted-foreground">Paste a flag emoji (e.g., 🇺🇸, 🇲🇽, 🇫🇷)</p> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="english_name"> | |
| English Name <span className="text-destructive">*</span> | |
| </Label> | |
| <Input | |
| id="english_name" | |
| value={formData.english_name} | |
| onChange={(e) => setFormData({ ...formData, english_name: e.target.value })} | |
| placeholder="English" | |
| required | |
| /> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="native_name"> | |
| Native Name <span className="text-destructive">*</span> | |
| </Label> | |
| <Input | |
| id="native_name" | |
| value={formData.native_name} | |
| onChange={(e) => setFormData({ ...formData, native_name: e.target.value })} | |
| placeholder="English, Español, Français" | |
| required | |
| /> | |
| <p className="text-xs text-muted-foreground">Name of the language in its native script</p> | |
| </div> | |
| <div className="space-y-2"> | |
| <Label htmlFor="status"> | |
| Status <span className="text-destructive">*</span> | |
| </Label> | |
| <Select | |
| value={formData.status} | |
| onValueChange={(value: "ACTIVE" | "INACTIVE") => setFormData({ ...formData, status: value })} | |
| > | |
| <SelectTrigger id="status" className="w-full"> | |
| <SelectValue /> | |
| </SelectTrigger> | |
| <SelectContent> | |
| <SelectItem value="ACTIVE">Active</SelectItem> | |
| <SelectItem value="INACTIVE">Inactive</SelectItem> | |
| </SelectContent> | |
| </Select> | |
| </div> | |
| <div className="flex-1" /> | |
| </form> | |
| <div className="flex flex-row-reverse gap-2 px-6 py-4 border-t border-border"> | |
| <Button type="submit" disabled={isPending}> | |
| {isPending ? "Saving..." : "Save"} | |
| </Button> | |
| <Button variant="outline" onClick={handleClose}> | |
| Cancel | |
| </Button> | |
| <div className="flex-1" /> | |
| {!isCreate && ( | |
| <Button | |
| type="button" | |
| variant="destructive" | |
| onClick={() => setDeleteDialogOpen(true)} | |
| disabled={isPending} | |
| > | |
| Delete | |
| </Button> | |
| )} | |
| </div> | |
| </> | |
| )} | |
| </div> | |
| </Drawer> | |
| {!isCreate && drawerId && ( | |
| <DeleteLocaleDialog | |
| locale={drawerId} | |
| open={deleteDialogOpen} | |
| onOpenChange={setDeleteDialogOpen} | |
| onConfirm={handleDelete} | |
| isPending={deleteMutation.isPending} | |
| /> | |
| )} | |
| </> | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment