@cccsaurora/howler-ui 2.19.0-dev.920 → 2.19.0-dev.922

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,10 @@ const TuiListBaseRoot = styled('div')(({ theme }) => ({
30
30
  display: 'flex',
31
31
  backgroundColor: emphasize(theme.palette.background.default, 0.04)
32
32
  }
33
+ },
34
+ '.elementDisabled': {
35
+ color: theme.palette.text.disabled,
36
+ cursor: 'default'
33
37
  }
34
38
  }));
35
39
  const TuiListBase = ({ keyboard = false, onSelect, children }) => {
@@ -5,11 +5,14 @@ import VSBoxElement from '../layout/vsbox/VSBoxElement';
5
5
  const TuiListElement = ({ position, item, onSelect: onClick, children }) => {
6
6
  const elementEl = useRef();
7
7
  const onItemClick = useCallback(_event => {
8
- if (onClick) {
8
+ if (onClick && !item.disabled) {
9
9
  onClick(item, position);
10
10
  }
11
11
  }, [onClick, item, position]);
12
12
  const classRenderer = useCallback(() => {
13
+ if (item.disabled) {
14
+ return 'elementDisabled';
15
+ }
13
16
  const _classes = ['elementHover'];
14
17
  if (item.cursor) {
15
18
  _classes.push('elementFocus');
@@ -18,7 +21,7 @@ const TuiListElement = ({ position, item, onSelect: onClick, children }) => {
18
21
  _classes.push('elementSelected');
19
22
  }
20
23
  return _classes.join(' ');
21
- }, [item.cursor, item.selected]);
22
- return (_jsx(VSBoxElement, { focus: !!item.cursor, children: _jsx("div", { ref: elementEl, "data-tuilist-index": position, "data-tuilist-id": item.id, "data-tuilist-focus": !!item.cursor, "data-tuilist-selected": !!item.selected, onClick: onItemClick, children: children({ item, position }, classRenderer) }) }));
24
+ }, [item.cursor, item.selected, item.disabled]);
25
+ return (_jsx(VSBoxElement, { focus: !!item.cursor, children: _jsx("div", { ref: elementEl, "data-tuilist-index": position, "data-tuilist-id": item.id, "data-tuilist-focus": !!item.cursor, "data-tuilist-selected": !!item.selected, "aria-disabled": item.disabled, onClick: onItemClick, children: children({ item, position }, classRenderer) }) }));
23
26
  };
24
27
  export default memo(TuiListElement);
@@ -8,6 +8,7 @@ export type TuiListItem<T> = {
8
8
  details?: boolean;
9
9
  cursor?: boolean;
10
10
  selected?: boolean;
11
+ disabled?: boolean;
11
12
  item: T;
12
13
  };
13
14
  export type TuiListItemProps<T> = {
@@ -1,5 +1,9 @@
1
1
  import type { FC } from 'react';
2
2
  declare const ConfirmDeleteModal: FC<{
3
3
  onConfirm: () => void;
4
+ title?: string;
5
+ description?: string;
6
+ preferDelete?: boolean;
7
+ preferCancel?: boolean;
4
8
  }>;
5
9
  export default ConfirmDeleteModal;
@@ -3,13 +3,15 @@ import { Button, Stack, Typography } from '@mui/material';
3
3
  import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
4
4
  import { useCallback, useContext } from 'react';
5
5
  import { useTranslation } from 'react-i18next';
6
- const ConfirmDeleteModal = ({ onConfirm }) => {
6
+ const ConfirmDeleteModal = ({ onConfirm, title, description, preferDelete, preferCancel }) => {
7
7
  const { t } = useTranslation();
8
8
  const { close } = useContext(ModalContext);
9
9
  const handleConfirm = useCallback(() => {
10
10
  onConfirm();
11
11
  close();
12
12
  }, [close, onConfirm]);
13
- return (_jsxs(Stack, { spacing: 2, p: 2, alignItems: "start", sx: { minWidth: '500px' }, children: [_jsx(Typography, { variant: "h4", children: t('modal.confirm.delete.title') }), _jsx(Typography, { children: t('modal.confirm.delete.description') }), _jsxs(Stack, { direction: "row", spacing: 1, alignSelf: "end", children: [_jsx(Button, { variant: "outlined", onClick: close, children: t('cancel') }), _jsx(Button, { variant: "outlined", onClick: handleConfirm, children: t('confirm') })] })] }));
13
+ const modalTitle = title ?? t('modal.confirm.delete.title');
14
+ const modalDesc = description ?? t('modal.confirm.delete.description');
15
+ return (_jsxs(Stack, { spacing: 2, p: 2, alignItems: "start", sx: { minWidth: '500px' }, children: [_jsx(Typography, { variant: "h4", children: modalTitle }), _jsx(Typography, { children: modalDesc }), _jsxs(Stack, { direction: "row", spacing: 1, alignSelf: "end", children: [_jsx(Button, { variant: preferCancel ? 'contained' : 'outlined', onClick: close, children: t('button.cancel') }), _jsx(Button, { variant: preferDelete ? 'contained' : 'outlined', onClick: handleConfirm, children: t('button.delete') })] })] }));
14
16
  };
15
17
  export default ConfirmDeleteModal;
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Analytics, InfoOutlined } from '@mui/icons-material';
3
3
  import { Alert, AlertTitle, Autocomplete, Box, Button, Chip, CircularProgress, Divider, Fade, Grid, LinearProgress, Stack, TextField, Tooltip, Typography } from '@mui/material';
4
4
  import api from '@cccsaurora/howler-ui/api';
@@ -23,6 +23,7 @@ const HitSummary = ({ response, onStart, onComplete }) => {
23
23
  const { hitFields } = useContext(FieldContext);
24
24
  const { showErrorMessage } = useMySnackbar();
25
25
  const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
26
+ const [showHitSummaryGraph] = useMyLocalStorageItem(StorageKey.SHOW_HIT_SUMMARY_GRAPH, true);
26
27
  const { getMatchingTemplate } = useMatchers();
27
28
  const searching = useContextSelector(HitSearchContext, ctx => ctx.searching);
28
29
  const error = useContextSelector(HitSearchContext, ctx => ctx.error);
@@ -130,7 +131,7 @@ const HitSummary = ({ response, onStart, onComplete }) => {
130
131
  performAggregation();
131
132
  // eslint-disable-next-line react-hooks/exhaustive-deps
132
133
  }, [query, views, searching, error]);
133
- return (_jsxs(Stack, { sx: { mx: 2, height: '100%' }, spacing: 1, children: [_jsx(Typography, { variant: "h6", children: t('hit.summary.aggregate.title') }), _jsx(Divider, { flexItem: true }), _jsx(HitGraph, {}), _jsx(Divider, { flexItem: true }), _jsxs(Stack, { sx: { overflow: 'auto', marginTop: '0 !important' }, pt: 1, spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 2, mb: 2, alignItems: "stretch", children: [_jsx(Autocomplete, { fullWidth: true, multiple: true, sx: { minWidth: '175px' }, size: "small", value: customKeys, options: hitFields.map(_field => _field.key), renderInput: _params => _jsx(TextField, { ..._params, label: t('hit.summary.adhoc') }), onChange: (_, value) => setCustomKeys(value) }), _jsx(Button, { variant: "outlined", startIcon: loading ? _jsx(CircularProgress, { size: 20, sx: { ml: 1 } }) : _jsx(Analytics, { sx: { ml: 1 } }), disabled: loading, onClick: () => performAggregation(), children: t('button.aggregate') })] }), isEmpty(aggregateResults) && (_jsxs(Alert, { severity: "info", variant: "outlined", children: [_jsx(AlertTitle, { children: t('hit.summary.aggregate.nokeys.title') }), t('hit.summary.aggregate.nokeys.description')] })), loading && _jsx(LinearProgress, { sx: { minHeight: '4px' } }), Object.keys(aggregateResults)
134
+ return (_jsxs(Stack, { sx: { mx: 2, height: '100%' }, spacing: 1, children: [_jsx(Typography, { variant: "h6", children: t('hit.summary.aggregate.title') }), _jsx(Divider, { flexItem: true }), showHitSummaryGraph && (_jsxs(_Fragment, { children: [_jsx(HitGraph, {}), _jsx(Divider, { flexItem: true })] })), _jsxs(Stack, { sx: { overflow: 'auto', marginTop: '0 !important' }, pt: 1, spacing: 1, children: [_jsxs(Stack, { direction: "row", spacing: 2, mb: 2, alignItems: "stretch", children: [_jsx(Autocomplete, { fullWidth: true, multiple: true, sx: { minWidth: '175px' }, size: "small", value: customKeys, options: hitFields.map(_field => _field.key), renderInput: _params => _jsx(TextField, { ..._params, label: t('hit.summary.adhoc') }), onChange: (_, value) => setCustomKeys(value) }), _jsx(Button, { variant: "outlined", startIcon: loading ? _jsx(CircularProgress, { size: 20, sx: { ml: 1 } }) : _jsx(Analytics, { sx: { ml: 1 } }), disabled: loading, onClick: () => performAggregation(), children: t('button.aggregate') })] }), isEmpty(aggregateResults) && (_jsxs(Alert, { severity: "info", variant: "outlined", children: [_jsx(AlertTitle, { children: t('hit.summary.aggregate.nokeys.title') }), t('hit.summary.aggregate.nokeys.description')] })), loading && _jsx(LinearProgress, { sx: { minHeight: '4px' } }), Object.keys(aggregateResults)
134
135
  .filter(key => !isEmpty(aggregateResults[key]))
135
136
  .flatMap(key => [
136
137
  _jsx(Fade, { in: true, children: _jsxs(Stack, { direction: "row", alignItems: "center", spacing: 1, children: [_jsx(Typography, { variant: "body1", children: key }, key + '-title'), keyCounts[key]?.count < 0 ? (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", t('hit.summary.adhoc.custom'), ")"] })) : (_jsxs(Typography, { variant: "caption", color: "text.secondary", children: ["(", keyCounts[key]?.count ?? '?', " ", t('references'), ")"] })), _jsx(Tooltip, { title: _jsxs(Stack, { children: [_jsx(Typography, { variant: "caption", children: t('hit.summary.aggregate.sources') }), keyCounts[key]?.sources.map(source => (_jsx(Typography, { variant: "caption", children: source }, source))) ?? '?'] }), children: _jsx(InfoOutlined, { fontSize: "inherit" }) })] }) }, key + '-refs'),
@@ -17,11 +17,12 @@ const LocalSection = () => {
17
17
  const [compactJson, setCompactJson] = useMyLocalStorageItem(StorageKey.COMPACT_JSON, true);
18
18
  const [flattenJson, setFlattenJson] = useMyLocalStorageItem(StorageKey.FLATTEN_JSON, false);
19
19
  const [forceDrawer, setForceDrawer] = useMyLocalStorageItem(StorageKey.FORCE_DRAWER, false);
20
+ const [showHitSummaryGraph, setShowHitSummaryGraph] = useMyLocalStorageItem(StorageKey.SHOW_HIT_SUMMARY_GRAPH, true);
20
21
  const [hitLayout, setHitLayout] = useMyLocalStorageItem(StorageKey.HIT_LAYOUT, false);
21
22
  const [displayType, setDisplayType] = useMyLocalStorageItem(StorageKey.DISPLAY_TYPE, 'list');
22
23
  const [pageCount, setPageCount] = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25);
23
24
  const [searchWidth, setSearchWidth] = useMyLocalStorageItem(StorageKey.SEARCH_PANE_WIDTH, null);
24
25
  const [templateFieldCount, setTemplateFieldCount] = useMyLocalStorageItem(StorageKey.TEMPLATE_FIELD_COUNT, null);
25
- return (_jsxs(SettingsSection, { title: t('page.settings.local.title'), colSpan: 3, children: [_jsx(EditRow, { titleKey: "page.settings.local.compact.json", descriptionKey: "page.settings.local.compact.json.description", value: compactJson, type: "checkbox", onEdit: async (value) => setCompactJson(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.flatten.json", descriptionKey: "page.settings.local.flatten.json.description", value: flattenJson, type: "checkbox", onEdit: async (value) => setFlattenJson(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.details.drawer", descriptionKey: "page.settings.local.details.drawer.description", value: forceDrawer, type: "checkbox", onEdit: async (value) => setForceDrawer(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.search.width", descriptionKey: "page.settings.local.search.width.description", value: searchWidth, type: "range", min: 400, max: Math.floor(window.innerWidth / 100) * 100, optional: true, onEdit: async (value) => setSearchWidth(value ? parseInt(value) : null) }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.hits.layout') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(ToggleButtonGroup, { size: "small", value: hitLayout, exclusive: true, onChange: (_, value) => setHitLayout(value), children: [_jsx(ToggleButton, { value: HitLayout.DENSE, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewCompact, {}), _jsx("span", { children: t('page.settings.local.hits.layout.dense') })] }) }), _jsx(ToggleButton, { value: HitLayout.NORMAL, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewModule, {}), _jsx("span", { children: t('page.settings.local.hits.layout.normal') })] }) }), _jsx(ToggleButton, { value: HitLayout.COMFY, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewComfy, {}), _jsx("span", { children: t('page.settings.local.hits.layout.comfy') })] }) })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.hits.layout.description') }) }) }), _jsx(EditRow, { titleKey: "page.settings.local.hits.field_count", descriptionKey: "page.settings.local.hits.field_count.description", value: templateFieldCount, type: "range", min: 0, step: 1, max: 15, optional: true, onEdit: async (value) => setTemplateFieldCount(value ? parseInt(value) : null), valueLabelFormat: val => val.toString() }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.hits.display_type') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(ToggleButtonGroup, { size: "small", value: displayType, exclusive: true, onChange: (_, value) => setDisplayType(value), children: [_jsx(ToggleButton, { value: "list", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(List, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.list') })] }) }), _jsx(ToggleButton, { value: "grid", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(TableChart, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.grid') })] }) })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.hits.display_type.description') }) }) }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.results.count') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(Select, { size: "small", sx: { minWidth: '75px', textAlign: 'left' }, value: pageCount, onChange: event => setPageCount(typeof event.target.value === 'string' ? parseInt(event.target.value) : event.target.value), children: [_jsx(MenuItem, { value: 5, children: "5" }), _jsx(MenuItem, { value: 10, children: "10" }), _jsx(MenuItem, { value: 25, children: "25" }), _jsx(MenuItem, { value: 50, children: "50" }), _jsx(MenuItem, { value: 75, children: "75" }), _jsx(MenuItem, { value: 100, children: "100" }), _jsx(MenuItem, { value: 150, children: "150" }), _jsx(MenuItem, { value: 250, children: "250" })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.results.count.description') }) }) }), howlerPluginStore.plugins.map(plugin => pluginStore.executeFunction(`${plugin}.settings`, 'local'))] }));
26
+ return (_jsxs(SettingsSection, { title: t('page.settings.local.title'), colSpan: 3, children: [_jsx(EditRow, { titleKey: "page.settings.local.compact.json", descriptionKey: "page.settings.local.compact.json.description", value: compactJson, type: "checkbox", onEdit: async (value) => setCompactJson(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.flatten.json", descriptionKey: "page.settings.local.flatten.json.description", value: flattenJson, type: "checkbox", onEdit: async (value) => setFlattenJson(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.details.drawer", descriptionKey: "page.settings.local.details.drawer.description", value: forceDrawer, type: "checkbox", onEdit: async (value) => setForceDrawer(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.show.hit.summary.graph", descriptionKey: "page.settings.local.show.hit.summary.graph.description", value: showHitSummaryGraph, type: "checkbox", onEdit: async (value) => setShowHitSummaryGraph(JSON.parse(value)) }), _jsx(EditRow, { titleKey: "page.settings.local.search.width", descriptionKey: "page.settings.local.search.width.description", value: searchWidth, type: "range", min: 400, max: Math.floor(window.innerWidth / 100) * 100, optional: true, onEdit: async (value) => setSearchWidth(value ? parseInt(value) : null) }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.hits.layout') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(ToggleButtonGroup, { size: "small", value: hitLayout, exclusive: true, onChange: (_, value) => setHitLayout(value), children: [_jsx(ToggleButton, { value: HitLayout.DENSE, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewCompact, {}), _jsx("span", { children: t('page.settings.local.hits.layout.dense') })] }) }), _jsx(ToggleButton, { value: HitLayout.NORMAL, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewModule, {}), _jsx("span", { children: t('page.settings.local.hits.layout.normal') })] }) }), _jsx(ToggleButton, { value: HitLayout.COMFY, children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(ViewComfy, {}), _jsx("span", { children: t('page.settings.local.hits.layout.comfy') })] }) })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.hits.layout.description') }) }) }), _jsx(EditRow, { titleKey: "page.settings.local.hits.field_count", descriptionKey: "page.settings.local.hits.field_count.description", value: templateFieldCount, type: "range", min: 0, step: 1, max: 15, optional: true, onEdit: async (value) => setTemplateFieldCount(value ? parseInt(value) : null), valueLabelFormat: val => val.toString() }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.hits.display_type') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(ToggleButtonGroup, { size: "small", value: displayType, exclusive: true, onChange: (_, value) => setDisplayType(value), children: [_jsx(ToggleButton, { value: "list", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(List, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.list') })] }) }), _jsx(ToggleButton, { value: "grid", children: _jsxs(Stack, { direction: "row", spacing: 0.5, children: [_jsx(TableChart, {}), _jsx("span", { children: t('page.settings.local.hits.display_type.grid') })] }) })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.hits.display_type.description') }) }) }), _jsxs(TableRow, { children: [_jsx(TableCell, { sx: CELL_SX, style: { whiteSpace: 'nowrap' }, children: t('page.settings.local.results.count') }), _jsx(TableCell, { sx: CELL_SX, colSpan: 2, align: "right", children: _jsxs(Select, { size: "small", sx: { minWidth: '75px', textAlign: 'left' }, value: pageCount, onChange: event => setPageCount(typeof event.target.value === 'string' ? parseInt(event.target.value) : event.target.value), children: [_jsx(MenuItem, { value: 5, children: "5" }), _jsx(MenuItem, { value: 10, children: "10" }), _jsx(MenuItem, { value: 25, children: "25" }), _jsx(MenuItem, { value: 50, children: "50" }), _jsx(MenuItem, { value: 75, children: "75" }), _jsx(MenuItem, { value: 100, children: "100" }), _jsx(MenuItem, { value: 150, children: "150" }), _jsx(MenuItem, { value: 250, children: "250" })] }) })] }), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: 3, sx: { paddingTop: '0 !important' }, children: _jsx(Typography, { variant: "caption", color: "text.secondary", children: t('page.settings.local.results.count.description') }) }) }), howlerPluginStore.plugins.map(plugin => pluginStore.executeFunction(`${plugin}.settings`, 'local'))] }));
26
27
  };
27
28
  export default LocalSection;
@@ -1,7 +1,9 @@
1
1
  import type { Template } from '@cccsaurora/howler-ui/models/entities/generated/Template';
2
- import type { FC } from 'react';
2
+ import { type FC } from 'react';
3
3
  declare const TemplateCard: FC<{
4
4
  template: Template;
5
+ onRemove?: (templateId: string) => void;
6
+ error?: boolean;
5
7
  className?: string;
6
8
  }>;
7
9
  export default TemplateCard;
@@ -1,13 +1,17 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Language, Lock, Person } from '@mui/icons-material';
3
- import { Card, Divider, Stack, Tooltip, Typography } from '@mui/material';
2
+ import { Language, Lock, Person, ReportProblem } from '@mui/icons-material';
3
+ import { Button, Card, Divider, Stack, Tooltip, Typography } from '@mui/material';
4
+ import { ModalContext } from '@cccsaurora/howler-ui/components/app/providers/ModalProvider';
5
+ import ConfirmDeleteModal from '@cccsaurora/howler-ui/components/elements/display/modals/ConfirmDeleteModal';
6
+ import { useContext } from 'react';
4
7
  import { useTranslation } from 'react-i18next';
5
- const TemplateCard = ({ template, className }) => {
8
+ const TemplateCard = ({ template, onRemove, error, className }) => {
6
9
  const { t } = useTranslation();
10
+ const { showModal } = useContext(ModalContext);
7
11
  return (_jsx(Card, { variant: "outlined", sx: { p: 1, mb: 1 }, className: className, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Tooltip, { title: t(`route.templates.manager.${template.type}`), children: {
8
12
  readonly: _jsx(Lock, {}),
9
13
  global: _jsx(Language, {}),
10
14
  personal: _jsx(Person, {})
11
- }[template.type] }), _jsx(Divider, { orientation: "vertical", flexItem: true }), _jsxs(Stack, { children: [_jsxs(Typography, { variant: "body1", children: [t(template.analytic), " - ", t(template.detection ?? 'all')] }), template.keys.map(key => (_jsx(Typography, { variant: "caption", sx: { ml: 1 }, children: _jsx("code", { children: key }) }, template.template_id + key)))] })] }) }, template.template_id));
15
+ }[template.type] }), _jsx(Divider, { orientation: "vertical", flexItem: true }), _jsxs(Stack, { children: [_jsxs(Typography, { variant: "body1", children: [t(template.analytic), " - ", t(template.detection ?? 'all')] }), template.keys.map(key => (_jsx(Typography, { variant: "caption", sx: { ml: 1 }, children: _jsx("code", { children: key }) }, template.template_id + key)))] }), error && (_jsx(Stack, { direction: "row", justifyContent: "end", width: "100%", children: _jsx(Stack, { children: _jsx(Tooltip, { title: t('route.templates.manager.error.action'), children: _jsx(Button, { startIcon: _jsx(ReportProblem, {}), color: "warning", onClick: () => showModal(_jsx(ConfirmDeleteModal, { onConfirm: () => onRemove?.(template.template_id), title: t('route.templates.manager.error.modal.title'), description: t('route.templates.manager.error.modal.description'), preferDelete: true })), children: t('route.templates.manager.error.message') }) }) }) }))] }) }, template.template_id));
12
16
  };
13
17
  export default TemplateCard;
@@ -3,6 +3,7 @@ import { Article } from '@mui/icons-material';
3
3
  import { Stack, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
4
4
  import api from '@cccsaurora/howler-ui/api';
5
5
  import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
6
+ import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
6
7
  import { TuiListProvider } from '@cccsaurora/howler-ui/components/elements/addons/lists';
7
8
  import { TuiListMethodContext } from '@cccsaurora/howler-ui/components/elements/addons/lists/TuiListProvider';
8
9
  import ItemManager from '@cccsaurora/howler-ui/components/elements/display/ItemManager';
@@ -19,8 +20,9 @@ const TemplatesBase = () => {
19
20
  const navigate = useNavigate();
20
21
  const { dispatchApi } = useMyApi();
21
22
  const [searchParams, setSearchParams] = useSearchParams();
22
- const { load } = useContext(TuiListMethodContext);
23
+ const { load, remove } = useContext(TuiListMethodContext);
23
24
  const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
25
+ const { analytics } = useContext(AnalyticContext);
24
26
  const [phrase, setPhrase] = useState('');
25
27
  const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
26
28
  const [response, setResponse] = useState(null);
@@ -57,17 +59,22 @@ const TemplatesBase = () => {
57
59
  }, [phrase, setSearchParams, searchParams, user.username, types, dispatchApi, pageCount, offset]);
58
60
  // Load the items into list when response changes.
59
61
  // This hook should only trigger when the 'response' changes.
62
+ // or if the analytic list changes to refresh the disabled state
60
63
  useEffect(() => {
61
64
  if (response) {
62
65
  load(response.items.map((item) => ({
63
66
  id: item.template_id,
64
67
  item,
65
68
  selected: false,
66
- cursor: false
69
+ cursor: false,
70
+ disabled: item.detection &&
71
+ !analytics
72
+ .find(v => v.name === item.analytic)
73
+ ?.detections?.map((s) => s.toLowerCase())
74
+ ?.includes(item.detection?.toLowerCase())
67
75
  })));
68
76
  }
69
- // eslint-disable-next-line react-hooks/exhaustive-deps
70
- }, [response, load]);
77
+ }, [response, load, analytics]);
71
78
  const onPageChange = useCallback((_offset) => {
72
79
  if (_offset !== offset) {
73
80
  searchParams.set('offset', _offset.toString());
@@ -96,12 +103,22 @@ const TemplatesBase = () => {
96
103
  }
97
104
  // eslint-disable-next-line react-hooks/exhaustive-deps
98
105
  }, [offset]);
99
- const renderer = useCallback((item, className) => _jsx(TemplateCard, { template: item, className: className }), []);
106
+ const removeTemplate = useCallback(async (templateId) => {
107
+ await dispatchApi(api.template.del(templateId), {
108
+ logError: false,
109
+ showError: true,
110
+ throwError: false
111
+ });
112
+ remove(templateId);
113
+ }, [dispatchApi, remove]);
114
+ const renderer = useCallback((item, error, className) => (_jsx(TemplateCard, { template: item, error: error, onRemove: removeTemplate, className: className })), [removeTemplate]);
100
115
  return (_jsx(ItemManager, { onSearch: onSearch, onPageChange: onPageChange, phrase: phrase, setPhrase: setPhrase, hasError: hasError, searching: searching, searchFilters: _jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", children: _jsxs(ToggleButtonGroup, { sx: { display: 'grid', gridTemplateColumns: '1fr 1fr', alignSelf: 'start' }, size: "small", value: types, onChange: (__, _types) => {
101
116
  if (_types) {
102
117
  setTypes(_types.length < 2 ? _types : []);
103
118
  }
104
- }, children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.templates.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.templates.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.templates.search.prompt') }), renderer: ({ item }, classRenderer) => renderer(item.item, classRenderer()), response: response, onSelect: (item) => navigate(`/templates/view?type=${item.item.type}&analytic=${item.item.analytic}${item.item.detection ? '&detection=' + item.item.detection : ''}`), onCreate: () => navigate('/templates/view'), createPrompt: "route.templates.create", searchPrompt: "route.templates.manager.search", createIcon: _jsx(Article, { sx: { mr: 1 } }) }));
119
+ }, children: [_jsx(ToggleButton, { value: "personal", "aria-label": "personal", children: t('route.templates.manager.personal') }), _jsx(ToggleButton, { value: "global", "aria-label": "global", children: t('route.templates.manager.global') })] }) }), aboveSearch: _jsx(Typography, { sx: theme => ({ fontStyle: 'italic', color: theme.palette.text.disabled, mb: 0.5 }), variant: "body2", children: t('route.templates.search.prompt') }), renderer: ({ item }, classRenderer) => renderer(item.item, !!item.disabled, classRenderer()), response: response, onSelect: (item) => {
120
+ navigate(`/templates/view?type=${item.item.type}&analytic=${item.item.analytic}${item.item.detection ? '&detection=' + item.item.detection : ''}`);
121
+ }, onCreate: () => navigate('/templates/view'), createPrompt: "route.templates.create", searchPrompt: "route.templates.manager.search", createIcon: _jsx(Article, { sx: { mr: 1 } }) }));
105
122
  };
106
123
  const Templates = () => {
107
124
  return (_jsx(TuiListProvider, { children: _jsx(TemplatesBase, {}) }));
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "*": "All values",
3
+ "actions.error": "Action \"{{action}}\" had error(s): {{messages}}",
3
4
  "actions.running": "Action \"{{action}}\" is executing.",
4
- "actions.succeeded": "Action \"{{action}}\" completed successfully.",
5
5
  "actions.skipped": "Action \"{{action}}\" was skipped: {{messages}}",
6
- "actions.error": "Action \"{{action}}\" had error(s): {{messages}}",
6
+ "actions.succeeded": "Action \"{{action}}\" completed successfully.",
7
7
  "add": "Add",
8
8
  "adminmenu": "Admin menu",
9
9
  "adminmenu.config": "Configuration",
@@ -386,6 +386,8 @@
386
386
  "page.settings.local.results.count.description": "How many results should we show per page when showing search results?",
387
387
  "page.settings.local.search.width": "Maximum Results Width",
388
388
  "page.settings.local.search.width.description": "The maximum size (in pixels) the results column in the search page.",
389
+ "page.settings.local.show.hit.summary.graph": "Show the Hit Summary Graph in Hit Search",
390
+ "page.settings.local.show.hit.summary.graph.description": "Shows the Hit Summary Graph in the Hit Search",
389
391
  "page.settings.local.title": "UI Preferences",
390
392
  "page.settings.profile.table.email": "Email",
391
393
  "page.settings.profile.table.groups": "Groups",
@@ -670,6 +672,10 @@
670
672
  "route.templates.manager.personal": "Personal",
671
673
  "route.templates.manager.readonly": "Built-in",
672
674
  "route.templates.manager.search": "Search Templates",
675
+ "route.templates.manager.error.message": "Invalid Detection",
676
+ "route.templates.manager.error.action": "Click to open quick fix options",
677
+ "route.templates.manager.error.modal.title": "Template detection no longer exists",
678
+ "route.templates.manager.error.modal.description": "The template fields are read only and will not be used. Do you want to remove the template?",
673
679
  "route.templates.personal": "Personal",
674
680
  "route.templates.prompt": "Activate autocomplete using [ctrl + space].",
675
681
  "route.templates.readonly.warning": "This is a built-in template, and cannot be edited. To make changes to it, contact",
@@ -2,10 +2,10 @@
2
2
  "*": "Toutes les valeurs",
3
3
  "Protected B": "Protégé B",
4
4
  "Unclassified//Official Use Only": "Non classé//Réservé à des fins officielles",
5
+ "actions.error": "Action \"{{action}}\" a rencontré une ou des erreur(s): {{messages}}",
5
6
  "actions.running": "Action \"{{action}}\" s'exécute.",
6
- "actions.succeeded": "Action \"{{action}}\" achevé avec succès.",
7
7
  "actions.skipped": "Action \"{{action}}\" a été ignorée: {{messages}}",
8
- "actions.error": "Action \"{{action}}\" a rencontré une ou des erreur(s): {{messages}}",
8
+ "actions.succeeded": "Action \"{{action}}\" achevé avec succès.",
9
9
  "add": "Ajouter",
10
10
  "adminmenu": "Menu d'administration",
11
11
  "adminmenu.config": "Configuration",
@@ -391,6 +391,8 @@
391
391
  "page.settings.local.results.count.description": "Combien de résultats devons-nous afficher par page lors de l'affichage des résultats de recherche ?",
392
392
  "page.settings.local.search.width": "Largeur maximale des résultats",
393
393
  "page.settings.local.search.width.description": "Taille maximale (en pixels) de la colonne des résultats dans la page de recherche.",
394
+ "page.settings.local.show.hit.summary.graph": "Afficher le graphique du résumé des résultats dans le volet Résumé des résultats",
395
+ "page.settings.local.show.hit.summary.graph.description": "Affiche le graphique du résumé des résultats dans le volet Résumé des résultats",
394
396
  "page.settings.local.title": "Préférences de l'interface utilisateur",
395
397
  "page.settings.profile.table.email": "Courriel",
396
398
  "page.settings.profile.table.groups": "Groupes",
@@ -674,6 +676,10 @@
674
676
  "route.templates.manager.personal": "Personnel",
675
677
  "route.templates.manager.readonly": "Intégré",
676
678
  "route.templates.manager.search": "Rechercher les modèles",
679
+ "route.templates.manager.error.message": "Détection non valide",
680
+ "route.templates.manager.error.action": "Cliquez ici pour afficher les options de correction rapide",
681
+ "route.templates.manager.error.modal.title": "La détection du modèle n'existe plus",
682
+ "route.templates.manager.error.modal.description": "Les clés du modèle sont en lecture seule et ne seront pas utilisées. Voulez-vous supprimer le modèle ?",
677
683
  "route.templates.personal": "Personnel",
678
684
  "route.templates.prompt": "Activer l'autocomplétion en utilisant [ctrl + espace].",
679
685
  "route.templates.readonly.warning": "Il s'agit d'un modèle intégré qui ne peut pas être modifié. Pour le modifier, veuillez contacter",
package/package.json CHANGED
@@ -96,7 +96,7 @@
96
96
  "internal-slot": "1.0.7"
97
97
  },
98
98
  "type": "module",
99
- "version": "2.19.0-dev.920",
99
+ "version": "2.19.0-dev.922",
100
100
  "exports": {
101
101
  "./i18n": "./i18n.js",
102
102
  "./index.css": "./index.css",
@@ -49,6 +49,7 @@ export declare enum StorageKey {
49
49
  COMPACT_JSON = "compact_json_view",
50
50
  FLATTEN_JSON = "flatten_json_view",
51
51
  FORCE_DRAWER = "force_drawer",
52
+ SHOW_HIT_SUMMARY_GRAPH = "show_hit_summary_graph",
52
53
  LAST_VIEW = "last_view",
53
54
  ONLY_RULES = "only_rules",
54
55
  PAGE_COUNT = "page_count",
@@ -54,6 +54,7 @@ export var StorageKey;
54
54
  StorageKey["COMPACT_JSON"] = "compact_json_view";
55
55
  StorageKey["FLATTEN_JSON"] = "flatten_json_view";
56
56
  StorageKey["FORCE_DRAWER"] = "force_drawer";
57
+ StorageKey["SHOW_HIT_SUMMARY_GRAPH"] = "show_hit_summary_graph";
57
58
  StorageKey["LAST_VIEW"] = "last_view";
58
59
  StorageKey["ONLY_RULES"] = "only_rules";
59
60
  StorageKey["PAGE_COUNT"] = "page_count";