import {useControlledState} from '@react-stately/utils'; import React, {Fragment, useState} from 'react'; import {useController} from 'react-hook-form'; import {mergeProps} from '@react-aria/utils'; import clsx from 'clsx'; import {produce} from 'immer'; import {Permission, PermissionRestriction} from '../permission'; import {useValueLists} from '../../http/value-lists'; import {ucFirst} from '../../utils/string/uc-first'; import {Accordion, AccordionItem} from '../../ui/accordion/accordion'; import {List, ListItem} from '../../ui/list/list'; import {Switch} from '../../ui/forms/toggle/switch'; import {TextField} from '../../ui/forms/input-field/text-field/text-field'; import {DoneAllIcon} from '../../icons/material/DoneAll'; import {Trans} from '../../i18n/trans'; interface PermissionSelectorProps { value?: Permission[]; onChange?: (value: Permission[]) => void; valueListKey?: 'permissions' | 'workspacePermissions'; } export const PermissionSelector = React.forwardRef< HTMLDivElement, PermissionSelectorProps >(({valueListKey = 'permissions', ...props}, ref) => { const {data} = useValueLists([valueListKey]); const permissions = data?.permissions || data?.workspacePermissions; const [value, setValue] = useControlledState(props.value, [], props.onChange); const [showAdvanced, setShowAdvanced] = useState(false); if (!permissions) return null; const groupedPermissions = buildPermissionList( permissions, value, showAdvanced ); const onRestrictionChange = (newPermission: Permission) => { const newValue = [...value]; const index = newValue.findIndex(p => p.id === newPermission.id); if (index > -1) { newValue.splice(index, 1, newPermission); } setValue(newValue); }; return ( {groupedPermissions.map(({groupName, items, anyChecked}) => ( } key={groupName} startIcon={anyChecked ? : undefined} > {items.map(permission => { const index = value.findIndex(v => v.id === permission.id); const isChecked = index > -1; return (
{ if (isChecked) { const newValue = [...value]; newValue.splice(index, 1); setValue(newValue); } else { setValue([...value, permission]); } }} endSection={ {}} /> } description={} > {isChecked && ( )}
); })}
))}
{ setShowAdvanced(e.target.checked); }} >
); }); interface RestrictionsProps { permission: Permission; onChange?: (newPermission: Permission) => void; } function Restrictions({permission, onChange}: RestrictionsProps) { if (!permission?.restrictions?.length) return null; const setRestrictionValue = ( name: string, value: PermissionRestriction['value'] ) => { const nextState = produce(permission, draftState => { const restriction = draftState.restrictions.find(r => r.name === name); if (restriction) { restriction.value = value; } }); onChange?.(nextState); }; return (
{permission.restrictions.map((restriction, index) => { const isLast = index === permission.restrictions.length - 1; const name = ; const description = restriction.description ? ( ) : undefined; if (restriction.type === 'bool') { return ( { setRestrictionValue(restriction.name, e.target.checked); }} > {name} ); } return ( { setRestrictionValue( restriction.name, e.target.value === '' ? undefined : parseInt(e.target.value) ); }} /> ); })}
); } export type FormChipFieldProps = PermissionSelectorProps & { name: string; }; export function FormPermissionSelector(props: FormChipFieldProps) { const { field: {onChange, value = [], ref}, } = useController({ name: props.name, }); const formProps: Partial = { onChange, value, }; return ; } export const prettyName = (name: string) => { return ucFirst(name.replace('_', ' ')); }; interface PermissionGroup { groupName: string; anyChecked: boolean; items: Permission[]; } // merge "restrictions" from selected value into all permissions to make // it easier to bind restriction values to form inputs export function buildPermissionList( allPermissions: Permission[], selectedPermissions: Permission[], showAdvanced: boolean ) { const groupedPermissions: PermissionGroup[] = []; allPermissions.forEach(permission => { const index = selectedPermissions.findIndex(p => p.id === permission.id); if (!showAdvanced && permission.advanced) return; let group: PermissionGroup | undefined = groupedPermissions.find( g => g.groupName === permission.group ); if (!group) { group = {groupName: permission.group, anyChecked: false, items: []}; groupedPermissions.push(group); } if (index > -1) { const mergedPermission = { ...permission, restrictions: mergeRestrictions( permission.restrictions, selectedPermissions[index].restrictions ), }; group.anyChecked = true; group.items.push(mergedPermission); } else { group.items.push(permission); } }); return groupedPermissions; } function mergeRestrictions( allRestrictions: PermissionRestriction[], selectedRestrictions: PermissionRestriction[] ): PermissionRestriction[] { return allRestrictions?.map(restriction => { const selected = selectedRestrictions.find( r => r.name === restriction.name ); if (selected) { return {...restriction, value: selected.value}; } else { return restriction; } }); }