Skip to content

Commit

Permalink
feat: add search bar form with DateRange
Browse files Browse the repository at this point in the history
  • Loading branch information
patrikzita committed Feb 14, 2024
1 parent 78073c2 commit 0937fd0
Show file tree
Hide file tree
Showing 17 changed files with 1,304 additions and 52 deletions.
410 changes: 409 additions & 1 deletion package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-navigation-menu": "^1.1.4",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@typescript-eslint/eslint-plugin": "^6.18.0",
"@typescript-eslint/parser": "^6.18.0",
"apollo-server-micro": "^3.13.0",
"better-sqlite3": "^9.2.2",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"date-fns": "^3.3.1",
"deepmerge": "^4.3.1",
"drizzle-orm": "^0.29.1",
"eslint-plugin-drizzle": "^0.2.3",
Expand All @@ -39,6 +42,7 @@
"micro": "^9.4.1",
"next": "14.0.4",
"react": "^18",
"react-day-picker": "^8.10.0",
"react-dom": "^18",
"react-hook-form": "^7.49.3",
"reflect-metadata": "^0.2.1",
Expand Down
62 changes: 62 additions & 0 deletions src/components/DateRange.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as React from "react";
import { addDays, format } from "date-fns";
import { Calendar as CalendarIcon } from "lucide-react";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import type { DateRange, SelectRangeEventHandler } from "react-day-picker";

type DateRangeProps = {
className?: string;
onSelect: SelectRangeEventHandler;
selected: DateRange;
};

export function DateRange({ className, selected, onSelect }: DateRangeProps) {
return (
<div className={cn("grid gap-2", className)}>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"w-[300px] justify-start text-left font-normal",
!selected && "text-muted-foreground"
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{selected?.from ? (
selected.to ? (
<>
{format(selected.from, "LLL dd, y")} -{" "}
{format(selected.to, "LLL dd, y")}
</>
) : (
format(selected.from, "LLL dd, y")
)
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
initialFocus
mode="range"
defaultMonth={selected?.from}
selected={selected}
onSelect={onSelect}
numberOfMonths={2}
/>
</PopoverContent>
</Popover>
</div>
);
}
27 changes: 27 additions & 0 deletions src/components/PreviewCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Glamp } from "@/generated/graphql";
import { cn } from "@/lib/utils";

type CardProps = React.ComponentProps<typeof Card> & {
glamp: Glamp;
};

export function PreviewCard({ className, glamp, ...props }: CardProps) {
return (
<Card className={cn("w-[380px]", className)} {...props}>
<CardHeader>
<CardTitle>{glamp.title}</CardTitle>
<CardDescription>{glamp.description}</CardDescription>
</CardHeader>
<CardContent className="grid gap-4"></CardContent>
<CardFooter></CardFooter>
</Card>
);
}
65 changes: 65 additions & 0 deletions src/components/SeachBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";

import { Button } from "@/components/ui/button";
import {
Form,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { toast } from "@/components/ui/use-toast";
import { DateRange } from "./DateRange";

const FormSchema = z.object({
dateRange: z.object({
from: z.date({ required_error: "A date of birth is required." }),
to: z.date({ required_error: "A date to is required" }),
}),
});

function SearchBar() {
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});

function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: "You submitted the following values:",
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
});
}

return (
<>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<FormField
control={form.control}
name="dateRange"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Date of birth</FormLabel>
<DateRange selected={field.value} onSelect={field.onChange} />
<FormDescription>
Your date of birth is used to calculate your age.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit">Submit</Button>
</form>
</Form>
</>
);
}

export default SearchBar;
37 changes: 37 additions & 0 deletions src/components/Shell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from "react";
import { cva, type VariantProps } from "class-variance-authority";

import { cn } from "@/lib/utils";

const shellVariants = cva("grid items-center gap-8 pb-8 pt-6 md:py-8", {
variants: {
variant: {
default: "container",
sidebar: "",
centered: "container flex h-[100dvh] max-w-2xl flex-col justify-center",
markdown: "container max-w-3xl py-8 md:py-10 lg:py-10",
},
},
defaultVariants: {
variant: "default",
},
});

interface ShellProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof shellVariants> {
as?: React.ElementType;
}

function Shell({
className,
as: Comp = "section",
variant,
...props
}: ShellProps) {
return (
<Comp className={cn(shellVariants({ variant }), className)} {...props} />
);
}

export { Shell, shellVariants };
70 changes: 70 additions & 0 deletions src/components/ui/calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from "react"
import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons"
import { DayPicker } from "react-day-picker"

import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"

export type CalendarProps = React.ComponentProps<typeof DayPicker>

function Calendar({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
months: "flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0",
month: "space-y-4",
caption: "flex justify-center pt-1 relative items-center",
caption_label: "text-sm font-medium",
nav: "space-x-1 flex items-center",
nav_button: cn(
buttonVariants({ variant: "outline" }),
"h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
nav_button_previous: "absolute left-1",
nav_button_next: "absolute right-1",
table: "w-full border-collapse space-y-1",
head_row: "flex",
head_cell:
"text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
row: "flex w-full mt-2",
cell: cn(
"relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md",
props.mode === "range"
? "[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md"
: "[&:has([aria-selected])]:rounded-md"
),
day: cn(
buttonVariants({ variant: "ghost" }),
"h-8 w-8 p-0 font-normal aria-selected:opacity-100"
),
day_range_start: "day-range-start",
day_range_end: "day-range-end",
day_selected:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
day_today: "bg-accent text-accent-foreground",
day_outside:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
day_disabled: "text-muted-foreground opacity-50",
day_range_middle:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
day_hidden: "invisible",
...classNames,
}}
components={{
IconLeft: ({ ...props }) => <ChevronLeftIcon className="h-4 w-4" />,
IconRight: ({ ...props }) => <ChevronRightIcon className="h-4 w-4" />,
}}
{...props}
/>
)
}
Calendar.displayName = "Calendar"

export { Calendar }
76 changes: 76 additions & 0 deletions src/components/ui/card.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as React from "react"

import { cn } from "@/lib/utils"

const Card = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className
)}
{...props}
/>
))
Card.displayName = "Card"

const CardHeader = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"

const CardTitle = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLHeadingElement>
>(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"

const CardDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"

const CardContent = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"

const CardFooter = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"

export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
Loading

0 comments on commit 0937fd0

Please sign in to comment.