Payload Logo
Health, Β Figma prototype, Β Components

Creating a custom calendar component

Author

james

Date Published

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 properly it can cost million.

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