JFIFxxC      C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr{ gilour

File "menu-item-form.tsx"

Full Path: /home/markqprx/iniasli.pro/resources/client/admin/menus/menu-item-form.tsx
File size: 8.06 KB
MIME-type: text/plain
Charset: utf-8

import {FormTextField} from '../../ui/forms/input-field/text-field/text-field';
import {Trans} from '../../i18n/trans';
import {useValueLists} from '../../http/value-lists';
import {useTrans} from '../../i18n/use-trans';
import {FormChipField} from '../../ui/forms/input-field/chip-field/form-chip-field';
import {Item} from '../../ui/forms/listbox/item';
import {Fragment, useEffect, useMemo} from 'react';
import {
  buildPermissionList,
  prettyName,
} from '../../auth/ui/permission-selector';
import {Section} from '../../ui/forms/listbox/section';
import {useFormContext} from 'react-hook-form';
import {MenuItemConfig} from '../../core/settings/settings';
import {FormSelect, Option} from '../../ui/forms/select/select';
import {useAvailableRoutes} from '../appearance/sections/menus/hooks/available-routes';
import {ButtonBaseProps} from '../../ui/buttons/button-base';
import {createSvgIconFromTree, IconTree} from '../../icons/create-svg-icon';
import {DialogTrigger} from '../../ui/overlays/dialog/dialog-trigger';
import {IconButton} from '../../ui/buttons/icon-button';
import {EditIcon} from '../../icons/material/Edit';
import {IconPickerDialog} from '../../ui/icon-picker/icon-picker-dialog';
import {message} from '../../i18n/message';
import {usePrevious} from '../../utils/hooks/use-previous';

interface NameProps {
  prefixName: (name: string) => string;
}

interface MenuItemFormProps {
  formPathPrefix?: string;
  hideRoleAndPermissionFields?: boolean;
}
export function MenuItemForm({
  formPathPrefix,
  hideRoleAndPermissionFields,
}: MenuItemFormProps) {
  const {trans} = useTrans();
  const prefixName = (name: string): string => {
    return formPathPrefix ? `${formPathPrefix}.${name}` : name;
  };

  return (
    <Fragment>
      <FormTextField
        className="mb-20"
        name={prefixName('label')}
        label={<Trans message="Label" />}
        placeholder={trans(message('No label...'))}
        startAppend={<IconDialogTrigger prefixName={prefixName} />}
      />
      <DestinationSelector prefixName={prefixName} />
      {!hideRoleAndPermissionFields && (
        <Fragment>
          <RoleSelector prefixName={prefixName} />
          <PermissionSelector prefixName={prefixName} />
        </Fragment>
      )}
      <TargetSelect prefixName={prefixName} />
    </Fragment>
  );
}

interface IconDialogTriggerProps extends ButtonBaseProps, NameProps {}
function IconDialogTrigger({
  prefixName,
  ...buttonProps
}: IconDialogTriggerProps) {
  const {watch, setValue} = useFormContext<MenuItemConfig>();
  const fieldName = prefixName('icon') as 'icon';
  const watchedItemIcon = watch(fieldName);
  const Icon = watchedItemIcon && createSvgIconFromTree(watchedItemIcon);
  return (
    <DialogTrigger
      type="modal"
      onClose={(iconTree?: IconTree[] | null) => {
        // null will be set explicitly if icon is cleared via icon picker
        if (iconTree || iconTree === null) {
          setValue(fieldName, iconTree, {
            shouldDirty: true,
          });
        }
      }}
    >
      <IconButton
        className="text-muted icon-sm"
        variant="outline"
        size="md"
        {...buttonProps}
      >
        {Icon ? <Icon /> : <EditIcon />}
      </IconButton>
      <IconPickerDialog />
    </DialogTrigger>
  );
}

function DestinationSelector({prefixName}: NameProps) {
  const form = useFormContext<MenuItemConfig>();
  const currentType = form.watch(prefixName('type') as 'type');
  const previousType = usePrevious(currentType);
  const {data} = useValueLists(['menuItemCategories']);
  const categories = data?.menuItemCategories || [];
  const selectedCategory = categories.find(c => c.type === currentType);
  const {trans} = useTrans();
  const routeItems = useAvailableRoutes();

  // clear "action" field when "type" field changes
  useEffect(() => {
    if (previousType && previousType !== currentType) {
      form.setValue(prefixName('action') as 'action', '');
    }
  }, [currentType, previousType, form, prefixName]);

  return (
    <Fragment>
      <FormSelect
        className="mb-20"
        name={prefixName('type')}
        selectionMode="single"
        label={<Trans message="Type" />}
      >
        <Option value="link">
          <Trans message="Custom link" />
        </Option>
        <Option value="route">
          <Trans message="Site page" />
        </Option>
        {categories.map(category => (
          <Option key={category.type} value={category.type}>
            {category.name}
          </Option>
        ))}
      </FormSelect>
      {currentType === 'link' && (
        <FormTextField
          className="mb-20"
          required
          type="url"
          name={prefixName('action')}
          placeholder={trans({message: 'Enter a url...'})}
          label={<Trans message="Url" />}
        />
      )}
      {currentType === 'route' && (
        <FormSelect
          className="mb-20"
          required
          items={routeItems}
          name={prefixName('action')}
          label={<Trans message="Page" />}
          searchPlaceholder={trans(message('Search pages'))}
          showSearchField
          selectionMode="single"
        >
          {item => (
            <Item value={item.id} key={item.id}>
              {item.label}
            </Item>
          )}
        </FormSelect>
      )}
      {selectedCategory && (
        <FormSelect
          className="mb-20"
          required
          items={selectedCategory.items}
          name={prefixName('action')}
          showSearchField
          searchPlaceholder={trans(message('Search...'))}
          selectionMode="single"
          label={<Trans message={selectedCategory.name} />}
        >
          {item => (
            <Item value={item.action}>
              <Trans message={item.label} />
            </Item>
          )}
        </FormSelect>
      )}
    </Fragment>
  );
}

function RoleSelector({prefixName}: NameProps) {
  const {data} = useValueLists(['roles', 'permissions']);
  const roles = data?.roles || [];
  const {trans} = useTrans();

  return (
    <FormChipField
      className="mb-20"
      placeholder={trans({message: 'Add role...'})}
      label={<Trans message="Only show if user has role" />}
      name={prefixName('roles')}
      chipSize="sm"
      suggestions={roles}
      valueKey="id"
      displayWith={c => roles.find(r => r.id === c.id)?.name}
    >
      {role => (
        <Item value={role.id} key={role.id} capitalizeFirst>
          <Trans message={role.name} />
        </Item>
      )}
    </FormChipField>
  );
}

function PermissionSelector({prefixName}: NameProps) {
  const {data} = useValueLists(['roles', 'permissions']);
  const {trans} = useTrans();

  const groupedPermissions = useMemo(() => {
    return buildPermissionList(data?.permissions || [], [], false);
  }, [data?.permissions]);

  return (
    <FormChipField
      label={<Trans message="Only show if user has permissions" />}
      placeholder={trans({message: 'Add permission...'})}
      chipSize="sm"
      suggestions={groupedPermissions}
      name={prefixName('permissions')}
      valueKey="name"
    >
      {({groupName, items}) => (
        <Section label={prettyName(groupName)} key={groupName}>
          {items.map(permission => (
            <Item
              key={permission.name}
              value={permission.name}
              description={<Trans message={permission.description} />}
            >
              <Trans message={permission.display_name || permission.name} />
            </Item>
          ))}
        </Section>
      )}
    </FormChipField>
  );
}

function TargetSelect({prefixName}: NameProps) {
  const form = useFormContext<MenuItemConfig>();
  const watchedType = form.watch(prefixName('type') as 'type');

  // routes and pages can only be "_self"
  if (watchedType !== 'link') {
    return null;
  }

  return (
    <FormSelect
      className="mt-20"
      selectionMode="single"
      name={prefixName('target')}
      label={<Trans message="Open link in" />}
    >
      <Option value="_self">
        <Trans message="Same window" />
      </Option>
      <Option value="_blank">
        <Trans message="New window" />
      </Option>
    </FormSelect>
  );
}