Payload Logo
Health, Β Figma prototype, Β Components

Creating a custom calendar component

Author

james

Date Published

AI Booking Assistant

Get personalized recommendations and book your perfect stay

To use the AI assistant and complete bookings, please log in.

Log In

Forms are easily the most overlooked / misunderstood chapters in UX. With little action and knowledge of the DOM, you can perform surgery on a database if executed correctly. Done incorrectly it can cost millions for a humble form.

Simplified we are adding a pre made component to our site and displaying it on the front end to clients in order to request a booking

ReadΒ case study
Innovative client delivering prescribed medication according to a schedule


Step 2


Form submissions

Add the Shadcn premade component to your site in order to display it on front end

npm add shadcn-calendar

|_ πŸ“ app
| |_ πŸ“ components
| | |_ πŸ“ ui
| | | |_ πŸ“„ card.tsx
| | | |_ πŸ“„ calendar.tsx
|_ πŸ“ collections
|_ πŸ“ fields
|_ πŸ“„ paylaod types.ts
|_ πŸ“„ payload.config.ts

Drag the calendar block into the from builder

1'use client'
2
3import * as React from 'react'
4import { ChevronLeft, ChevronRight } from 'lucide-react'
5import { DayPicker } from 'react-day-picker'
6
7import { cn } from '@/utilities/cn'
8import { buttonVariants } from '@/components/ui/button'
9
10export type CalendarProps = React.ComponentProps<typeof DayPicker>
11
12function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
13 return (
14 <DayPicker
15 showOutsideDays={showOutsideDays}
16 className={cn('p-3', className)}
17 classNames={{
18 months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
19 month: 'space-y-4',
20 caption: 'flex justify-center pt-1 relative items-center',
21 caption_label: 'text-sm font-medium',
22 nav: 'space-x-1 flex items-center',
23 nav_button: cn(
24 buttonVariants({ variant: 'outline' }),
25 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
26 ),
27 nav_button_previous: 'absolute left-1',
28 nav_button_next: 'absolute right-1',
29 table: 'w-full border-collapse space-y-1',
30 head_row: 'flex',
31 head_cell: 'text-muted-foreground rounded-md w-9 font-normal text-[0.8rem]',
32 row: 'flex w-full mt-2',
33 cell: 'h-9 w-9 text-center 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',
34 day: cn(
35 buttonVariants({ variant: 'ghost' }),
36 'h-9 w-9 p-0 font-normal aria-selected:opacity-100',
37 ),
38 day_range_end: 'day-range-end',
39 day_selected:
40 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
41 day_today: 'bg-accent text-accent-foreground',
42 day_outside:
43 'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
44 day_disabled: 'text-muted-foreground opacity-50',
45 day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
46 day_hidden: 'invisible',
47 ...classNames,
48 }}
49 components={{
50 IconLeft: ({ className, ...props }) => (
51 <ChevronLeft className={cn('h-4 w-4', className)} {...props} />
52 ),
53 IconRight: ({ className, ...props }) => (
54 <ChevronRight className={cn('h-4 w-4', className)} {...props} />
55 ),
56 }}
57 {...props}
58 />
59 )
60}
61Calendar.displayName = 'Calendar'
62
63export { Calendar }
64

Submitting a booking including the user id using a hook

|_ πŸ“ src
| |_ πŸ“ components
| |_ πŸ“ hooks
| | |_ πŸ“„ addCustomerIdToForm.ts
| | |_ πŸ“„ formatSlug.ts
| |_ πŸ“ plugins
| |_ πŸ“ providers
|_ πŸ“ collections
|_ πŸ“ fields
|_ πŸ“„ paylaod types.ts
|_ πŸ“„ payload.config.ts

1import { CollectionBeforeValidateHook } from 'payload'
2
3// This hook will add the customer ID to the form if the user is logged in.
4// FormIds are the id of forms for which this hook should run.
5export const addCustomerToForm =
6 (formIds: string[]): CollectionBeforeValidateHook =>
7 ({ data, req: { user, payload } }) => {
8 if (data && typeof data === 'object' && 'form' in data && formIds.includes(data.form) && user) {
9 payload.logger.info(
10 `User is logged in, adding customer to form. FormID: ${data.form}, CustomerID: ${user?.id}, SubmissionID: ${data?.id}`,
11 )
12 data.customer = user?.id
13 }
14
15 console.log(data)
16 return data
17 }
18

Create a form in payload and send a test message. you should see a form entry now includes the user who submitted the booking request using the hook which you can create a booking from manually