Skip to content

Commit

Permalink
Feat enhanced type safety for Real Estate form (Codehagen#178)
Browse files Browse the repository at this point in the history
* Added realestate zod schema

* Added error message from zod

* Fixed typo in valitators package.json
  • Loading branch information
chetra-seng authored Feb 29, 2024
1 parent d36abec commit 1062141
Show file tree
Hide file tree
Showing 3 changed files with 165 additions and 54 deletions.
191 changes: 138 additions & 53 deletions apps/www/components/buttons/AddRealEstateButton.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
"use client";

import * as React from "react";
import { zodResolver } from "@hookform/resolvers/zod";
import { format } from "date-fns";
import { Calendar as CalendarIcon } from "lucide-react";
import { useForm } from "react-hook-form";

import { RealEstate, realEstateSchema } from "@projectx/validators/realEstate";

import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
Expand All @@ -16,6 +20,7 @@ import {
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Form, FormField, FormItem, FormMessage } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import {
Expand All @@ -34,7 +39,13 @@ import {
import { Textarea } from "../ui/textarea";

export function AddRealEstateButton() {
const [date, setDate] = React.useState<Date>();
const form = useForm<RealEstate>({
resolver: zodResolver(realEstateSchema),
});

const onSubmit = (data: RealEstate) => {
console.log(data);
};

return (
<Dialog>
Expand All @@ -48,61 +59,135 @@ export function AddRealEstateButton() {
Add your property here. Click save when you are done.
</DialogDescription>
</DialogHeader>
<div className="grid gap-2">
<Label htmlFor="subject">Address</Label>
<Input id="subject" placeholder="Address..." />
</div>
<div className="grid gap-2">
<Label htmlFor="subject">City</Label>
<Input id="subject" placeholder="City..." />
</div>
<div className="grid grid-cols-2 gap-4">
<div className="grid gap-2">
<Label htmlFor="subject">State</Label>
<Input id="subject" placeholder="State..." />
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Postal Code</Label>
<Input id="subject" placeholder="Postal Code..." />
</div>
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Purchase Date</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"justify-start text-left font-normal",
!date && "text-muted-foreground",
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-3">
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">Address</Label>
<Input id="subject" placeholder="Address..." {...field} />
<FormMessage />
</FormItem>
)}
/>

<FormField
control={form.control}
name="city"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">City</Label>
<Input id="subject" placeholder="City..." {...field} />
<FormMessage />
</FormItem>
)}
/>
<div className="grid grid-cols-2 items-start gap-4">
<FormField
control={form.control}
name="state"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">State</Label>
<Input id="subject" placeholder="State..." {...field} />
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="postalCode"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">Postal Code</Label>
<Input
id="subject"
placeholder="Postal Code..."
{...field}
/>
<FormMessage />
</FormItem>
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : <span>Pick a date</span>}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={date}
onSelect={setDate}
initialFocus
/>
</PopoverContent>
</Popover>
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Purchase Value</Label>
<Input id="subject" placeholder="Purchase Value..." />
</div>
<div className="grid gap-2">
<Label htmlFor="subject">Curent value</Label>
<Input id="subject" placeholder="Curent value..." />
</div>
</div>
<FormField
control={form.control}
name="purchaseDate"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">Purchase Date</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"justify-start text-left font-normal",
!field.value && "text-muted-foreground",
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="purchaseValue"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">Purchase Value</Label>
<Input
id="subject"
type="number"
placeholder="Purchase value..."
{...field}
onChange={(e) => field.onChange(+e.target.value)}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="currentValue"
render={({ field }) => (
<FormItem className="grid gap-2">
<Label htmlFor="subject">Current Value</Label>
<Input
id="subject"
placeholder="Current value..."
type="number"
{...field}
onChange={(e) => field.onChange(+e.target.value)}
/>
<FormMessage />
</FormItem>
)}
/>

<DialogFooter>
<Button type="submit">Add Property</Button>
</DialogFooter>
<DialogFooter>
<Button type="submit">Add Property</Button>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
Expand Down
3 changes: 2 additions & 1 deletion packages/validators/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
"private": true,
"type": "module",
"exports": {
".": "./src/index.ts"
".": "./src/index.ts",
"./realEstate": "./src/realEstate.ts"
},
"typesVersions": {
"*": {
Expand Down
25 changes: 25 additions & 0 deletions packages/validators/src/realEstate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { z } from "zod";

export const realEstateSchema = z.object({
address: z
.string({ required_error: "Address is required" })
.min(5, { message: "Address must be at least 5 characters" }),
city: z
.string({ required_error: "City is required" })
.min(3, { message: "City must be at least 3 characters" }),
state: z
.string({ required_error: "State is required" })
.min(2, { message: "State must be at least 2 characters" }),
postalCode: z
.string({ required_error: "Postal Code is required" })
.min(5, { message: "Postal Code must be at least 5 characters" }),
purchaseDate: z.date({ required_error: "Purchase date is required" }),
purchaseValue: z
.number({ required_error: "Purchase value is required" })
.min(1, { message: "Purchase value must be at least 1" }),
currentValue: z
.number({ required_error: "Current value is required" })
.min(1, { message: "Current value must be at least 1" }),
});

export type RealEstate = z.infer<typeof realEstateSchema>;

0 comments on commit 1062141

Please sign in to comment.