Payload Logo
Agriculture, Β CX, Β Service design

Retrieving the Order collection considering user role and privacy

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


ReadΒ case study
Innovative client delivering medical cannabis according to a schedule

comprises of 2 files

Start by adding the - Booking collection

using established patterns to make the funnel delightful by communicating the activity
|_ πŸ“ src
| |_ πŸ“ app
| |_ πŸ“ blocks
| |_ πŸ“ collection
| | |_ πŸ“ Bookings
| | | |_ πŸ“ access
| | | | |_ πŸ“„ adminOrSelfOrGuests.ts
| | | |_ πŸ“„ index.ts
|_ πŸ“„ paylaod types.ts
|_ πŸ“„ payload.config.ts

1import { getPayload, Where } from 'payload'
2import config from '@payload-config'
3import React from 'react'
4import { Post, User } from '@/payload-types'
5import { getMeUser } from '@/utilities/getMeUser'
6import PageClient from './page.client'
7import BookingCard from '../../../components/Bookings/BookingCard'
8import { redirect } from 'next/navigation'
9
10export default async function Bookings() {
11 const currentUser = await getMeUser()
12
13 if (!currentUser) {
14 redirect('/login')
15 }
16
17 const [upcomingBookings, pastBookings] = await Promise.all([
18 getBookings('upcoming', currentUser.user),
19 getBookings('past', currentUser.user),
20 ])
21
22 const formattedUpcomingBookings = upcomingBookings.docs.map((booking) => ({
23 ...(booking.post as Pick<Post, 'meta' | 'slug' | 'title'>),
24 fromDate: booking.fromDate,
25 toDate: booking.toDate,
26 guests: booking.guests?.map(guest => typeof guest === 'number' ? guest.toString() : guest) || null,
27 id: booking.id.toString(),
28 }))
29
30 const formattedPastBookings = pastBookings.docs.map((booking) => ({
31 ...(booking.post as Pick<Post, 'meta' | 'slug' | 'title'>),
32 fromDate: booking.fromDate,
33 toDate: booking.toDate,
34 guests: booking.guests,
35 id: booking.id,
36 }))
37
38 console.log(upcomingBookings, pastBookings)
39
40 return (
41 <>
42 <PageClient />
43 <div className="my-10 container space-y-10">
44 <div>
45 {upcomingBookings.docs.length > 0 && (
46 <h2 className="text-4xl font-medium tracking-tighter my-6">Upcoming stays</h2>
47 )}
48
49 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2">
50 {formattedUpcomingBookings.map((booking) => (
51 <BookingCard key={booking.id} booking={booking} />
52 ))}
53 </div>
54 </div>
55
56 {pastBookings.docs.length > 0 && (
57 <h2 className="text-4xl font-medium tracking-tighter my-6">Past stays</h2>
58 )}
59
60 <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-2">
61 {formattedPastBookings.map((booking) => (
62 <BookingCard key={booking.id} booking={booking} />
63 ))}
64 </div>
65 </div>
66 </>
67 )
68}
69
70const getBookings = async (type: 'upcoming' | 'past', currentUser: User) => {
71 const payload = await getPayload({ config })
72
73 let whereQuery: Where
74
75 if (type === 'upcoming') {
76 whereQuery = {
77 and: [
78 {
79 fromDate: {
80 greater_than_equal: new Date(),
81 },
82 },
83 {
84 customer: {
85 equals: currentUser.id,
86 },
87 },
88 ],
89 }
90 } else {
91 whereQuery = {
92 and: [
93 {
94 fromDate: {
95 less_than: new Date(),
96 },
97 },
98 {
99 customer: {
100 equals: currentUser.id,
101 },
102 },
103 ],
104 }
105 }
106
107 const bookings = await payload.find({
108 collection: 'bookings',
109 limit: 100,
110 where: whereQuery,
111 depth: 2,
112 sort: '-fromDate',
113 select: {
114 slug: true,
115 post: true,
116 guests: true,
117 fromDate: true,
118 toDate: true,
119 },
120 })
121
122 return bookings
123}

Folder structure becomes the taxonomy

To create a booking collection comprises of 2 files predominately

Brackets () are omitted - Front end
|_ πŸ“ app
| |_ πŸ“ (frontend)
| | |_ πŸ“ bookings
| | | |_ πŸ“„ page.client.tsx
| | | |_ πŸ“„ page.tsx
|_ πŸ“ (payload)
|_ πŸ“ collections
|_ πŸ“„ paylaod types.ts
|_ πŸ“„ payload.config.ts

Back-end collection

src/collections/policy/index.ts

1import { adminOrSelf } from '@/access/adminOrSelf'
2import { adminOrSelfField } from '@/access/adminOrSelfField'
3import { isAdmin } from '@/access/isAdmin'
4import { isAdminField } from '@/access/isAdminField'
5import { slugField } from '@/fields/slug'
6import { CollectionConfig } from 'payload'
7import { adminOrSelfOrGuests } from './access/adminOrSelfOrGuests'
8
9export const Policy: CollectionConfig = {
10 slug: 'policys',
11 labels: {
12 singular: 'Policy',
13 plural: 'Policys',
14 },
15 typescript: {
16 interface: 'Policy',
17 },
18 admin: {
19 useAsTitle: 'title',
20 defaultColumns: ['title', 'fromDate', 'toDate', 'slug', 'customer'],
21 },
22 access: {
23 read: adminOrSelfOrGuests('customer', 'guests'),
24 create: isAdmin,
25 delete: isAdmin,
26 },
27 fields: [
28 {
29 name: 'title',
30 label: 'Title',
31 type: 'text',
32 required: true,
33 access: {
34 update: isAdminField,
35 },
36 },
37 {
38 name: 'customer',
39 type: 'relationship',
40 relationTo: 'users',
41 filterOptions: {
42 role: {
43 equals: 'customer',
44 },
45 },
46 access: {
47 update: isAdminField,
48 },
49 },
50 {
51 name: 'guests',
52 type: 'relationship',
53 hasMany: true,
54 relationTo: 'users',
55 access: {
56 update: adminOrSelfField('customer'),
57 },
58 admin: {
59 isSortable: true,
60 },
61 },
62 ...slugField('title', {
63 checkboxOverrides: {
64 access: {
65 update: isAdminField,
66 },
67 },
68 slugOverrides: {
69 access: {
70 update: isAdminField,
71 },
72 },
73 }),
74 {
75 name: 'post',
76 relationTo: 'posts',
77 type: 'relationship',
78 required: true,
79 access: {
80 update: isAdminField,
81 },
82 },
83 {
84 name: 'paymentStatus',
85 label: 'Payment Status',
86 type: 'select',
87 admin: {
88 position: 'sidebar',
89 },
90 options: [
91 {
92 label: 'Paid',
93 value: 'paid',
94 },
95 {
96 label: 'Unpaid',
97 value: 'unpaid',
98 },
99 ],
100 access: {
101 update: isAdminField,
102 },
103 },
104 {
105 name: 'fromDate',
106 type: 'date',
107 required: true,
108 index: true,
109 label: 'Check-in Date',
110 admin: {
111 position: 'sidebar',
112 date: {
113 pickerAppearance: 'dayAndTime',
114 },
115 },
116 access: {
117 update: isAdminField,
118 },
119 },
120 {
121 name: 'toDate',
122 type: 'date',
123 required: true,
124 label: 'Check-out Date',
125 admin: {
126 position: 'sidebar',
127 date: {
128 pickerAppearance: 'dayAndTime',
129 },
130 },
131 access: {
132 update: isAdminField,
133 },
134 },
135 ],
136}
137



Front end template: [src/ui/frontend/index.ts]

Health, Β Figma prototype, Β Components

Add a component to your design system. Use a hooks to join/relate User Agreement creating the ideal unchallenged User experience

Insurance, Β CX, Β Service design

Subscription payment and Sharing the policy using a protected route with a paywall to ensure privacy of users the payment is intended for