File tree Expand file tree Collapse file tree 14 files changed +145
-33
lines changed
examples/multi-tenant/src
packages/plugin-multi-tenant/src
components/GlobalViewRedirect
providers/TenantSelectionProvider Expand file tree Collapse file tree 14 files changed +145
-33
lines changed Original file line number Diff line number Diff line change @@ -78,8 +78,17 @@ export interface Page {
78
78
export interface Tenant {
79
79
id : string ;
80
80
name : string ;
81
+ /**
82
+ * Used for domain-based tenant handling
83
+ */
81
84
domain ?: string | null ;
85
+ /**
86
+ * Used for url paths, example: /tenant-slug/page-slug
87
+ */
82
88
slug : string ;
89
+ /**
90
+ * If checked, logging in is not required to read. Useful for building public pages.
91
+ */
83
92
allowPublicRead ?: boolean | null ;
84
93
updatedAt : string ;
85
94
createdAt : string ;
Original file line number Diff line number Diff line change 1
1
import type { CollectionSlug , ServerProps , ViewTypes } from 'payload'
2
2
3
+ import { headers as getHeaders } from 'next/headers.js'
3
4
import { redirect } from 'next/navigation.js'
4
5
5
6
import { getGlobalViewRedirect } from '../../utilities/getGlobalViewRedirect.js'
@@ -16,9 +17,11 @@ export const GlobalViewRedirect = async (args: Args) => {
16
17
const collectionSlug = args ?. collectionSlug
17
18
18
19
if ( collectionSlug && args . globalSlugs ?. includes ( collectionSlug ) ) {
20
+ const headers = await getHeaders ( )
19
21
const redirectRoute = await getGlobalViewRedirect ( {
20
22
slug : collectionSlug ,
21
23
docID : args . docID ,
24
+ headers,
22
25
payload : args . payload ,
23
26
tenantFieldName : args . tenantFieldName ,
24
27
view : args . viewType ,
Original file line number Diff line number Diff line change @@ -37,6 +37,10 @@ export const TenantSelectionProviderClient = ({
37
37
const [ preventRefreshOnChange , setPreventRefreshOnChange ] = React . useState ( false )
38
38
const { user } = useAuth ( )
39
39
const userID = React . useMemo ( ( ) => user ?. id , [ user ?. id ] )
40
+ const selectedTenantLabel = React . useMemo (
41
+ ( ) => tenantOptions . find ( ( option ) => option . value === selectedTenantID ) ?. label ,
42
+ [ selectedTenantID , tenantOptions ] ,
43
+ )
40
44
41
45
const router = useRouter ( )
42
46
@@ -80,16 +84,21 @@ export const TenantSelectionProviderClient = ({
80
84
} , [ userID , router ] )
81
85
82
86
return (
83
- < Context . Provider
84
- value = { {
85
- options : tenantOptions ,
86
- selectedTenantID,
87
- setPreventRefreshOnChange,
88
- setTenant,
89
- } }
87
+ < span
88
+ data-selected-tenant-id = { selectedTenantID }
89
+ data-selected-tenant-title = { selectedTenantLabel }
90
90
>
91
- { children }
92
- </ Context . Provider >
91
+ < Context . Provider
92
+ value = { {
93
+ options : tenantOptions ,
94
+ selectedTenantID,
95
+ setPreventRefreshOnChange,
96
+ setTenant,
97
+ } }
98
+ >
99
+ { children }
100
+ </ Context . Provider >
101
+ </ span >
93
102
)
94
103
}
95
104
Original file line number Diff line number Diff line change @@ -41,14 +41,10 @@ export const TenantSelectionProvider = async ({
41
41
42
42
const cookies = await getCookies ( )
43
43
const tenantCookie = cookies . get ( 'payload-tenant' ) ?. value
44
- const selectedTenant =
45
- tenantOptions . find ( ( option ) => option . value === tenantCookie ) ?. label || tenantCookie
46
44
47
45
return (
48
- < span data-selected-tenant-id = { tenantCookie } data-selected-tenant-title = { selectedTenant } >
49
- < TenantSelectionProviderClient initialValue = { tenantCookie } tenantOptions = { tenantOptions } >
50
- { children }
51
- </ TenantSelectionProviderClient >
52
- </ span >
46
+ < TenantSelectionProviderClient initialValue = { tenantCookie } tenantOptions = { tenantOptions } >
47
+ { children }
48
+ </ TenantSelectionProviderClient >
53
49
)
54
50
}
Original file line number Diff line number Diff line change 1
1
import type { Payload , ViewTypes } from 'payload'
2
2
3
- import { headers as getHeaders } from 'next/headers.js'
4
-
5
3
import { SELECT_ALL } from '../constants.js'
6
4
import { getTenantFromCookie } from './getTenantFromCookie.js'
7
5
8
6
type Args = {
9
7
docID ?: number | string
8
+ headers : Headers
10
9
payload : Payload
11
10
slug : string
12
11
tenantFieldName : string
@@ -15,11 +14,11 @@ type Args = {
15
14
export async function getGlobalViewRedirect ( {
16
15
slug,
17
16
docID,
17
+ headers,
18
18
payload,
19
19
tenantFieldName,
20
20
view,
21
21
} : Args ) : Promise < string | void > {
22
- const headers = await getHeaders ( )
23
22
const tenant = getTenantFromCookie ( headers , payload . db . defaultIDType )
24
23
let redirectRoute
25
24
Original file line number Diff line number Diff line change 1
1
import type { CollectionConfig } from 'payload'
2
2
3
3
import { postsSlug } from '../shared.js'
4
+ import { userFilterOptions } from './Users/filterOptions.js'
4
5
5
6
export const Posts : CollectionConfig = {
6
7
slug : postsSlug ,
@@ -18,20 +19,16 @@ export const Posts: CollectionConfig = {
18
19
type : 'text' ,
19
20
required : true ,
20
21
} ,
21
- {
22
- name : 'excerpt' ,
23
- label : 'Excerpt' ,
24
- type : 'text' ,
25
- } ,
26
- {
27
- type : 'text' ,
28
- name : 'slug' ,
29
- localized : true ,
30
- } ,
31
22
{
32
23
name : 'relatedLinks' ,
33
24
relationTo : 'links' ,
34
25
type : 'relationship' ,
35
26
} ,
27
+ {
28
+ name : 'author' ,
29
+ relationTo : 'users' ,
30
+ type : 'relationship' ,
31
+ filterOptions : userFilterOptions ,
32
+ } ,
36
33
] ,
37
34
}
Original file line number Diff line number Diff line change
1
+ import type { FilterOptions , Where } from 'payload'
2
+
3
+ import { getTenantFromCookie } from '@payloadcms/plugin-multi-tenant/utilities'
4
+
5
+ export const userFilterOptions : FilterOptions = ( { req } ) => {
6
+ const selectedTenant = getTenantFromCookie ( req . headers , req . payload . db . defaultIDType )
7
+ if ( ! selectedTenant ) {
8
+ return false
9
+ }
10
+
11
+ return {
12
+ or : [
13
+ {
14
+ 'tenants.tenant' : {
15
+ equals : selectedTenant ,
16
+ } ,
17
+ } ,
18
+ {
19
+ roles : {
20
+ in : [ 'admin' ] ,
21
+ } ,
22
+ } ,
23
+ ] ,
24
+ } as Where
25
+ }
Original file line number Diff line number Diff line change
1
+ import type { CollectionConfig } from 'payload'
2
+
3
+ import { usersSlug } from '../../shared.js'
4
+
5
+ export const Users : CollectionConfig = {
6
+ slug : usersSlug ,
7
+ auth : true ,
8
+ admin : {
9
+ useAsTitle : 'email' ,
10
+ } ,
11
+ access : {
12
+ read : ( ) => true ,
13
+ } ,
14
+ fields : [
15
+ // Email added by default
16
+ // Add more fields as needed
17
+ {
18
+ type : 'select' ,
19
+ name : 'roles' ,
20
+ hasMany : true ,
21
+ options : [
22
+ {
23
+ label : 'Admin' ,
24
+ value : 'admin' ,
25
+ } ,
26
+ {
27
+ label : 'User' ,
28
+ value : 'user' ,
29
+ } ,
30
+ ] ,
31
+ saveToJWT : true ,
32
+ } ,
33
+ ] ,
34
+ }
Original file line number Diff line number Diff line change
1
+ import './styles.css'
2
+
3
+ export function Icon ( ) {
4
+ return < div id = "tenant-icon" />
5
+ }
Original file line number Diff line number Diff line change
1
+ # tenant-icon {
2
+ border-radius : 100% ;
3
+ height : 18px ;
4
+ width : 18px ;
5
+ }
6
+
7
+ [data-selected-tenant-title = "Blue Dog" ] # tenant-icon {
8
+ background-color : var (--theme-success-300 );
9
+ }
10
+
11
+ [data-selected-tenant-title = "Steel Cat" ] # tenant-icon {
12
+ background-color : var (--theme-warning-300 );
13
+ }
You can’t perform that action at this time.
0 commit comments