From a84985b40f36123cce0e1696bd1ce2f76f3d93cc Mon Sep 17 00:00:00 2001 From: Kenan KARAER Date: Wed, 3 Dec 2025 22:27:48 +0300 Subject: [PATCH] Feat: Add searchable customer combobox to ReservationForm --- package-lock.json | 17 ++ package.json | 1 + src/app/dashboard/reservations/new/page.tsx | 2 +- .../reservations/new/reservation-form.tsx | 87 +++++++-- src/components/ui/command.tsx | 184 ++++++++++++++++++ 5 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 src/components/ui/command.tsx diff --git a/package-lock.json b/package-lock.json index 16dc705..f4636d5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@supabase/supabase-js": "^2.86.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "date-fns": "^2.30.0", "lucide-react": "^0.555.0", "next": "16.0.7", @@ -4474,6 +4475,22 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", diff --git a/package.json b/package.json index 314c3f8..c75b609 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@supabase/supabase-js": "^2.86.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", "date-fns": "^2.30.0", "lucide-react": "^0.555.0", "next": "16.0.7", diff --git a/src/app/dashboard/reservations/new/page.tsx b/src/app/dashboard/reservations/new/page.tsx index 6e8df30..ab2dff3 100644 --- a/src/app/dashboard/reservations/new/page.tsx +++ b/src/app/dashboard/reservations/new/page.tsx @@ -7,7 +7,7 @@ export default async function NewReservationPage() { // Fetch necessary data for the form const { data: halls } = await supabase.from('halls').select('id, name') - const { data: customers } = await supabase.from('customers').select('id, full_name').order('created_at', { ascending: false }).limit(50) + const { data: customers } = await supabase.from('customers').select('id, full_name, phone').order('created_at', { ascending: false }).limit(100) const { data: packages } = await supabase.from('packages').select('id, name, price').eq('is_active', true) return ( diff --git a/src/app/dashboard/reservations/new/reservation-form.tsx b/src/app/dashboard/reservations/new/reservation-form.tsx index 2cdde4a..00f4714 100644 --- a/src/app/dashboard/reservations/new/reservation-form.tsx +++ b/src/app/dashboard/reservations/new/reservation-form.tsx @@ -18,6 +18,21 @@ import { Textarea } from "@/components/ui/textarea" import { createReservation } from "./actions" import { useState } from "react" import { toast } from "sonner" +import { Check, ChevronsUpDown } from "lucide-react" +import { cn } from "@/lib/utils" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" const formSchema = z.object({ hall_id: z.string().min(1, "Salon seçmelisiniz."), @@ -31,13 +46,14 @@ const formSchema = z.object({ interface ReservationFormProps { halls: { id: string, name: string }[] - customers: { id: string, full_name: string }[] + customers: { id: string, full_name: string, phone?: string | null }[] packages: { id: string, name: string, price: number }[] } export function ReservationForm({ halls, customers, packages }: ReservationFormProps) { const [loading, setLoading] = useState(false) const [error, setError] = useState(null) + const [openCustomer, setOpenCustomer] = useState(false) const form = useForm>({ resolver: zodResolver(formSchema), @@ -123,20 +139,63 @@ export function ReservationForm({ halls, customers, packages }: ReservationFormP control={form.control} name="customer_id" render={({ field }) => ( - + Müşteri - + + + + + + + + + + + Müşteri bulunamadı. + + {customers.map((customer) => ( + { + form.setValue("customer_id", customer.id) + setOpenCustomer(false) + }} + > + +
+ {customer.full_name} + {customer.phone && ( + {customer.phone} + )} +
+
+ ))} +
+
+
+
+
)} diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..8cb4ca7 --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,184 @@ +"use client" + +import * as React from "react" +import { Command as CommandPrimitive } from "cmdk" +import { SearchIcon } from "lucide-react" + +import { cn } from "@/lib/utils" +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog" + +function Command({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandDialog({ + title = "Command Palette", + description = "Search for a command to run...", + children, + className, + showCloseButton = true, + ...props +}: React.ComponentProps & { + title?: string + description?: string + className?: string + showCloseButton?: boolean +}) { + return ( + + + {title} + {description} + + + + {children} + + + + ) +} + +function CommandInput({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ + +
+ ) +} + +function CommandList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandEmpty({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandGroup({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function CommandShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +}