UNPKG

@elastic-suite/gally-admin-components

Version:
1 lines 1.01 MB
{"version":3,"sources":["../src/components/atoms/Alert/Alert.tsx","../src/components/atoms/IonIcon/IonIcon.tsx","../src/components/atoms/boost/Boost.tsx","../src/components/atoms/breadcrumb/Breadcrumb.tsx","../src/components/atoms/breadcrumb/Breadcrumbs.tsx","../src/components/atoms/Chip/Chip.tsx","../src/components/atoms/closeComponent/CloseComponent.tsx","../src/components/atoms/form/CheckboxWithoutError.tsx","../src/components/atoms/form/Checkbox.tsx","../src/hooks/useApi.ts","../src/hooks/useLog.ts","../src/hooks/useResource.ts","../src/store/data.ts","../src/store/hooks.ts","../src/store/i18n.ts","../src/store/menu.ts","../src/store/store.ts","../src/store/user.ts","../src/hooks/useApiFilters.ts","../src/hooks/useApiTable.ts","../src/hooks/useApiForm.ts","../src/hooks/useCatalogs.ts","../src/hooks/useDataLoader.ts","../src/hooks/useFilters.ts","../src/hooks/useFormError.ts","../src/hooks/useGraphql.ts","../src/hooks/useIsHorizontalOverflow.ts","../src/hooks/useOptions.ts","../src/hooks/useSingletonLoader.ts","../src/hooks/useRuleEngineGraphqlFilters.ts","../src/hooks/useRuleOperators.ts","../src/hooks/useTabs.ts","../src/hooks/useUser.ts","../src/hooks/useFormValdation.ts","../src/components/atoms/form/DatePickerWithoutError.tsx","../src/components/atoms/form/InputTextWithoutError.tsx","../src/services/category.ts","../src/services/customTable.ts","../src/services/form.ts","../src/services/row.ts","../src/components/atoms/form/InfoTooltip.tsx","../src/components/atoms/modals/Tooltip.tsx","../src/components/atoms/form/InputText.styled.ts","../src/components/atoms/form/DatePicker.tsx","../src/components/atoms/form/DoubleDatePickerWithoutError.tsx","../src/components/atoms/form/FormControl.tsx","../src/components/atoms/form/DoubleDatePicker.tsx","../src/components/atoms/form/DropDownWithoutError.tsx","../src/components/atoms/form/DropDown.styled.tsx","../src/components/atoms/form/DropDown.tsx","../src/components/atoms/form/InputText.tsx","../src/components/atoms/form/RadioGroupWithoutError.tsx","../src/components/atoms/form/RadioGroup.tsx","../src/components/atoms/form/Range.tsx","../src/components/atoms/form/RangeWithoutError.tsx","../src/components/atoms/form/Range.styled.tsx","../src/components/atoms/form/Slider.tsx","../src/components/atoms/form/Slider.styled.tsx","../src/components/atoms/form/Switch.tsx","../src/components/atoms/form/Switch.styled.tsx","../src/components/atoms/form/TextareaWithoutError.tsx","../src/components/atoms/form/Textarea.tsx","../src/components/atoms/form/TreeSelectorWithoutError.tsx","../src/components/atoms/tree/Tree.tsx","../src/components/atoms/tree/Tree.styled.ts","../src/components/atoms/form/TreeSelector.styled.tsx","../src/components/atoms/form/TreeSelector.tsx","../src/components/atoms/format/FormatText.tsx","../src/components/atoms/help/Help.tsx","../src/components/atoms/help/HelpOver.tsx","../src/components/atoms/menu/MenuItem.tsx","../src/components/atoms/menu/MenuItemIcon.tsx","../src/components/atoms/modals/PopIn.tsx","../src/hooks/usePopin.ts","../src/components/atoms/modals/CustomDialog.tsx","../src/components/atoms/modals/PopIn.styled.ts","../src/components/atoms/buttons/Button.tsx","../src/components/atoms/buttons/Button.styled.tsx","../src/components/atoms/noAttributes/NoAttributes.tsx","../src/components/stateful-pages/Grid/Grid.tsx","../src/components/stateful-pages/ResourceTable/ResourceTable.tsx","../src/components/stateful/FieldGuesser/FieldGuesser.tsx","../src/components/stateful/FieldGuesser/EditableFieldGuesser.tsx","../src/components/stateful/FieldGuesser/ReadableFieldGuesser.tsx","../src/contexts/breadcrumb.ts","../src/contexts/catalog.ts","../src/contexts/i18n.ts","../src/contexts/metadata.ts","../src/contexts/options.ts","../src/contexts/ruleOptions.ts","../src/components/atoms/score/Score.tsx","../src/components/atoms/stock/Stock.tsx","../src/components/atoms/price/Price.tsx","../src/components/stateful/FieldGuesser/ReadableDropDownGuesser.tsx","../src/components/molecules/format/FormatRowArray.tsx","../src/components/atoms/format/FormatArray.tsx","../src/components/atoms/form/PreviewGridBoostConfiguration/PreviewGridBoostConfiguration.tsx","../src/components/molecules/FiltersPreviewBoostingTabs/FiltersPreviewBoostingTabs.tsx","../src/components/molecules/layout/tabs/CustomTabs.tsx","../src/components/molecules/layout/tabs/TabPanel.tsx","../src/components/molecules/layout/tabs/a11yProps.tsx","../src/components/molecules/FiltersPreviewBoostingTabs/FiltersPreviewBoostingTab.tsx","../src/components/atoms/form/Form.tsx","../src/components/atoms/form/SearchBar.tsx","../src/components/organisms/PreviewBoostingTable/PreviewBoostingTableManager.tsx","../src/components/molecules/CustomTable/Pagination/Pagination.tsx","../src/components/organisms/PreviewBoostingTable/PreviewBoostingTable.tsx","../src/components/organisms/CustomTable/CustomTable.tsx","../src/components/organisms/CustomTable/CustomTable.styled.tsx","../src/components/organisms/CustomTable/CustomTableBody/DraggableBody.tsx","../src/components/organisms/CustomTable/CustomTableRow/DraggableRow.tsx","../src/components/organisms/CustomTable/CustomTableBody/NonDraggableBody.tsx","../src/components/organisms/CustomTable/CustomTableRow/NonDraggableRow.tsx","../src/components/organisms/CustomTable/CustomTableHeader/CustomTableHeader.tsx","../src/components/atoms/image/Image.tsx","../src/components/atoms/positionEffect/PositionEffect.tsx","../src/components/stateful/FieldGuesser/EditableDropDownGuesser.tsx","../src/components/stateful/FieldGuesser/MultipleInput.tsx","../src/components/stateful/RequestTypeManager/RequestTypeManager.tsx","../src/components/atoms/form/RequestType.tsx","../src/components/atoms/form/RequestTypeItem.styled.tsx","../src/components/atoms/form/RequestTypeItem.tsx","../src/components/atoms/form/TextFieldTagsMultiple.tsx","../src/components/atoms/form/TextFieldTags.tsx","../src/components/atoms/form/TextFieldTagsWithoutError.tsx","../src/components/stateful/RulesManager/RulesManager.tsx","../src/components/atoms/rules/CombinationRules.tsx","../src/components/atoms/rules/Rule.tsx","../src/components/atoms/rules/Rule.styled.tsx","../src/components/atoms/rules/RuleLink.tsx","../src/components/stateful-providers/RuleOptionsProvider/RuleOptionsProvider.tsx","../src/components/atoms/form/Synonym.tsx","../src/components/atoms/form/Expansion.tsx","../src/components/molecules/ProportionalToAttributes/ProportionalToAttributesManager.tsx","../src/components/molecules/ProportionalToAttributes/ProportionalToAttributes.tsx","../src/components/molecules/ProportionalToAttributes/ProportionalToAttributes.styled.tsx","../src/components/stateful/FiltersGuesser/FiltersGuesser.tsx","../src/components/molecules/Filters/Filters.tsx","../src/components/molecules/Filters/Filters.styled.tsx","../src/components/stateful/TableGuesser/TableGuesser.tsx","../src/components/molecules/CustomTable/StickyBar/StickyBar.tsx","../src/components/organisms/PagerTable/PagerTable.tsx","../src/components/stateful/TableStickyBar/TableStickyBar.tsx","../src/components/atoms/PageTitle/PageTitle.tsx","../src/components/atoms/Placeholder/Placeholder.tsx","../src/components/atoms/scope/Language.tsx","../src/components/atoms/scope/PopInCatalogs.tsx","../src/components/atoms/scope/TitleScope.tsx","../src/components/atoms/scope/NbActiveLocales.tsx","../src/components/atoms/subTabs/SubTabPanel.tsx","../src/components/atoms/subTabs/SubTabs.tsx","../src/components/atoms/userMenu/UserMenu.tsx","../src/components/atoms/userMenu/UserMenuShow.tsx","../src/components/molecules/CustomTable/MassiveSelection/MassiveSelection.tsx","../src/components/molecules/CustomTable/TopProductsBanner/TopProductsBanner.tsx","../src/components/molecules/layout/appBar/AppBar.tsx","../src/components/molecules/layout/user/User.tsx","../src/components/molecules/layout/Menu/Menu.tsx","../src/components/molecules/layout/scope/ActiveLocales.tsx","../src/components/molecules/layout/scope/Catalogs.tsx","../src/components/molecules/layout/Sidebar/Sidebar.tsx","../src/components/molecules/layout/TitleBlock/TitleBlock.tsx","../src/components/molecules/layout/twoColsLayout/TwoColsLayout.tsx","../src/components/stateful/CatalogSwitcher/CatalogSwitcher.tsx","../src/components/stateful/CategoryTree/CategoryTree.tsx","../src/components/stateful/Merchandize/Merchandize.tsx","../src/components/stateful/ProductsContainer/ProductsContainer.tsx","../src/components/stateful/ProductsTopAndBottom/ProductsTopAndBottom.tsx","../src/components/stateful/TopAndBottomTable/BottomTable.tsx","../src/components/stateful/TopAndBottomTable/TopTable.tsx","../src/components/stateful/TopProductsTable/TopProductsTable.tsx","../src/components/stateful/Merchandize/SearchBar/SearchBar.tsx","../src/components/stateful/Merchandize/SearchBar/Search.styled.tsx","../src/components/stateful-layout/Layout/Layout.tsx","../src/components/stateful-pages/SettingsAttributes/SettingsAttributes.tsx","../src/components/atoms/metadata/Metadata.tsx","../src/components/stateful-pages/SettingsScope/SettingsScope.tsx","../src/components/stateful-pages/ResourceForm/ResourceForm.tsx","../src/components/organisms/CustomForm/CustomForm.tsx","../src/components/stateful/FieldGuesser/FormFieldGuesser.tsx","../src/components/organisms/CustomForm/CustomForm.styled.tsx","../src/components/atoms/backToLastPage/BackToLastPage.tsx","../src/components/stateful-providers/AppProvider/AppProvider.tsx","../src/components/stateful-providers/BreadcrumbProvider/BreadcrumbProvider.tsx","../src/components/stateful-providers/I18nProvider/I18nProvider.tsx","../src/components/stateful-providers/CatalogProvider/CatalogProvider.tsx","../src/components/stateful-providers/MetadataProvider/MetadataProvider.tsx","../src/components/stateful-providers/DataProvider/DataProvider.tsx","../src/components/stateful-providers/OptionsProvider/OptionsProvider.tsx","../src/constants/i18n.ts","../src/hocs/withAuth.tsx","../src/hocs/withOptions.tsx","../src/pages/admin/analyze/Results.tsx","../src/components/stateful/ProductPreview/MerchandiseBar.tsx","../src/components/stateful/ProductPreview/ProductsPreviewBottom.tsx","../src/components/molecules/Explain/ExplainQueryDetails.tsx","../src/components/molecules/Explain/ExplainDetails.tsx","../src/components/atoms/ReadOnlyTable/ReadOnlyTable.tsx","../src/components/atoms/ReadOnlyTable/ReadOnlyTable.styled.tsx","../src/components/atoms/modals/Tooltip.stories.tsx","../src/components/molecules/Explain/ExplainDetails.styled.tsx","../src/pages/admin/merchandize/categories.tsx","../src/pages/admin/merchandize/boost/Grid.tsx","../src/pages/admin/merchandize/boost/Create.tsx","../src/pages/admin/merchandize/boost/Edit.tsx","../src/pages/admin/search/thesaurus/Grid.tsx","../src/pages/admin/search/thesaurus/Create.tsx","../src/pages/admin/search/thesaurus/Edit.tsx","../src/pages/admin/search/configuration/Attributes.tsx","../src/pages/admin/search/Facets.tsx","../src/pages/admin/search/Vector.tsx","../src/pages/admin/settings/Index.tsx","../src/pages/admin/Dashboard.tsx","../src/pages/admin/Settings.tsx","../src/pages/swagger/Docs.tsx","../src/pages/GallyApp.tsx","../src/pages/Login.tsx","../src/pages/Welcome.tsx"],"sourcesContent":["import React, { ForwardedRef, forwardRef } from 'react'\nimport { IconButton, Alert as MuiAlert } from '@mui/material'\nimport { styled } from '@mui/system'\nimport { CustomContentProps, SnackbarKey } from 'notistack'\nimport { useTranslation } from 'next-i18next'\n\nimport IonIcon from '../IonIcon/IonIcon'\n\nconst StyledAlert = styled(MuiAlert)(({ severity, theme }) => ({\n padding: `6px ${theme.spacing(2)}`,\n background: theme.palette.colors.neutral[200],\n border: `1px solid ${theme.palette.colors.neutral[300]}`,\n color: theme.palette.colors.neutral[600],\n borderRadius: 8,\n alignItems: 'center',\n ...(severity === 'success' && {\n background: theme.palette.success.light,\n color: theme.palette.success.main,\n }),\n ...(severity === 'warning' && {\n background: theme.palette.warning.light,\n color: theme.palette.warning.main,\n }),\n ...(severity === 'error' && {\n background: theme.palette.error.light,\n color: theme.palette.error.main,\n }),\n '& .MuiAlert-icon': {\n color: 'inherit',\n marginRight: theme.spacing(1),\n },\n '& .MuiAlert-message': {\n padding: 0,\n fontWeight: 400,\n fontSize: '12px',\n lineHeight: '18px',\n whiteSpace: 'pre-line',\n },\n '& .MuiAlert-action': {\n paddingLeft: '10px',\n paddingTop: 0,\n paddingBottom: 0,\n paddingRight: 0,\n },\n}))\n\ninterface IProps extends Partial<CustomContentProps> {\n onShut?: (id: SnackbarKey) => void\n mb?: number\n}\n\nfunction Alert(props: IProps, ref: ForwardedRef<HTMLDivElement>): JSX.Element {\n const { id, message, onShut, variant = 'info', mb = 2 } = props\n const { t } = useTranslation('alert')\n\n function handleClick(): void {\n onShut?.(id)\n }\n\n return (\n <StyledAlert\n action={\n <IconButton\n aria-label=\"close\"\n color=\"inherit\"\n size=\"small\"\n onClick={handleClick}\n >\n <IonIcon name=\"close\" style={{ fontSize: 18, padding: '0px' }} />\n </IconButton>\n }\n ref={ref}\n severity={variant as 'success' | 'info' | 'warning' | 'error'}\n sx={{ mb }}\n >\n {t(message)}\n </StyledAlert>\n )\n}\n\nexport default forwardRef(Alert)\n","/* eslint-disable @typescript-eslint/no-namespace */\nimport React, { CSSProperties, MouseEvent } from 'react'\nimport { JSX as IonIconJSX } from 'ionicons'\nimport { JSXBase } from 'ionicons/dist/types/stencil-public-runtime'\n\nexport type IIonIconProps = Omit<\n IonIconJSX.IonIcon & JSXBase.HTMLAttributes<HTMLIonIconElement>,\n 'style' | 'onClick'\n> & {\n style?: CSSProperties\n onClick?: (event: MouseEvent<IIonIconProps>) => void\n}\n\ndeclare global {\n namespace JSX {\n // eslint-disable-next-line @typescript-eslint/naming-convention\n interface IntrinsicElements {\n 'ion-icon': IIonIconProps\n }\n }\n}\n\ninterface IProps extends IIonIconProps {\n name: string\n tooltip?: boolean\n}\n\nexport const iconSrcMapping = {\n dashboard: '/images/home2.svg',\n arrow: '/images/arrow.svg',\n telescope: '/images/telescope-outline.svg',\n 'push-pin': '/images/push-pin.svg',\n}\n\nexport const iconAliasMapping = {\n analyze: 'analytics',\n informationCircle: 'information-circle-outline',\n merchandize: 'funnel-outline',\n minus: 'remove-circle',\n monitoring: 'list',\n more: 'add-circle',\n settings: 'settings-outline',\n}\nexport const customIcons = [\n ...Object.keys(iconSrcMapping),\n ...Object.keys(iconAliasMapping),\n]\n\n/*\n * Creation of special props to be clean on Typescripts files\n * See: https://github.com/ionic-team/ionicons\n * List: https://ionic.io/ionicons/\n *\n * Setup switch for special names that need svg or have another name in ion-icons\n */\nfunction IonIcon(props: IProps): JSX.Element {\n const { name, tooltip, ...other } = props\n const style = tooltip\n ? {\n color: '#8187B9',\n width: '20px',\n height: '20px',\n display: 'flex',\n }\n : {}\n const iconProps = { ...other, style: { ...style, ...other.style } }\n if (name in iconSrcMapping) {\n return (\n <ion-icon\n src={iconSrcMapping[name as keyof typeof iconSrcMapping]}\n {...iconProps}\n />\n )\n } else if (name in iconAliasMapping) {\n return (\n <ion-icon\n name={iconAliasMapping[name as keyof typeof iconAliasMapping]}\n {...iconProps}\n />\n )\n }\n return <ion-icon name={name} {...iconProps} />\n}\n\nexport default IonIcon\n","import React from 'react'\nimport { Box } from '@mui/system'\n\nimport IonIcon from '../IonIcon/IonIcon'\nimport { TFunction, useTranslation } from 'next-i18next'\n\ninterface IProps {\n type: 'up' | 'down' | 'straight'\n boostNumber?: number\n boostMultiplicator?: number\n}\ninterface IBoostDetails {\n boostText: string\n boostColor: string\n boostTextColor: string\n boostIconName: string\n}\n\nfunction setBoostDetails(\n type: 'up' | 'down' | 'straight',\n boostNumber: number,\n boostMultiplicator: number,\n t: TFunction\n): IBoostDetails {\n const boostText = t('boost', { count: boostNumber })\n switch (type) {\n case 'up':\n return {\n boostText: ` x${boostMultiplicator} ( ${boostNumber} ${boostText}) `,\n boostColor: 'success.light',\n boostTextColor: 'success.main',\n boostIconName: 'trending-up-outline',\n }\n case 'down':\n return {\n boostText: ` x${boostMultiplicator} ( ${boostNumber} ${boostText}) `,\n boostColor: 'error.light',\n boostTextColor: 'error.main',\n boostIconName: 'trending-down-outline',\n }\n case 'straight':\n return {\n boostText: 'no boost',\n boostColor: 'neutral.light',\n boostTextColor: 'colors.neutral.600',\n boostIconName: 'arrow-forward-outline',\n }\n }\n}\n\nfunction Boost(props: IProps): JSX.Element {\n const { type, boostNumber, boostMultiplicator } = props\n\n const { t } = useTranslation('boost')\n const boostDetail = setBoostDetails(type, boostNumber, boostMultiplicator, t)\n\n return (\n <Box sx={{ display: 'flex', gap: '4px' }}>\n <Box\n sx={{\n backgroundColor: boostDetail.boostColor,\n color: boostDetail.boostTextColor,\n height: '20px',\n width: '20px',\n borderRadius: '50%',\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n >\n <IonIcon\n name={boostDetail.boostIconName}\n style={\n type === 'straight'\n ? { width: '14px', height: '14px' }\n : { width: '14px', height: '8px' }\n }\n />\n </Box>\n {boostNumber !== undefined && boostMultiplicator !== undefined && (\n <Box\n sx={{\n color: boostDetail.boostTextColor,\n fontFamily: 'var(--gally-font)',\n fontWeight: '500',\n fontSize: '12px',\n lineHeight: '18px',\n }}\n >\n {boostDetail.boostText}\n </Box>\n )}\n </Box>\n )\n}\n\nexport default Boost\n","import React from 'react'\nimport { Typography } from '@mui/material'\nimport { styled } from '@mui/system'\n\nconst CustomBreadCrumb = styled('span')({\n fontWeight: '500',\n fontSize: '12px',\n lineHeight: '18px',\n})\n\nconst CustomBreadCrumbColorClassique = styled(CustomBreadCrumb)(\n ({ theme }) => ({\n color: theme.palette.colors.neutral['500'],\n })\n)\n\nconst CustomBreadCrumbColorLast = styled(CustomBreadCrumb)(({ theme }) => ({\n color: theme.palette.colors.neutral['800'],\n}))\n\ninterface IProps {\n label: string\n last: boolean\n}\n\nfunction Breadcrumb(props: IProps): JSX.Element {\n const { label, last } = props\n const Component = last\n ? CustomBreadCrumbColorLast\n : CustomBreadCrumbColorClassique\n return (\n <Typography>\n <Component>{label}</Component>\n </Typography>\n )\n}\n\nexport default Breadcrumb\n","import React from 'react'\nimport { Breadcrumbs as MuiBreadcrumbs } from '@mui/material'\n\nimport {\n IMenu,\n findBreadcrumbLabel,\n getSlugArray,\n} from '@elastic-suite/gally-admin-shared'\n\nimport Breadcrumb from './Breadcrumb'\n\ninterface IProps {\n slug: string | string[]\n menu: IMenu\n}\n\nfunction Breadcrumbs(props: IProps): JSX.Element {\n const { menu, slug } = props\n\n if (!slug || slug.length === 0 || menu.hierarchy.length === 0) {\n return null\n }\n\n const breadcrumbProps = getSlugArray(slug)\n .map((item: string, index: number, slug: string[]) => ({\n key: item,\n label: findBreadcrumbLabel(index, slug, menu.hierarchy),\n last: slug.length <= index + 1,\n }))\n .filter(({ label }) => label)\n return (\n <MuiBreadcrumbs\n aria-label=\"breadcrumb\"\n sx={{ color: 'colors.neutral.500' }}\n data-testid=\"breadcrumbs\"\n >\n {breadcrumbProps.map(({ key, ...props }) => (\n <Breadcrumb key={key} {...props} />\n ))}\n </MuiBreadcrumbs>\n )\n}\n\nexport default Breadcrumbs\n","import React from 'react'\nimport { styled } from '@mui/system'\nimport { ChipProps, IconButton, Chip as MuiChip } from '@mui/material'\n\nimport IonIcon from '../IonIcon/IonIcon'\n\nconst StyledChip = styled(MuiChip)(({ theme, disabled }) => ({\n display: 'inline-flex',\n height: '26px',\n color: theme.palette.colors.neutral['900'],\n fontFamily: 'var(--gally-font)',\n fontWeight: disabled ? 400 : 600,\n fontSize: disabled ? 14 : 12,\n lineHeight: '18px',\n textAlign: 'center',\n alignItems: 'center',\n padding: `2px ${theme.spacing(1)}`,\n background: theme.palette.colors.neutral['300'],\n borderRadius: '13px',\n cursor: 'default',\n '&.MuiChip-colorPrimary': {\n color: theme.palette.colors.secondary['600'],\n background: theme.palette.colors.secondary['100'],\n },\n '&.MuiChip-colorSuccess': {\n color: theme.palette.success.main,\n background: theme.palette.success.light,\n },\n '&.MuiChip-colorWarning': {\n color: theme.palette.warning.main,\n background: theme.palette.warning.light,\n },\n '&.MuiChip-colorError': {\n color: theme.palette.error.main,\n background: theme.palette.error.light,\n },\n '&.MuiChip-sizeSmall': {\n height: '20px',\n },\n '& .MuiChip-label': {\n padding: 0,\n },\n '& .MuiChip-deleteIcon': {\n color: 'inherit',\n margin: `0 0 0 ${theme.spacing(0.5)}`,\n padding: 0,\n fontSize: '14px',\n '&:hover': {\n color: 'inherit',\n opacity: 0.5,\n },\n },\n}))\n\nfunction Chip(props: ChipProps): JSX.Element {\n const { disabled } = props\n\n return (\n <StyledChip\n disabled={disabled}\n {...props}\n style={{ opacity: 1 }}\n deleteIcon={\n <IconButton>\n <IonIcon name=\"close\" />\n </IconButton>\n }\n />\n )\n}\n\nexport default Chip\n","import React from 'react'\nimport { Box } from '@mui/material'\n\ninterface IProps {\n onClose: () => void\n}\n\nfunction CloseComponent({ onClose }: IProps): JSX.Element {\n return (\n <Box\n sx={{\n position: 'fixed',\n width: '100vw',\n height: '100vh',\n zIndex: 99,\n left: '0',\n top: '0',\n }}\n onClick={onClose}\n />\n )\n}\n\nexport default CloseComponent\n","import React, {\n ChangeEvent,\n ForwardedRef,\n ReactNode,\n SyntheticEvent,\n forwardRef,\n} from 'react'\nimport {\n CheckboxProps,\n FormControlLabel,\n FormHelperText,\n Checkbox as MuiCheckbox,\n} from '@mui/material'\nimport IonIcon from '../IonIcon/IonIcon'\n\nexport interface ICheckboxWithoutErrorProps\n extends Omit<CheckboxProps, 'onChange' | 'onClick'> {\n label?: ReactNode\n list?: boolean\n onChange?: (checked: boolean, event: SyntheticEvent) => void\n onClick?: (event: SyntheticEvent) => void\n small?: boolean\n helperIcon?: string\n helperText?: ReactNode\n error?: boolean\n dataTestId?: string\n}\n\nfunction CheckboxWithoutError(\n props: ICheckboxWithoutErrorProps,\n ref?: ForwardedRef<HTMLInputElement>\n): JSX.Element {\n const {\n error,\n helperIcon,\n helperText,\n disabled,\n label,\n list,\n onChange,\n onClick,\n small,\n dataTestId,\n ...checkboxProps\n } = props\n\n function handleChange(event: ChangeEvent<HTMLInputElement>): void {\n if (onChange) {\n onChange(event.target.checked, event)\n }\n }\n\n return (\n <FormControlLabel\n componentsProps={{ typography: { variant: list ? 'caption' : 'body2' } }}\n control={\n <MuiCheckbox\n {...checkboxProps}\n inputRef={ref}\n onChange={handleChange}\n sx={{\n ...(Boolean(list) && {\n marginBottom: '-9px',\n marginTop: '-9px',\n fontSize: '12px',\n }),\n ...(Boolean(small) && {\n padding: 0,\n marginLeft: '6px',\n marginRight: '6px',\n }),\n }}\n />\n }\n onClick={onClick}\n disabled={disabled}\n label={\n <div>\n {label}\n {Boolean(helperText) && (\n <FormHelperText error={error}>\n {Boolean(helperIcon) && (\n <IonIcon\n name={helperIcon}\n style={{ fontSize: 18, marginRight: 2 }}\n />\n )}\n {helperText}\n </FormHelperText>\n )}\n </div>\n }\n sx={{\n ...(Boolean(small) && {\n marginLeft: '-6px',\n }),\n }}\n data-testid={dataTestId}\n />\n )\n}\n\nexport default forwardRef(CheckboxWithoutError)\n","import React, { ForwardedRef, useCallback } from 'react'\n\nimport {\n IFieldErrorProps,\n IOnChange,\n IValidator,\n useFormError,\n} from '../../../hooks'\n\nimport CheckboxWithoutError, {\n ICheckboxWithoutErrorProps,\n} from './CheckboxWithoutError'\n\ninterface ICheckboxErrorProps\n extends IFieldErrorProps,\n ICheckboxWithoutErrorProps {}\n\nfunction Checkbox(props: ICheckboxErrorProps): JSX.Element {\n const { onChange, showError, additionalValidator, ...inputProps } = props\n\n const validator = useCallback<IValidator>(\n (value, event) => {\n if (additionalValidator) return additionalValidator(value, event)\n return ''\n },\n [additionalValidator]\n )\n\n const [{ ref, ...formErrorProps }] = useFormError(\n onChange as IOnChange,\n inputProps.checked,\n showError,\n validator,\n inputProps.disabled\n )\n\n return (\n <CheckboxWithoutError\n {...inputProps}\n {...formErrorProps}\n error={inputProps?.error || formErrorProps?.error}\n helperIcon={inputProps?.helperIcon || formErrorProps?.helperIcon}\n helperText={inputProps?.helperText || formErrorProps?.helperText}\n ref={ref as ForwardedRef<HTMLInputElement>}\n />\n )\n}\n\nexport default Checkbox\n","import {\n Dispatch,\n SetStateAction,\n useCallback,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from 'react'\nimport Router from 'next/router'\nimport { useTranslation } from 'next-i18next'\nimport debounce from 'lodash.debounce'\nimport {\n AuthError,\n IFetch,\n IFetchApi,\n IHydraMember,\n IHydraResponse,\n ILoadResource,\n IResource,\n IResourceEditableOperations,\n ISearchParameters,\n LoadStatus,\n defaultPageSize,\n fetchApi,\n fetchApiUsingPagination,\n getListApiParameters,\n isError,\n storageRemove,\n tokenStorageKey,\n} from '@elastic-suite/gally-admin-shared'\n\nimport { useLog } from './useLog'\nimport { useResourceOperations } from './useResource'\n\nconst debounceDelay = 1000\n\nexport function useApiFetch(secure = true): IFetchApi {\n const { i18n } = useTranslation('common')\n const log = useLog()\n\n const { t } = useTranslation('alert')\n\n return useCallback<IFetchApi>(\n async <T extends object>(\n resource: IResource | string,\n searchParameters?: ISearchParameters,\n options?: RequestInit\n ) => {\n try {\n const json = await fetchApi<T>(\n i18n.language,\n resource,\n searchParameters,\n options,\n secure\n )\n return json\n } catch (error) {\n if (secure && error instanceof AuthError) {\n storageRemove(tokenStorageKey)\n Router.push('/login')\n log(t('Expired JWT Token'))\n } else {\n log(error)\n }\n return { error }\n }\n },\n [i18n.language, log, secure, t]\n )\n}\n\nexport function useFetchApi<T extends object>(\n resource: IResource | string,\n searchParameters?: ISearchParameters,\n options?: RequestInit,\n secure = true,\n conditions = true,\n withDebounce = false,\n rowsPerPageWithPagination?: number\n): [IFetch<T>, Dispatch<SetStateAction<T>>, ILoadResource] {\n const fetchApi = useApiFetch(secure)\n const [response, setResponse] = useState<IFetch<T>>({\n status: LoadStatus.IDLE,\n })\n\n const updateResponse = useCallback((data: SetStateAction<T>): void => {\n setResponse((prevState) => ({\n ...prevState,\n data: data instanceof Function ? data(prevState.data) : data,\n }))\n }, [])\n\n const load = useCallback(() => {\n if (conditions) {\n setResponse((prevState) => ({\n data: prevState.data,\n status: LoadStatus.LOADING,\n }))\n if (!resource) {\n return setResponse({ data: null, status: LoadStatus.SUCCEEDED })\n }\n\n if (rowsPerPageWithPagination) {\n fetchApiUsingPagination<T>(\n fetchApi,\n resource,\n searchParameters,\n options,\n rowsPerPageWithPagination\n ).then((response) => {\n setResponse(response)\n })\n } else {\n fetchApi<T>(resource, searchParameters, options).then((json) => {\n if (isError(json)) {\n setResponse({ error: json.error, status: LoadStatus.FAILED })\n } else {\n setResponse({ data: json, status: LoadStatus.SUCCEEDED })\n }\n })\n }\n }\n }, [\n conditions,\n fetchApi,\n options,\n resource,\n searchParameters,\n rowsPerPageWithPagination,\n ])\n\n const customDebounce = useMemo(\n () => debounce((func: () => void) => func(), debounceDelay),\n []\n )\n\n useLayoutEffect(() => {\n if (withDebounce) {\n customDebounce(load)\n } else {\n load()\n }\n }, [load, customDebounce, withDebounce])\n\n return [response, updateResponse, load]\n}\n\nexport function useApiList<T extends object>(\n resource?: IResource | string,\n page: number | false = 0,\n rowsPerPage: number = defaultPageSize,\n searchParameters?: ISearchParameters,\n searchValue?: string,\n withDebounce = false,\n withPagination = false\n): [\n IFetch<IHydraResponse<T>> | null,\n Dispatch<SetStateAction<T[]>>?,\n ILoadResource?\n] {\n const parameters = useMemo(\n () =>\n getListApiParameters(page, rowsPerPage, searchParameters, searchValue),\n [page, rowsPerPage, searchParameters, searchValue]\n )\n const [response, updateResponse, load] = useFetchApi<IHydraResponse<T>>(\n resource,\n parameters,\n undefined,\n true,\n true,\n withDebounce,\n withPagination ? rowsPerPage : undefined\n )\n\n const updateList = useCallback(\n (data: SetStateAction<T[]>): void => {\n updateResponse((prevState) => ({\n ...prevState,\n 'hydra:member':\n data instanceof Function ? data(prevState['hydra:member']) : data,\n }))\n },\n [updateResponse]\n )\n\n return [response, updateList, load]\n}\n\nexport function useApiEditableList<T extends IHydraMember>(\n resource: IResource,\n page: number | false = 0,\n rowsPerPage: number = defaultPageSize,\n searchParameters?: ISearchParameters,\n searchValue?: string,\n url?: string,\n withDebounce = false\n): [\n IFetch<IHydraResponse<T>>,\n IResourceEditableOperations<T>,\n Dispatch<SetStateAction<T[]>>\n] {\n const [response, updateList, load] = useApiList<T>(\n url ? url : resource,\n page,\n rowsPerPage,\n searchParameters,\n searchValue,\n withDebounce\n )\n const { create, remove, replace, update } = useResourceOperations<T>(resource)\n const itemsToUpdate = useRef<Record<string, Partial<T>>>({})\n\n const debouncedUpdate = useMemo(\n () =>\n debounce(async (isValid = true): Promise<void> => {\n if (isValid) {\n const promises = Object.entries(itemsToUpdate.current).map(\n ([id, updatedItems]) => {\n delete itemsToUpdate.current[id]\n return update(id, updatedItems)\n }\n )\n\n await Promise.all(promises)\n load()\n }\n }, debounceDelay),\n [load, update]\n )\n\n const editableUpdate = useCallback(\n (id: string | number, updatedItem: Partial<T>, isValid = true) => {\n if (id in itemsToUpdate.current) {\n itemsToUpdate.current[id] = {\n ...itemsToUpdate.current[id],\n ...updatedItem,\n }\n } else {\n itemsToUpdate.current[id] = updatedItem\n }\n\n updateList((items) =>\n items.map((item) =>\n item.id === id ? { ...item, ...updatedItem } : item\n )\n )\n debouncedUpdate(isValid)\n },\n [debouncedUpdate, updateList]\n )\n\n const massEditableUpdate = useCallback(\n async (\n ids: (string | number)[],\n updatedItem: Partial<T>\n ): Promise<void> => {\n updateList((items) =>\n items.map((item) =>\n ids.includes(item.id) ? { ...item, ...updatedItem } : item\n )\n )\n const promises = ids.map((id) => update(id, updatedItem))\n await Promise.all(promises)\n load()\n },\n [load, update, updateList]\n )\n\n const massEditableReplace = useCallback(\n async (\n ids: (string | number)[],\n updatedItem: Omit<T, '@id' | '@type'>\n ): Promise<void> => {\n updateList((items) =>\n items.map((item) =>\n ids.includes(item.id) ? { ...item, ...updatedItem } : item\n )\n )\n const promises = ids.map((id) =>\n replace({ id, ...updatedItem } as unknown as T)\n )\n\n await Promise.all(promises)\n load()\n },\n [load, replace, updateList]\n )\n\n const editableCreate = useCallback(\n async (item: Omit<T, 'id' | '@id' | '@type'>): Promise<void> => {\n const createResponse = await create(item)\n if (\n !isError(createResponse) &&\n response.data['hydra:member'].length < rowsPerPage\n ) {\n // reload if item has been added and we are on the last page\n load()\n }\n },\n [create, load, response, rowsPerPage]\n )\n\n const debouncedReplace = useMemo(\n () =>\n debounce(async (isValid = true): Promise<void> => {\n if (isValid) {\n const promises = Object.entries(itemsToUpdate.current).map(\n ([id, replacedItem]) => {\n delete itemsToUpdate.current[id]\n return replace(replacedItem)\n }\n )\n\n await Promise.all(promises)\n load()\n }\n }, debounceDelay),\n [load, replace]\n )\n\n const editableReplace = useCallback(\n (replacedItem: Partial<T>, isValid = true) => {\n const id = replacedItem.id as string\n if (id in itemsToUpdate.current) {\n itemsToUpdate.current[id] = {\n ...itemsToUpdate.current[id],\n ...replacedItem,\n }\n } else {\n itemsToUpdate.current[id] = replacedItem\n }\n\n updateList((items) =>\n items.map((item) =>\n item.id === replacedItem.id ? { ...item, ...replacedItem } : item\n )\n )\n debouncedReplace(isValid)\n },\n [debouncedReplace, updateList]\n )\n\n const editableRemove = useCallback(\n async (id: string | number): Promise<void> => {\n updateList((items) => items.filter((item) => item.id !== id))\n const removeResponse = await remove(id)\n if (\n isError(removeResponse) ||\n response.data['hydra:member'].length === rowsPerPage\n ) {\n // reload if error\n // and reload if we are not on the last page to fill the space left by the deleted item\n load()\n }\n },\n [load, remove, response, rowsPerPage, updateList]\n )\n\n const operations = useMemo(() => {\n const operations: IResourceEditableOperations<T> = {}\n if (update) {\n operations.update = editableUpdate\n operations.massUpdate = massEditableUpdate\n }\n if (create) {\n operations.create = editableCreate\n }\n if (replace) {\n operations.replace = editableReplace\n operations.massReplace = massEditableReplace\n }\n if (remove) {\n operations.remove = editableRemove\n }\n return operations\n }, [\n create,\n editableCreate,\n editableRemove,\n editableReplace,\n editableUpdate,\n massEditableUpdate,\n massEditableReplace,\n remove,\n replace,\n update,\n ])\n\n return [response, operations, updateList]\n}\n","import { useCallback } from 'react'\nimport { closeSnackbar, enqueueSnackbar } from 'notistack'\nimport {\n MessageSeverity,\n NetworkError,\n log,\n} from '@elastic-suite/gally-admin-shared'\n\nexport function useLog(\n severity: MessageSeverity = 'error'\n): (error: NetworkError) => void {\n return useCallback(\n (error: NetworkError) =>\n log(error, (message: string) =>\n enqueueSnackbar(message, {\n onShut: closeSnackbar,\n variant: severity,\n preventDuplicate: true,\n })\n ),\n [severity]\n )\n}\n","import { useCallback, useMemo } from 'react'\nimport { useRouter } from 'next/router'\n\nimport {\n IError,\n IHydraMember,\n IMainContext,\n IResource,\n IResourceOperations,\n Method,\n contentTypeHeader,\n getApiUrl,\n getResource,\n updatePropertiesAccordingToPath,\n} from '@elastic-suite/gally-admin-shared'\n\nimport { selectApi, useAppSelector } from '../store'\n\nimport { useApiFetch } from './useApi'\n\nexport function useResource(\n resourceName: string,\n mainContext: IMainContext = IMainContext.GRID\n): IResource {\n const api = useAppSelector(selectApi)\n const { pathname } = useRouter()\n return useMemo(() => {\n const resource = getResource(api, resourceName)\n return {\n ...resource,\n supportedProperty:\n resource?.supportedProperty instanceof Array\n ? resource?.supportedProperty.map((field) =>\n updatePropertiesAccordingToPath(field, pathname, mainContext)\n )\n : resource?.supportedProperty\n ? [\n updatePropertiesAccordingToPath(\n resource.supportedProperty,\n pathname,\n mainContext\n ),\n ]\n : resource?.supportedProperty,\n }\n }, [api, pathname, mainContext, resourceName])\n}\n\nexport function useResourceOperations<T extends IHydraMember>(\n resource: IResource\n): IResourceOperations<T> {\n const { supportedOperation } = resource\n const fetchApi = useApiFetch()\n const apiUrl = getApiUrl(resource.url)\n\n const update = useCallback(\n (id: string | number, item: Partial<T>): Promise<T | IError> =>\n fetchApi<T>(`${apiUrl}/${id}`, undefined, {\n body: JSON.stringify(item),\n method: Method.PATCH,\n headers: { [contentTypeHeader]: 'application/merge-patch+json' },\n }),\n [apiUrl, fetchApi]\n )\n\n const create = useCallback(\n (item: Omit<T, 'id' | '@id' | '@type'>): Promise<T | IError> =>\n fetchApi(apiUrl, undefined, {\n body: JSON.stringify(item),\n method: Method.POST,\n }),\n [apiUrl, fetchApi]\n )\n\n const replace = useCallback(\n (item: Partial<T>): Promise<T | IError> =>\n fetchApi(`${apiUrl}/${item.id}`, undefined, {\n body: JSON.stringify(item),\n method: Method.PUT,\n }),\n [apiUrl, fetchApi]\n )\n\n const remove = useCallback(\n (id: string | number): Promise<T | IError> =>\n fetchApi(`${apiUrl}/${id}`, undefined, {\n method: Method.DELETE,\n }),\n [apiUrl, fetchApi]\n )\n\n return useMemo(\n () =>\n supportedOperation.reduce<IResourceOperations<T>>((acc, operation) => {\n if (typeof operation['@type'] === 'string') {\n acc.update = update\n } else if (\n operation['@type'] instanceof Array &&\n operation['@type'].length === 2 &&\n typeof operation['@type'][1] === 'string'\n ) {\n if (\n operation['@type'][1].indexOf('//schema.org/CreateAction') !== -1\n ) {\n acc.create = create\n } else if (\n operation['@type'][1].indexOf('//schema.org/ReplaceAction') !== -1\n ) {\n acc.replace = replace\n } else if (\n operation['@type'][1].indexOf('//schema.org/DeleteAction') !== -1\n ) {\n acc.remove = remove\n }\n }\n return acc\n }, {}),\n [create, remove, replace, supportedOperation, update]\n )\n}\n","import { PayloadAction, createSlice } from '@reduxjs/toolkit'\nimport {\n Bundle,\n IApi,\n IConfigurations,\n} from '@elastic-suite/gally-admin-shared'\n\nimport { RootState } from './store'\n\nexport interface IDataState {\n api: IApi\n bundles: Bundle[]\n configurations: IConfigurations\n}\n\nconst initialState: IDataState = {\n api: null,\n bundles: null,\n configurations: null,\n}\n\nconst dataSlice = createSlice({\n name: 'data',\n initialState,\n reducers: {\n setData(state, action: PayloadAction<IDataState>) {\n state.api = action.payload.api\n state.bundles = action.payload.bundles\n state.configurations = action.payload.configurations\n },\n },\n})\n\nexport const { setData } = dataSlice.actions\nexport const dataReducer = dataSlice.reducer\n\nexport const selectApi = (state: RootState): IApi => state.data.api\nexport const selectBundles = (state: RootState): Bundle[] => state.data.bundles\nexport const selectConfiguration = (state: RootState): IConfigurations =>\n state.data.configurations\n","import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'\nimport type { AppDispatch, RootState } from './store'\n\nexport const useAppDispatch = (): AppDispatch => useDispatch<AppDispatch>()\nexport const useAppSelector: TypedUseSelectorHook<RootState> = useSelector\n","import { PayloadAction, createSlice } from '@reduxjs/toolkit'\n\nimport { RootState } from './store'\n\nexport interface II18nState {\n language: string\n}\n\nconst initialState: II18nState = {\n language: 'en',\n}\n\nconst i18nSlice = createSlice({\n name: 'i18n',\n initialState,\n reducers: {\n setLanguage(state, action: PayloadAction<string>) {\n state.language = action.payload\n },\n },\n})\n\nexport const { setLanguage } = i18nSlice.actions\nexport const i18nReducer = i18nSlice.reducer\n\nexport const selectLanguage = (state: RootState): string => state.i18n.language\n","import { PayloadAction, createSlice } from '@reduxjs/toolkit'\n\nimport { IFetch, IMenu, LoadStatus } from '@elastic-suite/gally-admin-shared'\n\nimport { RootState } from './store'\n\nexport interface IMenuState {\n menu: IFetch<IMenu>\n menuItemActive: string\n sidebarState: boolean\n sidebarStateTimeout: boolean\n childrenState: Record<string, boolean>\n}\n\nconst initialState: IMenuState = {\n menu: {\n status: LoadStatus.IDLE,\n data: {\n hierarchy: [],\n },\n },\n menuItemActive: '',\n sidebarState: true,\n sidebarStateTimeout: false,\n childrenState: {},\n}\n\nconst menuSlice = createSlice({\n name: 'menu',\n initialState,\n reducers: {\n setChildState(\n state,\n action: PayloadAction<{ code: string; value: boolean }>\n ) {\n state.childrenState[action.payload.code] = action.payload.value\n },\n setMenu(state, action: PayloadAction<IFetch<IMenu>>) {\n state.menu = {\n ...state.menu,\n ...action.payload,\n }\n },\n setMenuItemActive(state, action: PayloadAction<string>) {\n state.menuItemActive = action.payload\n },\n setSidebarState(state, action: PayloadAction<boolean>) {\n state.sidebarState = action.payload\n },\n setSidebarStateTimeout(state, action: PayloadAction<boolean>) {\n state.sidebarStateTimeout = action.payload\n },\n },\n})\n\nexport const {\n setChildState,\n setMenu,\n setMenuItemActive,\n setSidebarState,\n setSidebarStateTimeout,\n} = menuSlice.actions\nexport const menuReducer = menuSlice.reducer\n\nexport const selectChildrenState = (\n state: RootState\n): Record<string, boolean> => state.menu.childrenState\nexport const selectMenu = (state: RootState): IMenu => state.menu.menu.data\nexport const selectMenuItemActive = (state: RootState): string =>\n state.menu.menuItemActive\nexport const selectSidebarState = (state: RootState): boolean =>\n state.menu.sidebarState\nexport const selectSidebarStateTimeout = (state: RootState): boolean =>\n state.menu.sidebarStateTimeout\n","import {\n PreloadedState,\n combineReducers,\n configureStore,\n} from '@reduxjs/toolkit'\nimport thunk from 'redux-thunk'\nimport { dataReducer } from './data'\nimport { i18nReducer } from './i18n'\nimport { menuReducer } from './menu'\nimport { userReducer } from './user'\n\nconst rootReducer = combineReducers({\n data: dataReducer,\n i18n: i18nReducer,\n menu: menuReducer,\n user: userReducer,\n})\n\n// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type\nexport const setupStore = (preloadedState?: PreloadedState<RootState>) => {\n return configureStore({\n middleware: (getDefaultMiddleware) =>\n process.env.NODE_ENV === 'development'\n ? getDefaultMiddleware({\n serializableCheck: {\n ignoredActionPaths: ['payload.error'],\n ignoredPaths: ['menu.menu.error'],\n },\n })\n : [thunk],\n preloadedState,\n reducer: rootReducer,\n })\n}\n\nexport type RootState = ReturnType<typeof rootReducer>\nexport type AppStore = ReturnType<typeof setupStore>\nexport type AppDispatch = AppStore['dispatch']\n\nexport interface IThunkApi {\n dispatch: AppDispatch\n state: RootState\n}\n","import { PayloadAction, createSlice } from '@reduxjs/toolkit'\n\nimport { RootState } from './store'\n\nexport interface IUserState {\n requestedPath: string | null\n}\n\nconst initialState: IUserState = {\n requestedPath: null,\n}\n\nconst userSlice = createSlice({\n name: 'user',\n initialState,\n reducers: {\n setRequestedPath(state, action: PayloadAction<string>) {\n state.requestedPath = action.payload\n },\n },\n})\n\nexport const { setRequestedPath } = userSlice.actions\nexport const userReducer = userSlice.reducer\n\nexport const selectRequestedPath = (state: RootState): string =>\n state.user.requestedPath\n","import { useMemo } from 'react'\nimport { useTranslation } from 'next-i18next'\nimport {\n IFieldConfig,\n IHydraMember,\n IHydraResponse,\n IResource,\n getFilter,\n getMappings,\n} from '@elastic-suite/gally-admin-shared'\n\nexport function useApiFilters<A extends IHydraMember>(\n apiData: IHydraResponse<A>,\n resource: IResource\n): IFieldConfig[] {\n const { t } = useTranslation('api')\n const mappings = useMemo(\n () => getMappings(apiData, resource),\n [apiData, resource]\n )\n\n return useMemo(\n () => mappings.map((mapping) => getFilter(mapping, t)),\n [mappings, t]\n )\n}\n","import { useTranslation } from 'next-i18next'\nimport { useMemo } from 'react'\nimport {\n IField,\n IFieldConfig,\n IFieldConfigFormWithFieldset,\n IOptions,\n IResource,\n getFieldHeader,\n} from '@elastic-suite/gally-admin-shared'\nimport { useApiDoubleDatePicker } from './useApiForm'\n\nexport function useApiHeaders(resource: IResource): IFieldConfig[] {\n const { t } = useTranslation('api')\n const transformedResource = useApiDoubleDatePicker(resource)\n return useMemo(\n () =>\n transformedResource.supportedProperty\n .filter((field) => field.gally?.visible)\n .sort((a, b) => a.gally?.position - b.gally?.position)\n .map((field) => getFieldHeader(field, t)),\n [t, transformedResource]\n )\n}\n\n// group all fields that do not have a fieldset in this fieldset\nconst FIELDSET_OTHER_KEY = '_other'\n\nexport function useApiHeadersForm(\n resource: IResource\n): IFieldConfigFormWithFieldset[] {\n const apiHeaders = useApiHeaders(resource)\n\n return useMemo(() => {\n const apiHeaderMap = apiHeaders.reduce<\n Record<string, IFieldConfigFormWithFieldset>\n >(\n (acc, { gridHeaderInfoTooltip, ...header }) => {\n const fieldsetCode = header.fieldset\n const fieldset = resource.gally?.fieldset?.[fieldsetCode]\n if (fieldsetCode && fieldset) {\n if (fieldsetCode in acc) {\n acc[fieldsetCode].children.push(header)\n } else {\n acc[fieldsetCode] = {\n ...fieldset,\n code: fieldsetCode,\n children: [header],\n }\n }\n } else {\n acc[FIELDSET_OTHER_KEY].children.push(header)\n }\n return acc\n },\n {\n [FIELDSET_OTHER_KEY]: {\n code: FIELDSET_OTHER_KEY,\n children: [],\n position: Infinity,\n },\n }\n )\n return Object.values(apiHeaderMap).sort((a, b) => a?.position - b?.position)\n }, [apiHeaders, resource])\n}\n\nexport function useApiEditableFieldOptions(\n resource: IResource\n): IOptions<IField> {\n return useMemo(() => {\n return resource.supportedProperty\n .filter(\n (field) =>\n field.gally?.visible && field.gally?.editable && field.writeable\n )\n .map((field) => ({\n id: field.title,\n label: field.property.label,\n value: field,\n }))\n }, [resource])\n}\n","import { IResource } from '@elastic-suite/gally-admin-shared'\nimport { useMemo } from 'react'\n\nexport function useApiDoubleDatePicker(resource: IResource): IResource {\n return useMemo(() => {\n const newSupportedProperty = resource.supportedProperty.map((element) => {\n if (element?.gally?.input === 'rangeDate') {