1
1
'use client'
2
2
3
- import type { RelationshipFieldClientProps } from 'payload'
3
+ import type { RelationshipFieldClientProps , StaticLabel } from 'payload'
4
4
5
- import { RelationshipField , useField , useFormModified } from '@payloadcms/ui'
5
+ import { getTranslation } from '@payloadcms/translations'
6
+ import {
7
+ ConfirmationModal ,
8
+ RelationshipField ,
9
+ Translation ,
10
+ useField ,
11
+ useForm ,
12
+ useFormModified ,
13
+ useModal ,
14
+ useTranslation ,
15
+ } from '@payloadcms/ui'
6
16
import React from 'react'
7
17
8
- import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'
18
+ import type {
19
+ PluginMultiTenantTranslationKeys ,
20
+ PluginMultiTenantTranslations ,
21
+ } from '../../translations/index.js'
22
+
9
23
import './index.scss'
24
+ import { useTenantSelection } from '../../providers/TenantSelectionProvider/index.client.js'
10
25
11
26
const baseClass = 'tenantField'
12
27
@@ -16,62 +31,172 @@ type Props = {
16
31
} & RelationshipFieldClientProps
17
32
18
33
export const TenantField = ( args : Props ) => {
19
- const { debug, unique } = args
20
- const { setValue, value } = useField < number | string > ( )
21
- const modified = useFormModified ( )
22
- const {
23
- options,
24
- selectedTenantID,
25
- setEntityType : setEntityType ,
26
- setModified,
27
- setTenant,
28
- } = useTenantSelection ( )
29
-
30
- const hasSetValueRef = React . useRef ( false )
34
+ const { entityType, options, selectedTenantID, setEntityType, setTenant } = useTenantSelection ( )
35
+ const { value } = useField < number | string > ( )
31
36
32
37
React . useEffect ( ( ) => {
33
- if ( ! hasSetValueRef . current ) {
34
- // set value on load
35
- if ( value && value !== selectedTenantID ) {
36
- setTenant ( { id : value , refresh : unique } )
37
- } else {
38
- // in the document view, the tenant field should always have a value
39
- const defaultValue = selectedTenantID || options [ 0 ] ?. value
40
- setTenant ( { id : defaultValue , refresh : unique } )
38
+ if ( ! entityType ) {
39
+ setEntityType ( args . unique ? 'global' : 'document' )
40
+ } else {
41
+ // unique documents are controlled from the global TenantSelector
42
+ if ( ! args . unique && value ) {
43
+ if ( ! selectedTenantID || value !== selectedTenantID ) {
44
+ setTenant ( { id : value , refresh : false } )
45
+ }
41
46
}
42
- hasSetValueRef . current = true
43
- } else if ( ! value || value !== selectedTenantID ) {
44
- // Update the field on the document value when the tenant is changed
45
- setValue ( selectedTenantID , ! value || value === selectedTenantID )
46
47
}
47
- } , [ value , selectedTenantID , setTenant , setValue , options , unique ] )
48
48
49
- React . useEffect ( ( ) => {
50
- setEntityType ( unique ? 'global' : 'document' )
51
49
return ( ) => {
52
- setEntityType ( undefined )
53
- }
54
- } , [ unique , setEntityType ] )
55
-
56
- React . useEffect ( ( ) => {
57
- // sync form modified state with the tenant selection provider context
58
- setModified ( modified )
59
-
60
- return ( ) => {
61
- setModified ( false )
50
+ if ( entityType ) {
51
+ setEntityType ( undefined )
52
+ }
62
53
}
63
- } , [ modified , setModified ] )
54
+ } , [ args . unique , options , selectedTenantID , setTenant , value , setEntityType , entityType ] )
64
55
65
- if ( debug ) {
56
+ if ( options . length > 1 ) {
66
57
return (
67
- < div className = { baseClass } >
68
- < div className = { `${ baseClass } __wrapper` } >
69
- < RelationshipField { ...args } />
58
+ < >
59
+ < div className = { baseClass } >
60
+ < div className = { `${ baseClass } __wrapper` } >
61
+ < RelationshipField
62
+ { ...args }
63
+ field = { {
64
+ ...args . field ,
65
+ required : true ,
66
+ } }
67
+ readOnly = { args . readOnly || args . unique }
68
+ />
69
+ </ div >
70
70
</ div >
71
- < div className = { `${ baseClass } __hr` } />
72
- </ div >
71
+ { args . unique ? (
72
+ < SyncFormModified />
73
+ ) : (
74
+ < ConfirmTenantChange fieldLabel = { args . field . label } fieldPath = { args . path } />
75
+ ) }
76
+ </ >
73
77
)
74
78
}
75
79
76
80
return null
77
81
}
82
+
83
+ const confirmSwitchTenantSlug = 'confirm-switch-tenant'
84
+
85
+ const ConfirmTenantChange = ( {
86
+ fieldLabel,
87
+ fieldPath,
88
+ } : {
89
+ fieldLabel ?: StaticLabel
90
+ fieldPath : string
91
+ } ) => {
92
+ const { options, selectedTenantID, setTenant } = useTenantSelection ( )
93
+ const { setValue : setTenantFormValue , value : tenantFormValue } = useField < null | number | string > (
94
+ { path : fieldPath } ,
95
+ )
96
+ const { setModified } = useForm ( )
97
+ const modified = useFormModified ( )
98
+ const { i18n, t } = useTranslation <
99
+ PluginMultiTenantTranslations ,
100
+ PluginMultiTenantTranslationKeys
101
+ > ( )
102
+ const { isModalOpen, openModal } = useModal ( )
103
+
104
+ const prevTenantValueRef = React . useRef < null | number | string > ( tenantFormValue || null )
105
+ const [ tenantToConfirm , setTenantToConfirm ] = React . useState < null | number | string > (
106
+ tenantFormValue || null ,
107
+ )
108
+
109
+ const fromTenantOption = React . useMemo ( ( ) => {
110
+ if ( tenantFormValue ) {
111
+ return options . find ( ( option ) => option . value === tenantFormValue )
112
+ }
113
+ return undefined
114
+ } , [ options , tenantFormValue ] )
115
+
116
+ const toTenantOption = React . useMemo ( ( ) => {
117
+ if ( tenantToConfirm ) {
118
+ return options . find ( ( option ) => option . value === tenantToConfirm )
119
+ }
120
+ return undefined
121
+ } , [ options , tenantToConfirm ] )
122
+
123
+ const modalIsOpen = isModalOpen ( confirmSwitchTenantSlug )
124
+ const testRef = React . useRef < boolean > ( false )
125
+
126
+ React . useEffect ( ( ) => {
127
+ // the form value changed
128
+ if (
129
+ ! modalIsOpen &&
130
+ tenantFormValue &&
131
+ prevTenantValueRef . current &&
132
+ tenantFormValue !== prevTenantValueRef . current
133
+ ) {
134
+ // revert the form value change temporarily
135
+ setTenantFormValue ( prevTenantValueRef . current , true )
136
+ // save the tenant to confirm in modal
137
+ setTenantToConfirm ( tenantFormValue )
138
+ // open confirmation modal
139
+ openModal ( confirmSwitchTenantSlug )
140
+ }
141
+ } , [
142
+ tenantFormValue ,
143
+ setTenantFormValue ,
144
+ openModal ,
145
+ setTenant ,
146
+ selectedTenantID ,
147
+ modalIsOpen ,
148
+ modified ,
149
+ ] )
150
+
151
+ return (
152
+ < ConfirmationModal
153
+ body = {
154
+ < Translation
155
+ elements = { {
156
+ 0 : ( { children } ) => {
157
+ return < b > { children } </ b >
158
+ } ,
159
+ } }
160
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
161
+ // @ts -expect-error
162
+ i18nKey = "plugin-multi-tenant:confirm-modal-tenant-switch--body"
163
+ t = { t }
164
+ variables = { {
165
+ fromTenant : fromTenantOption ?. label ,
166
+ toTenant : toTenantOption ?. label ,
167
+ } }
168
+ />
169
+ }
170
+ heading = { t ( 'plugin-multi-tenant:confirm-modal-tenant-switch--heading' , {
171
+ tenantLabel : fieldLabel
172
+ ? getTranslation ( fieldLabel , i18n )
173
+ : t ( 'plugin-multi-tenant:nav-tenantSelector-label' ) ,
174
+ } ) }
175
+ modalSlug = { confirmSwitchTenantSlug }
176
+ onCancel = { ( ) => {
177
+ setModified ( testRef . current )
178
+ } }
179
+ onConfirm = { ( ) => {
180
+ // set the form value to the tenant to confirm
181
+ prevTenantValueRef . current = tenantToConfirm
182
+ setTenantFormValue ( tenantToConfirm )
183
+ } }
184
+ />
185
+ )
186
+ }
187
+
188
+ /**
189
+ * Tells the global selector when the form has been modified
190
+ * so it can display the "Leave without saving" confirmation modal
191
+ * if modified and attempting to change the tenant
192
+ */
193
+ const SyncFormModified = ( ) => {
194
+ const modified = useFormModified ( )
195
+ const { setModified } = useTenantSelection ( )
196
+
197
+ React . useEffect ( ( ) => {
198
+ setModified ( modified )
199
+ } , [ modified , setModified ] )
200
+
201
+ return null
202
+ }
0 commit comments