1
- import { useMemo , useEffect } from 'react' ;
1
+ import * as Ariakit from '@ariakit/react' ;
2
+ import { useMemo , useEffect , useState } from 'react' ;
2
3
import { ShieldEllipsis } from 'lucide-react' ;
3
4
import { useForm , Controller } from 'react-hook-form' ;
4
5
import { Permissions , SystemRoles , roleDefaults , PermissionTypes } from 'librechat-data-provider' ;
5
6
import type { Control , UseFormSetValue , UseFormGetValues } from 'react-hook-form' ;
6
7
import { OGDialog , OGDialogTitle , OGDialogContent , OGDialogTrigger } from '~/components/ui' ;
7
8
import { useUpdatePromptPermissionsMutation } from '~/data-provider' ;
8
9
import { useLocalize , useAuthContext } from '~/hooks' ;
9
- import { Button , Switch } from '~/components/ui' ;
10
+ import { Button , Switch , DropdownPopup } from '~/components/ui' ;
10
11
import { useToastContext } from '~/Providers' ;
11
12
12
13
type FormValues = Record < Permissions , boolean > ;
@@ -19,8 +20,6 @@ type LabelControllerProps = {
19
20
getValues : UseFormGetValues < FormValues > ;
20
21
} ;
21
22
22
- const defaultValues = roleDefaults [ SystemRoles . USER ] ;
23
-
24
23
const LabelController : React . FC < LabelControllerProps > = ( {
25
24
control,
26
25
promptPerm,
@@ -32,7 +31,6 @@ const LabelController: React.FC<LabelControllerProps> = ({
32
31
< button
33
32
className = "cursor-pointer select-none"
34
33
type = "button"
35
- // htmlFor={promptPerm}
36
34
onClick = { ( ) =>
37
35
setValue ( promptPerm , ! getValues ( promptPerm ) , {
38
36
shouldDirty : true ,
@@ -70,6 +68,16 @@ const AdminSettings = () => {
70
68
} ,
71
69
} ) ;
72
70
71
+ const [ isRoleMenuOpen , setIsRoleMenuOpen ] = useState ( false ) ;
72
+ const [ selectedRole , setSelectedRole ] = useState < SystemRoles > ( SystemRoles . USER ) ;
73
+
74
+ const defaultValues = useMemo ( ( ) => {
75
+ if ( roles ?. [ selectedRole ] ) {
76
+ return roles [ selectedRole ] [ PermissionTypes . PROMPTS ] ;
77
+ }
78
+ return roleDefaults [ selectedRole ] [ PermissionTypes . PROMPTS ] ;
79
+ } , [ roles , selectedRole ] ) ;
80
+
73
81
const {
74
82
reset,
75
83
control,
@@ -79,20 +87,16 @@ const AdminSettings = () => {
79
87
formState : { isSubmitting } ,
80
88
} = useForm < FormValues > ( {
81
89
mode : 'onChange' ,
82
- defaultValues : useMemo ( ( ) => {
83
- if ( roles ?. [ SystemRoles . USER ] ) {
84
- return roles [ SystemRoles . USER ] [ PermissionTypes . PROMPTS ] ;
85
- }
86
-
87
- return defaultValues [ PermissionTypes . PROMPTS ] ;
88
- } , [ roles ] ) ,
90
+ defaultValues,
89
91
} ) ;
90
92
91
93
useEffect ( ( ) => {
92
- if ( roles ?. [ SystemRoles . USER ] ?. [ PermissionTypes . PROMPTS ] ) {
93
- reset ( roles [ SystemRoles . USER ] [ PermissionTypes . PROMPTS ] ) ;
94
+ if ( roles ?. [ selectedRole ] ?. [ PermissionTypes . PROMPTS ] ) {
95
+ reset ( roles [ selectedRole ] [ PermissionTypes . PROMPTS ] ) ;
96
+ } else {
97
+ reset ( roleDefaults [ selectedRole ] [ PermissionTypes . PROMPTS ] ) ;
94
98
}
95
- } , [ roles , reset ] ) ;
99
+ } , [ roles , selectedRole , reset ] ) ;
96
100
97
101
if ( user ?. role !== SystemRoles . ADMIN ) {
98
102
return null ;
@@ -103,20 +107,35 @@ const AdminSettings = () => {
103
107
promptPerm : Permissions . SHARED_GLOBAL ,
104
108
label : localize ( 'com_ui_prompts_allow_share_global' ) ,
105
109
} ,
106
- {
107
- promptPerm : Permissions . USE ,
108
- label : localize ( 'com_ui_prompts_allow_use' ) ,
109
- } ,
110
110
{
111
111
promptPerm : Permissions . CREATE ,
112
112
label : localize ( 'com_ui_prompts_allow_create' ) ,
113
113
} ,
114
+ {
115
+ promptPerm : Permissions . USE ,
116
+ label : localize ( 'com_ui_prompts_allow_use' ) ,
117
+ } ,
114
118
] ;
115
119
116
120
const onSubmit = ( data : FormValues ) => {
117
- mutate ( { roleName : SystemRoles . USER , updates : data } ) ;
121
+ mutate ( { roleName : selectedRole , updates : data } ) ;
118
122
} ;
119
123
124
+ const roleDropdownItems = [
125
+ {
126
+ label : SystemRoles . USER ,
127
+ onClick : ( ) => {
128
+ setSelectedRole ( SystemRoles . USER ) ;
129
+ } ,
130
+ } ,
131
+ {
132
+ label : SystemRoles . ADMIN ,
133
+ onClick : ( ) => {
134
+ setSelectedRole ( SystemRoles . ADMIN ) ;
135
+ } ,
136
+ } ,
137
+ ] ;
138
+
120
139
return (
121
140
< OGDialog >
122
141
< OGDialogTrigger asChild >
@@ -129,33 +148,70 @@ const AdminSettings = () => {
129
148
< span className = "hidden sm:flex" > { localize ( 'com_ui_admin' ) } </ span >
130
149
</ Button >
131
150
</ OGDialogTrigger >
132
- < OGDialogContent className = "bg-white dark:border-gray-700 dark:bg-gray-850 dark:text-gray-300" >
133
- < OGDialogTitle > { `${ localize ( 'com_ui_admin_settings' ) } - ${ localize (
134
- 'com_ui_prompts' ,
135
- ) } `} </ OGDialogTitle >
136
- < form className = "p-2" onSubmit = { handleSubmit ( onSubmit ) } >
137
- < div className = "py-5" >
138
- { labelControllerData . map ( ( { promptPerm, label } ) => (
139
- < LabelController
140
- key = { promptPerm }
141
- control = { control }
142
- promptPerm = { promptPerm }
143
- label = { label }
144
- getValues = { getValues }
145
- setValue = { setValue }
146
- />
147
- ) ) }
148
- </ div >
149
- < div className = "flex justify-end" >
150
- < button
151
- type = "submit"
152
- disabled = { isSubmitting || isLoading }
153
- className = "btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
154
- >
155
- { localize ( 'com_ui_save' ) }
156
- </ button >
151
+ < OGDialogContent className = "w-1/4 border-border-light bg-surface-primary text-text-primary" >
152
+ < OGDialogTitle >
153
+ { `${ localize ( 'com_ui_admin_settings' ) } - ${ localize ( 'com_ui_prompts' ) } ` }
154
+ </ OGDialogTitle >
155
+ < div className = "p-2" >
156
+ { /* Role selection dropdown */ }
157
+ < div className = "flex items-center gap-2" >
158
+ < span className = "font-medium" > { localize ( 'com_ui_role_select' ) } :</ span >
159
+ < DropdownPopup
160
+ menuId = "prompt-role-dropdown"
161
+ isOpen = { isRoleMenuOpen }
162
+ setIsOpen = { setIsRoleMenuOpen }
163
+ trigger = {
164
+ < Ariakit . MenuButton className = "inline-flex w-1/4 items-center justify-center rounded-lg border border-border-light bg-transparent px-2 py-1 text-text-primary transition-all ease-in-out hover:bg-surface-tertiary" >
165
+ { selectedRole }
166
+ </ Ariakit . MenuButton >
167
+ }
168
+ items = { roleDropdownItems }
169
+ className = "border border-border-light bg-surface-primary"
170
+ itemClassName = "hover:bg-surface-tertiary items-center justify-center"
171
+ sameWidth = { true }
172
+ />
157
173
</ div >
158
- </ form >
174
+ < form onSubmit = { handleSubmit ( onSubmit ) } >
175
+ < div className = "py-5" >
176
+ { labelControllerData . map ( ( { promptPerm, label } ) => (
177
+ < div key = { promptPerm } >
178
+ < LabelController
179
+ control = { control }
180
+ promptPerm = { promptPerm }
181
+ label = { label }
182
+ getValues = { getValues }
183
+ setValue = { setValue }
184
+ />
185
+ { selectedRole === SystemRoles . ADMIN && promptPerm === Permissions . USE && (
186
+ < >
187
+ < div className = "mb-2 max-w-full whitespace-normal break-words text-sm text-red-600" >
188
+ < span > { localize ( 'com_ui_admin_access_warning' ) } </ span >
189
+ { '\n' }
190
+ < a
191
+ href = "https://www.librechat.ai/docs/configuration/librechat_yaml/object_structure/interface"
192
+ target = "_blank"
193
+ rel = "noreferrer"
194
+ className = "text-blue-500 underline"
195
+ >
196
+ { localize ( 'com_ui_more_info' ) }
197
+ </ a >
198
+ </ div >
199
+ </ >
200
+ ) }
201
+ </ div >
202
+ ) ) }
203
+ </ div >
204
+ < div className = "flex justify-end" >
205
+ < button
206
+ type = "submit"
207
+ disabled = { isSubmitting || isLoading }
208
+ className = "btn rounded bg-green-500 font-bold text-white transition-all hover:bg-green-600"
209
+ >
210
+ { localize ( 'com_ui_save' ) }
211
+ </ button >
212
+ </ div >
213
+ </ form >
214
+ </ div >
159
215
</ OGDialogContent >
160
216
</ OGDialog >
161
217
) ;
0 commit comments