Skip to content

Commit

Permalink
Merge pull request #4 from finxol/web/addTripForm
Browse files Browse the repository at this point in the history
  • Loading branch information
finxol authored Jan 22, 2025
2 parents b101e63 + 27f5176 commit 28fca52
Show file tree
Hide file tree
Showing 20 changed files with 1,172 additions and 44 deletions.
25 changes: 25 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"arrowParens": "always",
"printWidth": 90,
"singleQuote": false,
"semi": false,
"trailingComma": "none",
"tabWidth": 4,
"bracketSpacing": true,
"plugins": ["prettier-plugin-tailwindcss", "@ianvs/prettier-plugin-sort-imports"],
"tailwindStylesheet": "./packages/ui/src/styles/globals.css",
"tailwindFunctions": ["cn", "clsx"],
"importOrderTypeScriptVersion": "4.4.0",
"importOrder": [
"^(react/(.*)$)|^(react$)",
"^(next/(.*)$)|^(next$)",
"<THIRD_PARTY_MODULES>",
"",
"^@karr/(.*)$",
"^karr/(.*)$",
"",
"^@/(.*)$",
"^[./]"
],
"proseWrap": "always"
}
7 changes: 6 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,10 @@
{
"mode": "auto"
}
]
],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"prettier.configPath": ".prettierrc.json",
"prettier.resolveGlobalModules": true,
"prettier.requireConfig": true
}
4 changes: 3 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,19 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hookform/resolvers": "catalog:",
"@karr/config": "workspace:*",
"@karr/db": "workspace:*",
"@karr/ui": "workspace:*",
"@tailwindcss/postcss": "catalog:",
"@tanstack/react-query": "catalog:",
"babel-plugin-react-compiler": "catalog:",
"date-fns": "catalog:",
"lucide-react": "catalog:",
"next": "catalog:",
"next-themes": "catalog:",
"ofetch": "catalog:",
"posthog-js": "^1.207.0",
"posthog-js": "catalog:",
"react": "catalog:",
"react-dom": "catalog:",
"react-hook-form": "catalog:",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export default function RootLayout({
variant="link"
className="text-md"
>
<Link href="/search">Trips</Link>
<Link href="/trips/search">Trips</Link>
</Button>
</nav>
</div>
Expand Down
137 changes: 137 additions & 0 deletions apps/web/src/app/trips/new/newTripForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"use client"

import { zodResolver } from "@hookform/resolvers/zod"
import { format } from "date-fns"
import { CalendarDays } from "lucide-react"
import { useForm } from "react-hook-form"

import { NewTripInputSchema, type NewTripInput } from "@karr/db/schemas/trips.js"
import { Button } from "@karr/ui/components/button"
import { Calendar } from "@karr/ui/components/calendar"
import { CurrencyInput } from "@karr/ui/components/currencyInput"
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage
} from "@karr/ui/components/form"
import { Input } from "@karr/ui/components/input"
import { Popover, PopoverContent, PopoverTrigger } from "@karr/ui/components/popover"
import { cn } from "@karr/ui/lib/utils"

export default function NewTripForm() {
const form = useForm<NewTripInput>({
resolver: zodResolver(NewTripInputSchema),
defaultValues: {
account: undefined,
departure: new Date(Date.now()),
from: "",
to: "",
price: 6
}
})

const onSubmit = async (data: NewTripInput) => {
console.log("Submitting...")
console.log(data)
}

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-2">
<FormField
control={form.control}
name="from"
render={({ field }) => (
<FormItem>
<FormLabel>From</FormLabel>
<FormControl>
<Input placeholder="Vannes" {...field} />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="to"
render={({ field }) => (
<FormItem>
<FormLabel>To</FormLabel>
<FormControl>
<Input placeholder="Rennes" {...field} />
</FormControl>
<FormDescription />
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="departure"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Departure</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"pl-3 text-left font-normal w-full",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "dd/MM/yyyy")
) : (
<span>Pick a date</span>
)}
<CalendarDays className="ml-auto w-4 h-4" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent>
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
autoFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
<FormDescription />
</FormItem>
)}
/>
<FormField
control={form.control}
name="price"
render={({ field }) => (
<FormItem>
<FormLabel>Price (per passenger)</FormLabel>
<FormControl>
<div>
<CurrencyInput
value={field.value}
onChange={field.onChange}
/>
</div>
</FormControl>
<FormMessage />
<FormDescription />
</FormItem>
)}
/>
<Button type="submit" className="mt-3">
Create new trip
</Button>
</form>
</Form>
)
}
16 changes: 16 additions & 0 deletions apps/web/src/app/trips/new/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Suspense } from "react"

import Loading from "@/components/Loading"
import NewTripForm from "./newTripForm"

export default function New() {
return (
<article className="mt-6">
<h3>Add new trip</h3>

<Suspense fallback={<Loading />}>
<NewTripForm />
</Suspense>
</article>
)
}
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"dev": "turbo dev",
"lint": "turbo lint",
"check-types": "turbo check-types",
"format": "prettier --write \"**/*.{ts,tsx,md,json,yml,yaml,css,html}\"",
"format:check": "prettier --config prettier.config.mjs --check \"**/*.{ts,tsx,md,json,yml,yaml,css,html}\"",
"format": "prettier --config .prettierrc.json --write \"**/*.{ts,tsx,md,json,yml,yaml,css,html}\"",
"format:check": "prettier --config .prettierrc.json --check \"**/*.{ts,tsx,md,json,yml,yaml,css,html}\"",
"test": "turbo test",
"prepare": "husky",
"commitlint": "commitlint --edit",
Expand Down
14 changes: 12 additions & 2 deletions packages/db/src/schemas/trips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const TripSchema = z.object({
from: z.string(),
to: z.string(),
departure: z.string(),
price: z.number(),
price: z.number().min(0),
createdAt: z.string().optional().nullable(),
updatedAt: z.string().optional().nullable(),
account: z.any().optional()
Expand All @@ -33,8 +33,18 @@ export const NewTripSchema = z.object({
from: z.string(),
to: z.string(),
departure: z.string(),
price: z.number(),
price: z.number().min(0),
account: z.any().optional()
})

export type NewTrip = z.infer<typeof NewTripSchema>

export const NewTripInputSchema = z.object({
from: z.string().min(1),
to: z.string().min(1),
departure: z.date(),
price: z.number().min(0),
account: z.any().optional()
})

export type NewTripInput = z.infer<typeof NewTripInputSchema>
6 changes: 6 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,21 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hookform/resolvers": "catalog:",
"@radix-ui/react-label": "catalog:",
"@radix-ui/react-popover": "catalog:",
"@radix-ui/react-separator": "catalog:",
"@radix-ui/react-slot": "catalog:",
"class-variance-authority": "catalog:",
"clsx": "catalog:",
"date-fns": "catalog:",
"lucide-react": "catalog:",
"next": "catalog:",
"next-themes": "catalog:",
"react": "catalog:",
"react-day-picker": "catalog:",
"react-dom": "catalog:",
"react-hook-form": "catalog:",
"tailwind-merge": "catalog:",
"tailwindcss-animate": "catalog:",
"zod": "catalog:"
Expand Down
94 changes: 94 additions & 0 deletions packages/ui/src/components/calendar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"use client"

import * as React from "react"
import {
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronUpIcon
} from "lucide-react"
import { DayFlag, DayPicker, SelectionState, UI } from "react-day-picker"

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

export type CalendarProps = React.ComponentProps<typeof DayPicker>

export const Calendar = ({
className,
classNames,
showOutsideDays = true,
...props
}: CalendarProps) => {
return (
<DayPicker
showOutsideDays={showOutsideDays}
className={cn("p-3", className)}
classNames={{
[UI.Months]: "relative",
[UI.Month]: "space-y-4 ml-0",
[UI.MonthCaption]: "flex justify-center items-center h-7",
[UI.CaptionLabel]: "text-sm font-medium",
[UI.PreviousMonthButton]: cn(
buttonVariants({ variant: "outline" }),
"absolute left-1 top-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
[UI.NextMonthButton]: cn(
buttonVariants({ variant: "outline" }),
"absolute right-1 top-0 h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100"
),
[UI.MonthGrid]: "w-full border-collapse space-y-1",
[UI.Weekdays]: "flex",
[UI.Weekday]:
"text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]",
[UI.Week]: "flex w-full mt-2",
[UI.Day]:
"h-9 w-9 text-center rounded-md text-sm p-0 relative [&:has([aria-selected].day-range-end)]:rounded-r-md [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected])]:bg-accent first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20",
[UI.DayButton]: cn(
buttonVariants({ variant: "ghost" }),
"h-9 w-9 p-0 font-normal aria-selected:opacity-100 hover:bg-primary hover:text-primary-foreground"
),
[SelectionState.range_end]: "day-range-end",
[SelectionState.selected]:
"bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground",
[SelectionState.range_middle]:
"aria-selected:bg-accent aria-selected:text-accent-foreground",
[DayFlag.today]: "bg-accent text-accent-foreground",
[DayFlag.outside]:
"day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30",
[DayFlag.disabled]: "text-muted-foreground opacity-50",
[DayFlag.hidden]: "invisible",
...classNames
}}
components={{
Chevron: ({ className, orientation = "left", ...props }) => (
<Chevron className={className} orientation={orientation} {...props} />
)
}}
{...props}
/>
)
}

type Direction = "left" | "right" | "up" | "down"

const Chevron = ({
className,
orientation = "left"
}: {
className: string | undefined
orientation: Direction
}) => {
switch (orientation) {
case "left":
return <ChevronLeftIcon className={cn("h-4 w-4", className)} />
case "right":
return <ChevronRightIcon className={cn("h-4 w-4", className)} />
case "up":
return <ChevronUpIcon className={cn("h-4 w-4", className)} />
case "down":
return <ChevronDownIcon className={cn("h-4 w-4", className)} />
default:
return null
}
}
Loading

0 comments on commit 28fca52

Please sign in to comment.