|
1 | 1 | "use client"; |
2 | 2 |
|
3 | | -import * as React from "react"; |
4 | | -import { Check, ChevronsUpDown, Lock, X } from "lucide-react"; |
5 | 3 | import type { Role } from "@prisma/client"; |
| 4 | +import * as React from "react"; |
6 | 5 |
|
7 | | -import { cn } from "@comp/ui/cn"; |
8 | 6 | import { useI18n } from "@/locales/client"; |
9 | | -import { Badge } from "@comp/ui/badge"; |
10 | | -import { Button } from "@comp/ui/button"; |
11 | | -import { |
12 | | - Command, |
13 | | - CommandEmpty, |
14 | | - CommandGroup, |
15 | | - CommandInput, |
16 | | - CommandItem, |
17 | | - CommandList, |
18 | | -} from "@comp/ui/command"; |
19 | | -import { Popover, PopoverContent, PopoverTrigger } from "@comp/ui/popover"; |
20 | | -import { |
21 | | - Tooltip, |
22 | | - TooltipContent, |
23 | | - TooltipProvider, |
24 | | - TooltipTrigger, |
25 | | -} from "@comp/ui/tooltip"; |
| 7 | +import { Dialog, DialogContent } from "@comp/ui/dialog"; |
| 8 | +import { MultiRoleComboboxContent } from "./MultiRoleComboboxContent"; |
| 9 | +import { MultiRoleComboboxTrigger } from "./MultiRoleComboboxTrigger"; |
26 | 10 |
|
27 | 11 | // Define the selectable roles explicitly (exclude owner) |
28 | 12 | const selectableRoles: { |
@@ -148,190 +132,33 @@ export function MultiRoleCombobox({ |
148 | 132 | return label.toLowerCase().includes(searchTerm.toLowerCase()); |
149 | 133 | }); |
150 | 134 |
|
151 | | - return ( |
152 | | - <Popover open={open} onOpenChange={setOpen} modal={false}> |
153 | | - <PopoverTrigger asChild> |
154 | | - <Button |
155 | | - variant="outline" |
156 | | - role="combobox" |
157 | | - aria-expanded={open} |
158 | | - className="w-full justify-between min-h-[40px] h-auto shadow-none" |
159 | | - disabled={disabled} |
160 | | - > |
161 | | - <div className="flex flex-wrap gap-1 items-center"> |
162 | | - {selectedRoles.length === 0 && ( |
163 | | - <span className="text-muted-foreground text-sm"> |
164 | | - {triggerText} |
165 | | - </span> |
166 | | - )} |
167 | | - {selectedRoles.map((role) => ( |
168 | | - <Badge |
169 | | - key={role} |
170 | | - variant="secondary" |
171 | | - className={cn( |
172 | | - "text-xs", |
173 | | - lockedRoles.includes(role) && |
174 | | - "border border-primary", |
175 | | - )} |
176 | | - onClick={(e) => { |
177 | | - e.stopPropagation(); // Prevent popover |
178 | | - handleSelect(role); |
179 | | - }} |
180 | | - > |
181 | | - {getRoleLabel(role)} |
182 | | - {!lockedRoles.includes(role) ? ( |
183 | | - <X className="ml-1 h-3 w-3 cursor-pointer" /> |
184 | | - ) : ( |
185 | | - <TooltipProvider> |
186 | | - <Tooltip> |
187 | | - <TooltipTrigger asChild> |
188 | | - <Lock className="ml-1 h-3 w-3 text-primary" /> |
189 | | - </TooltipTrigger> |
190 | | - <TooltipContent> |
191 | | - <p> |
192 | | - {t( |
193 | | - "people.member_actions.role_dialog.owner_note", |
194 | | - )} |
195 | | - </p> |
196 | | - </TooltipContent> |
197 | | - </Tooltip> |
198 | | - </TooltipProvider> |
199 | | - )} |
200 | | - </Badge> |
201 | | - ))} |
202 | | - </div> |
203 | | - <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> |
204 | | - </Button> |
205 | | - </PopoverTrigger> |
206 | | - <PopoverContent className="w-[--radix-popover-trigger-width] p-0"> |
207 | | - <Command> |
208 | | - <CommandInput |
209 | | - placeholder="Search..." |
210 | | - value={searchTerm} |
211 | | - onValueChange={setSearchTerm} |
| 135 | + return ( |
| 136 | + <> |
| 137 | + <MultiRoleComboboxTrigger |
| 138 | + selectedRoles={selectedRoles} |
| 139 | + lockedRoles={lockedRoles} |
| 140 | + triggerText={triggerText} |
| 141 | + disabled={disabled} |
| 142 | + handleSelect={handleSelect} // For badge clicks |
| 143 | + getRoleLabel={getRoleLabel} |
| 144 | + t={t} |
| 145 | + onClick={() => setOpen(true)} |
| 146 | + ariaExpanded={open} |
| 147 | + /> |
| 148 | + <Dialog open={open} onOpenChange={setOpen}> |
| 149 | + <DialogContent className="p-0"> |
| 150 | + <MultiRoleComboboxContent |
| 151 | + searchTerm={searchTerm} |
| 152 | + setSearchTerm={setSearchTerm} |
| 153 | + t={t} |
| 154 | + filteredRoles={filteredRoles} |
| 155 | + handleSelect={handleSelect} // For item selection |
| 156 | + lockedRoles={lockedRoles} |
| 157 | + selectedRoles={selectedRoles} |
| 158 | + onCloseDialog={() => setOpen(false)} |
212 | 159 | /> |
213 | | - <CommandList> |
214 | | - <CommandEmpty> |
215 | | - {t("people.empty.no_results.title")} |
216 | | - </CommandEmpty> |
217 | | - <CommandGroup> |
218 | | - {filteredRoles.map((role) => ( |
219 | | - <CommandItem |
220 | | - key={role.value} |
221 | | - value={(() => { |
222 | | - switch (role.value) { |
223 | | - case "owner": |
224 | | - return t("people.roles.owner"); |
225 | | - case "admin": |
226 | | - return t("people.roles.admin"); |
227 | | - case "auditor": |
228 | | - return t( |
229 | | - "people.roles.auditor", |
230 | | - ); |
231 | | - case "employee": |
232 | | - return t( |
233 | | - "people.roles.employee", |
234 | | - ); |
235 | | - default: |
236 | | - return role.value; |
237 | | - } |
238 | | - })()} // Use label for search |
239 | | - onSelect={() => { |
240 | | - handleSelect(role.value); |
241 | | - }} |
242 | | - disabled={ |
243 | | - role.value === "owner" || // Always disable the owner role |
244 | | - (lockedRoles.includes(role.value) && |
245 | | - selectedRoles.includes(role.value)) // Disable any locked roles |
246 | | - } |
247 | | - className={cn( |
248 | | - "flex flex-col items-start py-2 cursor-pointer", // Adjust padding and alignment |
249 | | - lockedRoles.includes(role.value) && |
250 | | - selectedRoles.includes( |
251 | | - role.value, |
252 | | - ) && |
253 | | - "bg-muted/50 text-muted-foreground", |
254 | | - )} |
255 | | - > |
256 | | - <div className="flex w-full items-center"> |
257 | | - {" "} |
258 | | - {/* Wrap label and check */} |
259 | | - <Check |
260 | | - className={cn( |
261 | | - "mr-2 h-4 w-4 flex-shrink-0", // Ensure check doesn't shrink |
262 | | - selectedRoles.includes( |
263 | | - role.value, |
264 | | - ) |
265 | | - ? "opacity-100" |
266 | | - : "opacity-0", |
267 | | - )} |
268 | | - /> |
269 | | - <span className="flex-grow"> |
270 | | - {" "} |
271 | | - {/* Allow label to take space */} |
272 | | - {(() => { |
273 | | - switch (role.value) { |
274 | | - case "owner": |
275 | | - return t( |
276 | | - "people.roles.owner", |
277 | | - ); |
278 | | - case "admin": |
279 | | - return t( |
280 | | - "people.roles.admin", |
281 | | - ); |
282 | | - case "auditor": |
283 | | - return t( |
284 | | - "people.roles.auditor", |
285 | | - ); |
286 | | - case "employee": |
287 | | - return t( |
288 | | - "people.roles.employee", |
289 | | - ); |
290 | | - default: |
291 | | - return role.value; |
292 | | - } |
293 | | - })()} |
294 | | - </span> |
295 | | - {lockedRoles.includes(role.value) && |
296 | | - selectedRoles.includes( |
297 | | - role.value, |
298 | | - ) && ( |
299 | | - <span className="ml-auto text-xs text-muted-foreground pl-2 flex-shrink-0"> |
300 | | - (Locked) |
301 | | - </span> |
302 | | - )} |
303 | | - </div> |
304 | | - {/* Add description below */} |
305 | | - <div className="ml-6 text-xs text-muted-foreground mt-1"> |
306 | | - {(() => { |
307 | | - switch (role.value) { |
308 | | - case "owner": |
309 | | - return t( |
310 | | - "people.roles.owner_description", |
311 | | - ); |
312 | | - case "admin": |
313 | | - return t( |
314 | | - "people.roles.admin_description", |
315 | | - ); |
316 | | - case "auditor": |
317 | | - return t( |
318 | | - "people.roles.auditor_description", |
319 | | - ); |
320 | | - case "employee": |
321 | | - return t( |
322 | | - "people.roles.employee_description", |
323 | | - ); |
324 | | - default: |
325 | | - return ""; |
326 | | - } |
327 | | - })()} |
328 | | - </div> |
329 | | - </CommandItem> |
330 | | - ))} |
331 | | - </CommandGroup> |
332 | | - </CommandList> |
333 | | - </Command> |
334 | | - </PopoverContent> |
335 | | - </Popover> |
| 160 | + </DialogContent> |
| 161 | + </Dialog> |
| 162 | + </> |
336 | 163 | ); |
337 | 164 | } |
0 commit comments