@cccsaurora/howler-ui 2.12.0-dev.48 → 2.12.0-dev.62
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.
- package/components/app/providers/DossierProvider.d.ts +1 -0
- package/components/app/providers/DossierProvider.js +4 -1
- package/components/app/providers/HitProvider.d.ts +1 -0
- package/components/app/providers/HitProvider.js +4 -1
- package/components/app/providers/HitSearchProvider.d.ts +1 -0
- package/components/app/providers/HitSearchProvider.js +5 -1
- package/components/app/providers/ParameterProvider.d.ts +1 -0
- package/components/app/providers/ParameterProvider.js +4 -1
- package/components/app/providers/SocketProvider.js +1 -1
- package/components/app/providers/TemplateProvider.d.ts +1 -0
- package/components/app/providers/TemplateProvider.js +4 -1
- package/components/app/providers/ViewProvider.d.ts +1 -0
- package/components/app/providers/ViewProvider.js +4 -1
- package/components/hooks/useHitSelection.d.ts +1 -2
- package/components/hooks/useHitSelection.js +4 -3
- package/components/routes/hits/search/HitBrowser.js +1 -1
- package/components/routes/hits/search/HitContextMenu.d.ts +1 -0
- package/components/routes/hits/search/HitContextMenu.js +2 -2
- package/components/routes/hits/search/SearchPane.js +1 -1
- package/components/routes/hits/search/grid/AddColumnModal.js +7 -3
- package/components/routes/hits/search/grid/ColumnHeader.d.ts +8 -0
- package/components/routes/hits/search/grid/ColumnHeader.js +30 -0
- package/components/routes/hits/search/grid/EnhancedCell.js +18 -0
- package/components/routes/hits/search/grid/HitGrid.js +29 -26
- package/components/routes/hits/search/grid/HitRow.d.ts +1 -0
- package/components/routes/hits/search/grid/HitRow.js +3 -7
- package/components/routes/hits/search/shared/SearchSpan.js +1 -1
- package/components/routes/settings/LocalSection.js +3 -2
- package/locales/en/translation.json +4 -0
- package/locales/fr/translation.json +4 -0
- package/package.json +1 -1
- package/utils/constants.d.ts +2 -1
- package/utils/constants.js +1 -0
- package/components/routes/hits/search/grid/EnchancedCell.js +0 -18
- /package/components/routes/hits/search/grid/{EnchancedCell.d.ts → EnhancedCell.d.ts} +0 -0
|
@@ -12,4 +12,5 @@ export interface DossierContextType {
|
|
|
12
12
|
}
|
|
13
13
|
export declare const DossierContext: import("use-context-selector").Context<DossierContextType>;
|
|
14
14
|
declare const DossierProvider: FC<PropsWithChildren>;
|
|
15
|
+
export declare const useDossierContextSelector: <Selected>(selector: (value: DossierContextType) => Selected) => Selected;
|
|
15
16
|
export default DossierProvider;
|
|
@@ -3,7 +3,7 @@ import api from '@cccsaurora/howler-ui/api';
|
|
|
3
3
|
import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
|
|
4
4
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
5
5
|
import { useCallback, useEffect, useState } from 'react';
|
|
6
|
-
import { createContext } from 'use-context-selector';
|
|
6
|
+
import { createContext, useContextSelector } from 'use-context-selector';
|
|
7
7
|
export const DossierContext = createContext(null);
|
|
8
8
|
const DossierProvider = ({ children }) => {
|
|
9
9
|
const { dispatchApi } = useMyApi();
|
|
@@ -76,4 +76,7 @@ const DossierProvider = ({ children }) => {
|
|
|
76
76
|
getMatchingDossiers
|
|
77
77
|
}, children: children }));
|
|
78
78
|
};
|
|
79
|
+
export const useDossierContextSelector = (selector) => {
|
|
80
|
+
return useContextSelector(DossierContext, selector);
|
|
81
|
+
};
|
|
79
82
|
export default DossierProvider;
|
|
@@ -17,4 +17,5 @@ export declare const HitContext: import("use-context-selector").Context<HitProvi
|
|
|
17
17
|
* Central repository for storing individual hit data across the application. Allows efficient retrieval of hits across componenents.
|
|
18
18
|
*/
|
|
19
19
|
declare const HitProvider: FC<PropsWithChildren>;
|
|
20
|
+
export declare const useHitContextSelector: <Selected>(selector: (value: HitProviderType) => Selected) => Selected;
|
|
20
21
|
export default HitProvider;
|
|
@@ -3,7 +3,7 @@ import api from '@cccsaurora/howler-ui/api';
|
|
|
3
3
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
4
4
|
import { uniq } from 'lodash-es';
|
|
5
5
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
-
import { createContext } from 'use-context-selector';
|
|
6
|
+
import { createContext, useContextSelector } from 'use-context-selector';
|
|
7
7
|
import { SocketContext } from './SocketProvider';
|
|
8
8
|
export const HitContext = createContext(null);
|
|
9
9
|
/**
|
|
@@ -101,4 +101,7 @@ const HitProvider = ({ children }) => {
|
|
|
101
101
|
loadHits
|
|
102
102
|
}, children: children }));
|
|
103
103
|
};
|
|
104
|
+
export const useHitContextSelector = (selector) => {
|
|
105
|
+
return useContextSelector(HitContext, selector);
|
|
106
|
+
};
|
|
104
107
|
export default HitProvider;
|
|
@@ -22,4 +22,5 @@ interface HitSearchProviderType {
|
|
|
22
22
|
}
|
|
23
23
|
export declare const HitSearchContext: import("use-context-selector").Context<HitSearchProviderType>;
|
|
24
24
|
declare const HitSearchProvider: FC<PropsWithChildren>;
|
|
25
|
+
export declare const useHitSearchContextSelector: <Selected>(selector: (value: HitSearchProviderType) => Selected) => Selected;
|
|
25
26
|
export default HitSearchProvider;
|
|
@@ -11,6 +11,7 @@ import { isMobile } from 'react-device-detect';
|
|
|
11
11
|
import { useLocation, useParams } from 'react-router-dom';
|
|
12
12
|
import { createContext, useContextSelector } from 'use-context-selector';
|
|
13
13
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
14
|
+
import { getStored } from '@cccsaurora/howler-ui/utils/localStorage';
|
|
14
15
|
import Throttler from '@cccsaurora/howler-ui/utils/Throttler';
|
|
15
16
|
import { convertCustomDateRangeToLucene, convertDateToLucene } from '@cccsaurora/howler-ui/utils/utils';
|
|
16
17
|
import { HitContext } from './HitProvider';
|
|
@@ -37,7 +38,7 @@ const HitSearchProvider = ({ children }) => {
|
|
|
37
38
|
const startDate = useContextSelector(ParameterContext, ctx => ctx.startDate);
|
|
38
39
|
const endDate = useContextSelector(ParameterContext, ctx => ctx.endDate);
|
|
39
40
|
const loadHits = useContextSelector(HitContext, ctx => ctx.loadHits);
|
|
40
|
-
const [displayType, setDisplayType] = useState('list');
|
|
41
|
+
const [displayType, setDisplayType] = useState(getStored(StorageKey.DISPLAY_TYPE) ?? 'list');
|
|
41
42
|
const [searching, setSearching] = useState(false);
|
|
42
43
|
const [error, setError] = useState(null);
|
|
43
44
|
const [response, setResponse] = useState();
|
|
@@ -165,4 +166,7 @@ const HitSearchProvider = ({ children }) => {
|
|
|
165
166
|
setFzfSearch
|
|
166
167
|
}, children: children }));
|
|
167
168
|
};
|
|
169
|
+
export const useHitSearchContextSelector = (selector) => {
|
|
170
|
+
return useContextSelector(HitSearchContext, selector);
|
|
171
|
+
};
|
|
168
172
|
export default HitSearchProvider;
|
|
@@ -22,4 +22,5 @@ export declare const ParameterContext: import("use-context-selector").Context<Pa
|
|
|
22
22
|
* Context responsible for tracking updates to query operations in hit and view search.
|
|
23
23
|
*/
|
|
24
24
|
declare const ParameterProvider: FC<PropsWithChildren>;
|
|
25
|
+
export declare const useParameterContextSelector: <Selected>(selector: (value: ParameterProviderType) => Selected) => Selected;
|
|
25
26
|
export default ParameterProvider;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { isEmpty, isNull, isUndefined, omitBy, pickBy } from 'lodash-es';
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
|
|
5
|
-
import { createContext } from 'use-context-selector';
|
|
5
|
+
import { createContext, useContextSelector } from 'use-context-selector';
|
|
6
6
|
import Throttler from '@cccsaurora/howler-ui/utils/Throttler';
|
|
7
7
|
export const ParameterContext = createContext(null);
|
|
8
8
|
const DEFAULT_VALUES = {
|
|
@@ -177,4 +177,7 @@ const ParameterProvider = ({ children }) => {
|
|
|
177
177
|
setFilter: useMemo(() => set('filter'), [set])
|
|
178
178
|
}, children: children }));
|
|
179
179
|
};
|
|
180
|
+
export const useParameterContextSelector = (selector) => {
|
|
181
|
+
return useContextSelector(ParameterContext, selector);
|
|
182
|
+
};
|
|
180
183
|
export default ParameterProvider;
|
|
@@ -121,7 +121,7 @@ const SocketProvider = ({ children }) => {
|
|
|
121
121
|
setRetry(false);
|
|
122
122
|
// Here we go!
|
|
123
123
|
setStatus(Status.CONNECTING);
|
|
124
|
-
const host = window.location.host
|
|
124
|
+
const host = window.location.host;
|
|
125
125
|
const protocol = window.location.protocol.startsWith('http:') ? 'ws' : 'wss';
|
|
126
126
|
const ws = new WebSocket(`${protocol}://${host}/socket/v1/connect`);
|
|
127
127
|
// Add our listeners to the websocket
|
|
@@ -10,4 +10,5 @@ interface TemplateContextType {
|
|
|
10
10
|
}
|
|
11
11
|
export declare const TemplateContext: import("use-context-selector").Context<TemplateContextType>;
|
|
12
12
|
declare const TemplateProvider: FC<PropsWithChildren>;
|
|
13
|
+
export declare const useTemplateContextSelector: <Selected>(selector: (value: TemplateContextType) => Selected) => Selected;
|
|
13
14
|
export default TemplateProvider;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import api from '@cccsaurora/howler-ui/api';
|
|
3
3
|
import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
4
4
|
import { useCallback, useRef, useState } from 'react';
|
|
5
|
-
import { createContext } from 'use-context-selector';
|
|
5
|
+
import { createContext, useContextSelector } from 'use-context-selector';
|
|
6
6
|
const SIX_TAIL_PHISH_DETAILS = [
|
|
7
7
|
'event.start',
|
|
8
8
|
'event.end',
|
|
@@ -97,4 +97,7 @@ const TemplateProvider = ({ children }) => {
|
|
|
97
97
|
}, []);
|
|
98
98
|
return (_jsx(TemplateContext.Provider, { value: { templates, getTemplates, getMatchingTemplate, refresh, loaded }, children: children }));
|
|
99
99
|
};
|
|
100
|
+
export const useTemplateContextSelector = (selector) => {
|
|
101
|
+
return useContextSelector(TemplateContext, selector);
|
|
102
|
+
};
|
|
100
103
|
export default TemplateProvider;
|
|
@@ -15,4 +15,5 @@ export interface ViewContextType {
|
|
|
15
15
|
}
|
|
16
16
|
export declare const ViewContext: import("use-context-selector").Context<ViewContextType>;
|
|
17
17
|
declare const ViewProvider: FC<PropsWithChildren>;
|
|
18
|
+
export declare const useViewContextSelector: <Selected>(selector: (value: ViewContextType) => Selected) => Selected;
|
|
18
19
|
export default ViewProvider;
|
|
@@ -5,7 +5,7 @@ import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
|
|
|
5
5
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
6
6
|
import { useCallback, useEffect, useState } from 'react';
|
|
7
7
|
import { useLocation, useParams } from 'react-router-dom';
|
|
8
|
-
import { createContext } from 'use-context-selector';
|
|
8
|
+
import { createContext, useContextSelector } from 'use-context-selector';
|
|
9
9
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
10
10
|
export const ViewContext = createContext(null);
|
|
11
11
|
const ViewProvider = ({ children }) => {
|
|
@@ -96,4 +96,7 @@ const ViewProvider = ({ children }) => {
|
|
|
96
96
|
getCurrentView
|
|
97
97
|
}, children: children }));
|
|
98
98
|
};
|
|
99
|
+
export const useViewContextSelector = (selector) => {
|
|
100
|
+
return useContextSelector(ViewContext, selector);
|
|
101
|
+
};
|
|
99
102
|
export default ViewProvider;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import type { HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
|
|
2
1
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
3
2
|
import type React from 'react';
|
|
4
|
-
declare const useHitSelection: (
|
|
3
|
+
declare const useHitSelection: () => {
|
|
5
4
|
lastSelected: string;
|
|
6
5
|
setLastSelected: React.Dispatch<React.SetStateAction<string>>;
|
|
7
6
|
onClick: (e: React.MouseEvent<HTMLDivElement>, hit: Hit) => void;
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { useAppBreadcrumbs } from '@cccsaurora/howler-ui/commons/components/app/hooks';
|
|
2
2
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
3
|
+
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
3
4
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
4
5
|
import useMySitemap from '@cccsaurora/howler-ui/components/hooks/useMySitemap';
|
|
5
6
|
import { useCallback, useState } from 'react';
|
|
6
7
|
import { useNavigate } from 'react-router-dom';
|
|
7
8
|
import { useContextSelector } from 'use-context-selector';
|
|
8
|
-
const useHitSelection = (
|
|
9
|
+
const useHitSelection = () => {
|
|
9
10
|
const navigate = useNavigate();
|
|
10
11
|
const { setItems } = useAppBreadcrumbs();
|
|
11
12
|
const { routes } = useMySitemap();
|
|
13
|
+
const response = useContextSelector(HitSearchContext, ctx => ctx.response);
|
|
12
14
|
const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
|
|
13
15
|
const addHitToSelection = useContextSelector(HitContext, ctx => ctx.addHitToSelection);
|
|
14
16
|
const removeHitFromSelection = useContextSelector(HitContext, ctx => ctx.removeHitFromSelection);
|
|
@@ -65,11 +67,10 @@ const useHitSelection = (response) => {
|
|
|
65
67
|
lastSelected,
|
|
66
68
|
navigate,
|
|
67
69
|
removeHitFromSelection,
|
|
68
|
-
response
|
|
70
|
+
response,
|
|
69
71
|
routes,
|
|
70
72
|
selectedHits,
|
|
71
73
|
setItems,
|
|
72
|
-
setLastSelected,
|
|
73
74
|
setSelected
|
|
74
75
|
]);
|
|
75
76
|
return { lastSelected, setLastSelected, onClick };
|
|
@@ -118,7 +118,7 @@ const HitBrowser = () => {
|
|
|
118
118
|
}
|
|
119
119
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
120
120
|
}, [addHitToSelection, location.pathname, removeHitFromSelection, response, routeParams.id, selected, setSelected]);
|
|
121
|
-
return (_jsxs(Stack, { direction: "row", flex: 1, sx: { overflow: 'hidden' }, children: [_jsxs(Box, { position: "relative", flex: 1, height: "100%", display: "flex", sx: [{ overflow: 'auto' }, displayType === 'list' && !isNull(searchPaneWidth) && { maxWidth: searchPaneWidth }], children: [_jsx(ErrorBoundary, { children: displayType === 'list' ? _jsx(SearchPane, {}) : _jsx(HitGrid, {}) }), _jsx(Collapse, { in: showSelectBar, unmountOnExit: true, sx: {
|
|
121
|
+
return (_jsxs(Stack, { direction: "row", flex: 1, sx: { overflow: 'hidden' }, children: [_jsxs(Box, { position: "relative", flex: 1.15, height: "100%", display: "flex", sx: [{ overflow: 'auto' }, displayType === 'list' && !isNull(searchPaneWidth) && { maxWidth: searchPaneWidth }], children: [_jsx(ErrorBoundary, { children: displayType === 'list' ? _jsx(SearchPane, {}) : _jsx(HitGrid, {}) }), _jsx(Collapse, { in: showSelectBar, unmountOnExit: true, sx: {
|
|
122
122
|
position: 'absolute',
|
|
123
123
|
bottom: 0,
|
|
124
124
|
left: 0,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { FC, PropsWithChildren } from 'react';
|
|
2
2
|
interface HitContextMenuProps {
|
|
3
3
|
getSelectedId: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => string;
|
|
4
|
+
Component?: React.ElementType;
|
|
4
5
|
}
|
|
5
6
|
declare const HitContextMenu: FC<PropsWithChildren<HitContextMenuProps>>;
|
|
6
7
|
export default HitContextMenu;
|
|
@@ -22,7 +22,7 @@ const ICON_MAP = {
|
|
|
22
22
|
vote: _jsx(HowToVote, {}),
|
|
23
23
|
action: _jsx(Edit, {})
|
|
24
24
|
};
|
|
25
|
-
const HitContextMenu = ({ children, getSelectedId }) => {
|
|
25
|
+
const HitContextMenu = ({ children, getSelectedId, Component = Box }) => {
|
|
26
26
|
const { t } = useTranslation();
|
|
27
27
|
const analyticContext = useContext(AnalyticContext);
|
|
28
28
|
const { dispatchApi } = useMyApi();
|
|
@@ -104,7 +104,7 @@ const HitContextMenu = ({ children, getSelectedId }) => {
|
|
|
104
104
|
setAnalytic(null);
|
|
105
105
|
}
|
|
106
106
|
}, [anchorEl]);
|
|
107
|
-
return (_jsxs(
|
|
107
|
+
return (_jsxs(Component, { id: "contextMenu", onContextMenu: onContextMenu, children: [children, _jsxs(Menu, { id: "hit-menu", open: !!anchorEl, anchorEl: anchorEl, onClose: () => setAnchorEl(null), slotProps: {
|
|
108
108
|
paper: {
|
|
109
109
|
sx: {
|
|
110
110
|
transform: `translate(${clickLocation[0]}px, ${clickLocation[1]}px) !important`,
|
|
@@ -94,7 +94,7 @@ const SearchPane = () => {
|
|
|
94
94
|
const response = useContextSelector(HitSearchContext, ctx => ctx.response);
|
|
95
95
|
const error = useContextSelector(HitSearchContext, ctx => ctx.error);
|
|
96
96
|
const viewId = useContextSelector(HitSearchContext, ctx => ctx.viewId);
|
|
97
|
-
const { onClick } = useHitSelection(
|
|
97
|
+
const { onClick } = useHitSelection();
|
|
98
98
|
const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
|
|
99
99
|
const clearSelectedHits = useContextSelector(HitContext, ctx => ctx.clearSelectedHits);
|
|
100
100
|
const bundleHit = useContextSelector(HitContext, ctx => location.pathname.startsWith('/bundles') ? ctx.hits[routeParams.id] : null);
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Add, Check } from '@mui/icons-material';
|
|
3
|
-
import { Autocomplete, Chip, Divider, Grid, Popover, Stack, TextField } from '@mui/material';
|
|
3
|
+
import { Autocomplete, Chip, Divider, Grid, IconButton, Popover, Stack, TextField } from '@mui/material';
|
|
4
4
|
import { FieldContext } from '@cccsaurora/howler-ui/components/app/providers/FieldProvider';
|
|
5
5
|
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
6
6
|
import { TemplateContext } from '@cccsaurora/howler-ui/components/app/providers/TemplateProvider';
|
|
7
7
|
import { sortBy, uniq } from 'lodash-es';
|
|
8
|
-
import { memo, useContext, useMemo } from 'react';
|
|
8
|
+
import { memo, useContext, useMemo, useState } from 'react';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import { useContextSelector } from 'use-context-selector';
|
|
11
11
|
const AddColumnModal = ({ open, onClose, anchorEl, addColumn, columns }) => {
|
|
@@ -13,9 +13,13 @@ const AddColumnModal = ({ open, onClose, anchorEl, addColumn, columns }) => {
|
|
|
13
13
|
const { hitFields } = useContext(FieldContext);
|
|
14
14
|
const response = useContextSelector(HitSearchContext, ctx => ctx.response);
|
|
15
15
|
const getMatchingTemplate = useContextSelector(TemplateContext, ctx => ctx.getMatchingTemplate);
|
|
16
|
+
const [columnToAdd, setColumnToAdd] = useState(null);
|
|
16
17
|
const options = useMemo(() => hitFields.map(field => field.key), [hitFields]);
|
|
17
18
|
const suggestions = useMemo(() => uniq((response?.items ?? []).flatMap(_hit => getMatchingTemplate(_hit)?.keys ?? [])), [getMatchingTemplate, response?.items]);
|
|
18
|
-
return (_jsx(Popover, { open: open, onClose: onClose, anchorEl: anchorEl, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, children: _jsxs(Stack, { spacing: 1, p: 1, width: "500px", children: [_jsx(Autocomplete, { options: options, renderInput: params => _jsx(TextField, { fullWidth: true, placeholder: t('hit.fields'), ...params })
|
|
19
|
+
return (_jsx(Popover, { open: open, onClose: onClose, anchorEl: anchorEl, anchorOrigin: { vertical: 'bottom', horizontal: 'left' }, children: _jsxs(Stack, { spacing: 1, p: 1, width: "500px", children: [_jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Autocomplete, { sx: { flex: 1 }, size: "small", options: options, value: columnToAdd, renderInput: params => _jsx(TextField, { fullWidth: true, placeholder: t('hit.fields'), ...params }), onChange: (_ev, value) => setColumnToAdd(value) }), _jsx(IconButton, { disabled: !columnToAdd, onClick: () => {
|
|
20
|
+
addColumn(columnToAdd);
|
|
21
|
+
setColumnToAdd(null);
|
|
22
|
+
}, children: _jsx(Add, {}) })] }), _jsx(Divider, { orientation: "horizontal" }), _jsx(Grid, { container: true, spacing: 1, children: sortBy(suggestions.map(key => ({ key, used: columns.includes(key) })), 'used').map(({ key, used }) => {
|
|
19
23
|
return (_jsx(Grid, { item: true, children: _jsx(Chip, { size: "small", variant: "outlined", color: used ? 'success' : 'default', label: key, icon: used ? _jsx(Check, {}) : _jsx(Add, {}), onClick: () => addColumn(key), disabled: used }) }, key));
|
|
20
24
|
}) })] }) }));
|
|
21
25
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FC, type SetStateAction } from 'react';
|
|
2
|
+
declare const ColumnHeader: FC<{
|
|
3
|
+
width: string;
|
|
4
|
+
col: string;
|
|
5
|
+
setColumns: (val: SetStateAction<string[]>) => void;
|
|
6
|
+
onMouseDown: (col: string, event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
|
7
|
+
}>;
|
|
8
|
+
export default ColumnHeader;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useSortable } from '@dnd-kit/sortable';
|
|
3
|
+
import { CSS } from '@dnd-kit/utilities';
|
|
4
|
+
import { DragIndicator, Remove } from '@mui/icons-material';
|
|
5
|
+
import { Box, IconButton, Stack, TableCell, Tooltip, useTheme } from '@mui/material';
|
|
6
|
+
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
7
|
+
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
8
|
+
import { useContext } from 'react';
|
|
9
|
+
const ColumnHeader = ({ col, width, setColumns, onMouseDown }) => {
|
|
10
|
+
const theme = useTheme();
|
|
11
|
+
const { config } = useContext(ApiConfigContext);
|
|
12
|
+
const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: col });
|
|
13
|
+
return (_jsx(TableCell, { ref: setNodeRef, sx: {
|
|
14
|
+
borderRight: 'thin solid',
|
|
15
|
+
borderRightColor: 'divider',
|
|
16
|
+
py: 0.5,
|
|
17
|
+
position: 'relative'
|
|
18
|
+
}, style: { transform: CSS.Translate.toString(transform), transition }, children: _jsxs(Stack, { className: `col-${col.replaceAll('.', '-')}`, direction: "row", spacing: 1, alignItems: "center", sx: [{ minWidth: '220px' }, !!width ? { width, maxWidth: width } : { maxWidth: '300px' }], children: [_jsx(Tooltip, { title: config.indexes.hit[col].description, children: _jsx("span", { children: col }) }), _jsx(FlexOne, {}), _jsx(IconButton, { size: "small", sx: { fontSize: '1rem' }, onClick: () => setColumns(_columns => _columns.filter(_col => _col !== col)), children: _jsx(Remove, { fontSize: "inherit" }) }), _jsx(DragIndicator, { fontSize: "small", sx: { cursor: 'grab' }, ...attributes, ...listeners }), _jsx(Box, { sx: {
|
|
19
|
+
position: 'absolute',
|
|
20
|
+
top: theme.spacing(0.75),
|
|
21
|
+
bottom: theme.spacing(0.75),
|
|
22
|
+
right: -3,
|
|
23
|
+
width: '5px',
|
|
24
|
+
borderRight: 'thin solid',
|
|
25
|
+
borderLeft: 'thin solid',
|
|
26
|
+
borderColor: 'divider',
|
|
27
|
+
cursor: 'col-resize'
|
|
28
|
+
}, onMouseDown: e => onMouseDown(col, e) })] }) }));
|
|
29
|
+
};
|
|
30
|
+
export default ColumnHeader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable react/no-array-index-key */
|
|
3
|
+
import { Stack, TableCell } from '@mui/material';
|
|
4
|
+
import PluginTypography from '@cccsaurora/howler-ui/components/elements/PluginTypography';
|
|
5
|
+
import { memo } from 'react';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
const EnhancedCell = ({ value: rawValue, sx = {}, className }) => {
|
|
8
|
+
const { t } = useTranslation();
|
|
9
|
+
if (!rawValue) {
|
|
10
|
+
return _jsx(TableCell, { style: { borderBottom: 'none' }, children: t('none') });
|
|
11
|
+
}
|
|
12
|
+
const values = (Array.isArray(rawValue) ? rawValue : [rawValue]).filter(_value => !!_value);
|
|
13
|
+
return (_jsx(TableCell, { sx: { borderBottom: 'none', borderRight: 'thin solid', borderRightColor: 'divider', fontSize: '0.8rem' }, children: _jsx(Stack, { direction: "row", className: className, spacing: 0.5, sx: [
|
|
14
|
+
{ display: 'flex', justifyContent: 'start', width: '100%', overflow: 'hidden' },
|
|
15
|
+
...(Array.isArray(sx) ? sx : [sx])
|
|
16
|
+
], children: values.map((value, index) => (_jsx(PluginTypography, { context: "table", sx: { fontSize: 'inherit', textOverflow: 'ellipsis' }, value: value, children: value }, value + index))) }) }));
|
|
17
|
+
};
|
|
18
|
+
export default memo(EnhancedCell);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { DndContext, KeyboardSensor, PointerSensor, pointerWithin, useSensor, useSensors } from '@dnd-kit/core';
|
|
3
|
+
import { arrayMove, SortableContext, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
|
|
4
|
+
import { Add, FormatIndentDecrease, FormatIndentIncrease, Info, List, Search, TableChart } from '@mui/icons-material';
|
|
5
|
+
import { IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow, ToggleButton, ToggleButtonGroup, Typography, useTheme } from '@mui/material';
|
|
4
6
|
import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
|
|
5
|
-
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
6
7
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
7
8
|
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
8
9
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
@@ -11,24 +12,28 @@ import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/Fle
|
|
|
11
12
|
import SearchTotal from '@cccsaurora/howler-ui/components/elements/addons/search/SearchTotal';
|
|
12
13
|
import DevelopmentBanner from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentBanner';
|
|
13
14
|
import DevelopmentIcon from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentIcon';
|
|
15
|
+
import useHitSelection from '@cccsaurora/howler-ui/components/hooks/useHitSelection';
|
|
14
16
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
15
17
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
16
18
|
import { useTranslation } from 'react-i18next';
|
|
17
19
|
import { useLocation, useParams } from 'react-router-dom';
|
|
18
20
|
import { useContextSelector } from 'use-context-selector';
|
|
19
21
|
import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
|
|
22
|
+
import HitContextMenu from '../HitContextMenu';
|
|
20
23
|
import HitQuery from '../HitQuery';
|
|
21
24
|
import QuerySettings from '../shared/QuerySettings';
|
|
22
25
|
import ViewLink from '../ViewLink';
|
|
23
26
|
import AddColumnModal from './AddColumnModal';
|
|
27
|
+
import ColumnHeader from './ColumnHeader';
|
|
24
28
|
import HitRow from './HitRow';
|
|
25
29
|
const HitGrid = () => {
|
|
26
30
|
const { t } = useTranslation();
|
|
27
|
-
const { config } = useContext(ApiConfigContext);
|
|
28
31
|
const { getIdFromName } = useContext(AnalyticContext);
|
|
29
32
|
const routeParams = useParams();
|
|
30
33
|
const location = useLocation();
|
|
31
34
|
const theme = useTheme();
|
|
35
|
+
const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
|
|
36
|
+
const { onClick } = useHitSelection();
|
|
32
37
|
const search = useContextSelector(HitSearchContext, ctx => ctx.search);
|
|
33
38
|
const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
|
|
34
39
|
const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
|
|
@@ -92,6 +97,8 @@ const HitGrid = () => {
|
|
|
92
97
|
window.removeEventListener('mouseup', onMouseUp);
|
|
93
98
|
}, [onMouseMove]);
|
|
94
99
|
const onMouseDown = useCallback((col, event) => {
|
|
100
|
+
event.stopPropagation();
|
|
101
|
+
event.preventDefault();
|
|
95
102
|
resizingCol.current = [col, event.target.parentElement];
|
|
96
103
|
window.addEventListener('mousemove', onMouseMove);
|
|
97
104
|
window.addEventListener('mouseup', onMouseUp);
|
|
@@ -102,6 +109,22 @@ const HitGrid = () => {
|
|
|
102
109
|
search(query, true);
|
|
103
110
|
}
|
|
104
111
|
}, [query, search]);
|
|
112
|
+
const handleDragEnd = useCallback((event) => {
|
|
113
|
+
const { active, over } = event;
|
|
114
|
+
if (active.id !== over.id) {
|
|
115
|
+
const oldIndex = (columns ?? []).findIndex(entry => entry === active.id);
|
|
116
|
+
const newIndex = (columns ?? []).findIndex(entry => entry === over.id);
|
|
117
|
+
setColumns(arrayMove(columns, oldIndex, newIndex));
|
|
118
|
+
}
|
|
119
|
+
}, [columns]);
|
|
120
|
+
const getSelectedId = useCallback((event) => {
|
|
121
|
+
const target = event.target;
|
|
122
|
+
const selectedElement = target.closest('[id]');
|
|
123
|
+
if (!selectedElement) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
return selectedElement.id;
|
|
127
|
+
}, []);
|
|
105
128
|
return (_jsxs(Stack, { spacing: 1, p: 2, width: "100%", sx: { overflow: 'hidden', height: `calc(100vh - ${theme.spacing(showSelectBar ? 13 : 8)})` }, children: [_jsx(DevelopmentBanner, {}), _jsx(ViewLink, {}), _jsxs(Stack, { direction: "row", justifyContent: "space-between", alignItems: "center", children: [_jsx(Typography, { sx: { color: 'text.secondary', fontSize: '0.9em', fontStyle: 'italic', mb: 0.5, textAlign: 'left' }, variant: "body2", children: t('hit.search.prompt') }), _jsx(DevelopmentIcon, {})] }), _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsxs(Stack, { position: "relative", flex: 1, children: [_jsx(HitQuery, { disabled: viewId && !selectedView, searching: searching, triggerSearch: search, compact: true }), searching && (_jsx(LinearProgress, { sx: {
|
|
106
129
|
position: 'absolute',
|
|
107
130
|
left: 0,
|
|
@@ -109,29 +132,9 @@ const HitGrid = () => {
|
|
|
109
132
|
bottom: 0,
|
|
110
133
|
borderBottomLeftRadius: theme.shape.borderRadius,
|
|
111
134
|
borderBottomRightRadius: theme.shape.borderRadius
|
|
112
|
-
} }))] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", children: [_jsx(ToggleButton, { value: "list", children: _jsx(List, {}) }), _jsx(ToggleButton, { value: "grid", children: _jsx(TableChart, {}) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", sx: { '& > *': { flex: 1 } }, children: [_jsx(QuerySettings, {}), response && _jsx(SearchTotal, { offset: response.offset, pageLength: response.rows, total: response.total }), _jsxs(Stack, { direction: "row", children: [_jsx(FlexOne, {}), _jsx(IconButton, { ref: columnModalRef, onClick: () => setShowAddColumn(true), children: _jsx(Add, { fontSize: "small" }) })] }), _jsx(AddColumnModal, { anchorEl: columnModalRef.current, open: showAddColumn, onClose: () => setShowAddColumn(false), columns: columns, addColumn: key => setColumns(_columns => [..._columns, key]) })] }), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsxs(Table, { sx: { '& td,th': { px: 1, py: 0.25, whiteSpace: 'nowrap' } }, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { sx: {
|
|
135
|
+
} }))] }), _jsxs(ToggleButtonGroup, { exclusive: true, value: displayType, onChange: (__, value) => setDisplayType(value), size: "small", children: [_jsx(ToggleButton, { value: "list", children: _jsx(List, {}) }), _jsx(ToggleButton, { value: "grid", children: _jsx(TableChart, {}) })] })] }), _jsxs(Stack, { direction: "row", spacing: 1, width: "100%", sx: { '& > *': { flex: 1 } }, children: [_jsx(QuerySettings, {}), response && (_jsx(SearchTotal, { sx: { alignSelf: 'center' }, color: "text.secondary", offset: response.offset, pageLength: response.rows, total: response.total })), _jsxs(Stack, { direction: "row", children: [_jsx(FlexOne, {}), _jsx(IconButton, { ref: columnModalRef, onClick: () => setShowAddColumn(true), children: _jsx(Add, { fontSize: "small" }) })] }), _jsx(AddColumnModal, { anchorEl: columnModalRef.current, open: showAddColumn, onClose: () => setShowAddColumn(false), columns: columns, addColumn: key => setColumns(_columns => [..._columns, key]) })] }), _jsxs(Stack, { component: Paper, spacing: 1, width: "100%", height: "100%", sx: { overflow: 'auto', flex: 1 }, onScroll: onScroll, children: [_jsxs(Table, { sx: { '& td,th': { px: 1, py: 0.25, whiteSpace: 'nowrap' } }, children: [_jsx(TableHead, { children: _jsxs(TableRow, { children: [_jsx(TableCell, { sx: {
|
|
113
136
|
borderRight: 'thin solid',
|
|
114
137
|
borderRightColor: 'divider'
|
|
115
|
-
}, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), columns.map(col => (_jsx(TableCell, { sx: {
|
|
116
|
-
borderRight: 'thin solid',
|
|
117
|
-
borderRightColor: 'divider',
|
|
118
|
-
py: 0.5,
|
|
119
|
-
position: 'relative'
|
|
120
|
-
}, children: _jsxs(Stack, { className: `col-${col.replaceAll('.', '-')}`, direction: "row", spacing: 1, alignItems: "center", sx: [
|
|
121
|
-
{ minWidth: '150px' },
|
|
122
|
-
!!columnWidths[col]
|
|
123
|
-
? { width: columnWidths[col], maxWidth: columnWidths[col] }
|
|
124
|
-
: { maxWidth: '300px' }
|
|
125
|
-
], children: [_jsx(Tooltip, { title: config.indexes.hit[col].description, children: _jsx("span", { children: col }) }), _jsx(FlexOne, {}), _jsx(IconButton, { size: "small", sx: { fontSize: '1rem' }, onClick: () => setColumns(_columns => _columns.filter(_col => _col !== col)), children: _jsx(Remove, { fontSize: "inherit" }) }), _jsx(Box, { sx: {
|
|
126
|
-
position: 'absolute',
|
|
127
|
-
top: theme.spacing(0.75),
|
|
128
|
-
bottom: theme.spacing(0.75),
|
|
129
|
-
right: -3,
|
|
130
|
-
width: '5px',
|
|
131
|
-
borderRight: 'thin solid',
|
|
132
|
-
borderLeft: 'thin solid',
|
|
133
|
-
borderColor: 'divider',
|
|
134
|
-
cursor: 'col-resize'
|
|
135
|
-
}, onMouseDown: e => onMouseDown(col, e) })] }) }, col))), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(TableBody, { children: [response?.items.map(hit => (_jsx(HitRow, { hit: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
|
|
138
|
+
}, children: _jsx(IconButton, { onClick: () => setCollapseMainColumn(!collapseMainColumn), children: collapseMainColumn ? (_jsx(FormatIndentIncrease, { fontSize: "small" })) : (_jsx(FormatIndentDecrease, { fontSize: "small" })) }) }), _jsx(DndContext, { sensors: sensors, collisionDetection: pointerWithin, onDragEnd: handleDragEnd, children: _jsx(SortableContext, { items: columns, children: columns.map(col => (_jsx(ColumnHeader, { col: col, width: columnWidths[col], onMouseDown: onMouseDown, setColumns: setColumns }, col))) }) }), _jsx(TableCell, { sx: { width: '100%' } })] }) }), _jsxs(HitContextMenu, { Component: TableBody, getSelectedId: getSelectedId, children: [response?.items.map(hit => (_jsx(HitRow, { hit: hit, analyticIds: analyticIds, columns: columns, columnWidths: columnWidths, collapseMainColumn: collapseMainColumn, onClick: onClick }, hit.howler.id))), _jsx(TableRow, { children: _jsx(TableCell, { colSpan: columns.length + 2, children: _jsx(Stack, { alignItems: "center", justifyContent: "center", py: 0.5, px: 1, children: _jsx(IconButton, { onClick: () => search(query, true), children: _jsx(Search, {}) }) }) }) })] })] }), (response?.total ?? 0) < 1 && (_jsx(Stack, { direction: "row", spacing: 1, alignItems: "center", p: 1, justifyContent: "center", flex: 1, children: _jsxs(Typography, { variant: "h3", color: "text.secondary", display: "flex", flexDirection: "row", alignItems: "center", children: [_jsx(Info, { fontSize: "inherit", sx: { color: 'text.secondary', mr: 1 } }), _jsx("span", { children: t('app.list.empty') })] }) }))] })] }));
|
|
136
139
|
};
|
|
137
140
|
export default HitGrid;
|
|
@@ -2,26 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { KeyboardArrowUp } from '@mui/icons-material';
|
|
3
3
|
import { Box, Collapse, IconButton, lighten, Stack, TableCell, TableRow, Typography, useTheme } from '@mui/material';
|
|
4
4
|
import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
|
|
5
|
-
import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
|
|
6
5
|
import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
|
|
7
6
|
import Assigned from '@cccsaurora/howler-ui/components/elements/hit/elements/Assigned';
|
|
8
7
|
import EscalationChip from '@cccsaurora/howler-ui/components/elements/hit/elements/EscalationChip';
|
|
9
8
|
import HitCard from '@cccsaurora/howler-ui/components/elements/hit/HitCard';
|
|
10
9
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
11
|
-
import useHitSelection from '@cccsaurora/howler-ui/components/hooks/useHitSelection';
|
|
12
10
|
import { get } from 'lodash-es';
|
|
13
11
|
import { memo, useState } from 'react';
|
|
14
12
|
import { useTranslation } from 'react-i18next';
|
|
15
13
|
import { Link } from 'react-router-dom';
|
|
16
14
|
import { useContextSelector } from 'use-context-selector';
|
|
17
|
-
import EnhancedCell from './
|
|
18
|
-
const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn }) => {
|
|
15
|
+
import EnhancedCell from './EnhancedCell';
|
|
16
|
+
const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn, onClick }) => {
|
|
19
17
|
const theme = useTheme();
|
|
20
18
|
const { t } = useTranslation();
|
|
21
19
|
const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
|
|
22
|
-
const response = useContextSelector(HitSearchContext, ctx => ctx.response);
|
|
23
20
|
const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
|
|
24
|
-
const { onClick } = useHitSelection(response);
|
|
25
21
|
const [expandRow, setExpandRow] = useState(false);
|
|
26
22
|
return (_jsxs(_Fragment, { children: [_jsxs(TableRow, { id: hit.howler.id, onClick: ev => onClick(ev, hit), sx: [
|
|
27
23
|
{
|
|
@@ -49,6 +45,6 @@ const HitRow = ({ hit, analyticIds, columns, columnWidths, collapseMainColumn })
|
|
|
49
45
|
e.preventDefault();
|
|
50
46
|
e.stopPropagation();
|
|
51
47
|
setExpandRow(_expanded => !_expanded);
|
|
52
|
-
}, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [_jsx(EscalationChip, { hit: hit, layout: HitLayout.DENSE, hideLabel: true }), _jsxs(Typography, { sx: { textWrap: 'nowrap', whiteSpace: 'nowrap', fontSize: 'inherit' }, children: [analyticIds[hit.howler.analytic] ? (_jsx(Link, { to: `/analytics/${analyticIds[hit.howler.analytic]}`, onClick: e => e.stopPropagation(), children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }), hit.howler.assignment !== 'unassigned' && _jsx(Assigned, { hit: hit, layout: HitLayout.DENSE, hideLabel: true })] }) })] }) }), columns.map(col => (_jsx(EnhancedCell, { className: `col-${col.replaceAll('.', '-')}`, value: get(hit, col) ?? t('none'), sx: columnWidths[col] ? { width: columnWidths[col] } : { width: '
|
|
48
|
+
}, children: _jsx(KeyboardArrowUp, {}) }), _jsx(Collapse, { in: !collapseMainColumn, orientation: "horizontal", unmountOnExit: true, children: _jsxs(Stack, { direction: "row", spacing: 1, flexWrap: "nowrap", children: [_jsx(EscalationChip, { hit: hit, layout: HitLayout.DENSE, hideLabel: true }), _jsxs(Typography, { sx: { textWrap: 'nowrap', whiteSpace: 'nowrap', fontSize: 'inherit' }, children: [analyticIds[hit.howler.analytic] ? (_jsx(Link, { to: `/analytics/${analyticIds[hit.howler.analytic]}`, onClick: e => e.stopPropagation(), children: hit.howler.analytic })) : (hit.howler.analytic), hit.howler.detection && ': ', hit.howler.detection] }), hit.howler.assignment !== 'unassigned' && _jsx(Assigned, { hit: hit, layout: HitLayout.DENSE, hideLabel: true })] }) })] }) }), columns.map(col => (_jsx(EnhancedCell, { className: `col-${col.replaceAll('.', '-')}`, value: get(hit, col) ?? t('none'), sx: columnWidths[col] ? { width: columnWidths[col] } : { width: '220px', maxWidth: '300px' } }, col)))] }, hit.howler.id), _jsx(TableRow, { onClick: ev => onClick(ev, hit), children: _jsx(TableCell, { colSpan: columns.length + 2, style: { paddingBottom: 0, paddingTop: 0 }, children: _jsx(Collapse, { in: expandRow, unmountOnExit: true, children: _jsx(Box, { width: "100%", maxWidth: "1200px", margin: 1, children: _jsx(HitCard, { id: hit.howler.id, layout: HitLayout.NORMAL }) }) }) }) })] }));
|
|
53
49
|
};
|
|
54
50
|
export default memo(HitRow);
|
|
@@ -35,6 +35,6 @@ const SearchSpan = ({ omitCustom = false, size }) => {
|
|
|
35
35
|
}
|
|
36
36
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
37
37
|
}, [selectedView]);
|
|
38
|
-
return (_jsx(Autocomplete, { fullWidth: true, sx: { minWidth: '
|
|
38
|
+
return (_jsx(Autocomplete, { fullWidth: true, sx: { minWidth: '200px', flex: 1 }, size: size ?? 'small', value: span, options: omitCustom ? DATE_RANGES.slice(0, DATE_RANGES.length - 1) : DATE_RANGES, renderInput: _params => _jsx(TextField, { ..._params, label: t('hit.search.span') }), getOptionLabel: option => t(option), onChange: (_, value) => setSpan(value), disableClearable: true }));
|
|
39
39
|
};
|
|
40
40
|
export default memo(SearchSpan);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
/* eslint-disable react/jsx-no-literals */
|
|
3
|
-
import { ViewComfy, ViewCompact, ViewModule } from '@mui/icons-material';
|
|
3
|
+
import { List, TableChart, ViewComfy, ViewCompact, ViewModule } from '@mui/icons-material';
|
|
4
4
|
import { MenuItem, Select, Stack, TableCell, TableRow, ToggleButton, ToggleButtonGroup, Typography } from '@mui/material';
|
|
5
5
|
import { HitLayout } from '@cccsaurora/howler-ui/components/elements/hit/HitLayout';
|
|
6
6
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
@@ -18,8 +18,9 @@ const LocalSection = () => {
|
|
|
18
18
|
const [flattenJson, setFlattenJson] = useMyLocalStorageItem(StorageKey.FLATTEN_JSON, false);
|
|
19
19
|
const [forceDrawer, setForceDrawer] = useMyLocalStorageItem(StorageKey.FORCE_DRAWER, false);
|
|
20
20
|
const [hitLayout, setHitLayout] = useMyLocalStorageItem(StorageKey.HIT_LAYOUT, false);
|
|
21
|
+
const [displayType, setDisplayType] = useMyLocalStorageItem(StorageKey.DISPLAY_TYPE, 'list');
|
|
21
22
|
const [pageCount, setPageCount] = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25);
|
|
22
23
|
const [searchWidth, setSearchWidth] = useMyLocalStorageItem(StorageKey.SEARCH_PANE_WIDTH, null);
|
|
23
|
-
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') }) }) }), _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'))] }));
|
|
24
|
+
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') }) }) }), _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'))] }));
|
|
24
25
|
};
|
|
25
26
|
export default LocalSection;
|
|
@@ -372,6 +372,10 @@
|
|
|
372
372
|
"page.settings.local.flatten.json.description": "Instead of showing the object structure of the JSON, show it as flattened keys.",
|
|
373
373
|
"page.settings.local.details.drawer": "Always use drawer in Hit Search",
|
|
374
374
|
"page.settings.local.details.drawer.description": "Instead of switching between a divided view and the drawer on different screen sizes, this setting forces Howler to always use the drawer.",
|
|
375
|
+
"page.settings.local.hits.display_type": "Default display type for results",
|
|
376
|
+
"page.settings.local.hits.display_type.description": "This setting allows you to decide whether to use the list or grid view by default when searching.",
|
|
377
|
+
"page.settings.local.hits.display_type.list": "List",
|
|
378
|
+
"page.settings.local.hits.display_type.grid": "Grid",
|
|
375
379
|
"page.settings.local.hits.layout": "Change Layout",
|
|
376
380
|
"page.settings.local.hits.layout.description": "This setting allows you to decide how much padding and spacing, as well as font size and element size, we use when rendering the UI. The denser the UI, the more data can be viewed.",
|
|
377
381
|
"page.settings.local.hits.layout.dense": "Dense",
|
|
@@ -373,6 +373,10 @@
|
|
|
373
373
|
"page.settings.local.flatten.json.description": "Au lieu d'afficher la structure d'objet du JSON, affichez-la sous forme de clés aplaties",
|
|
374
374
|
"page.settings.local.details.drawer": "Toujours utiliser le tiroir dans la recherche du hit",
|
|
375
375
|
"page.settings.local.details.drawer.description": "Au lieu de basculer entre une vue divisée et le tiroir en fonction de la taille de l'écran, ce paramètre oblige Howler à toujours utiliser le tiroir.",
|
|
376
|
+
"page.settings.local.hits.display_type": "Type d'affichage par défaut pour les résultats",
|
|
377
|
+
"page.settings.local.hits.display_type.description": "Ce paramètre vous permet de choisir entre l'affichage sous forme de liste ou de grille par défaut lors d'une recherche.",
|
|
378
|
+
"page.settings.local.hits.display_type.list": "Liste",
|
|
379
|
+
"page.settings.local.hits.display_type.grid": "Grille",
|
|
376
380
|
"page.settings.local.hits.layout": "Change Layout",
|
|
377
381
|
"page.settings.local.hits.layout.description": "Ce paramètre vous permet de décider de la quantité de rembourrage et d'espacement, ainsi que de la taille de la police et de la taille de l'élément, que nous utilisons lors du rendu de l'interface utilisateur. Plus l'interface est dense, plus il est possible d'afficher de données.",
|
|
378
382
|
"page.settings.local.hits.layout.dense": "Dense",
|
package/package.json
CHANGED
package/utils/constants.d.ts
CHANGED
|
@@ -55,7 +55,8 @@ export declare enum StorageKey {
|
|
|
55
55
|
SEARCH_PANE_WIDTH = "search_pane_width",
|
|
56
56
|
GRID_COLLAPSE_COLUMN = "grid_collapse_column",
|
|
57
57
|
QUERY_HISTORY = "query_history",
|
|
58
|
-
LOGIN_NONCE = "login_nonce"
|
|
58
|
+
LOGIN_NONCE = "login_nonce",
|
|
59
|
+
DISPLAY_TYPE = "display_type"
|
|
59
60
|
}
|
|
60
61
|
export declare const MOCK_SEARCH_QUERY_STORE = "howler.ui.mock_search_query_store";
|
|
61
62
|
export declare const MOCK_FAVOURITES_STORE = "howler.ui.mock_favourite_store";
|
package/utils/constants.js
CHANGED
|
@@ -61,6 +61,7 @@ export var StorageKey;
|
|
|
61
61
|
StorageKey["GRID_COLLAPSE_COLUMN"] = "grid_collapse_column";
|
|
62
62
|
StorageKey["QUERY_HISTORY"] = "query_history";
|
|
63
63
|
StorageKey["LOGIN_NONCE"] = "login_nonce";
|
|
64
|
+
StorageKey["DISPLAY_TYPE"] = "display_type";
|
|
64
65
|
})(StorageKey || (StorageKey = {}));
|
|
65
66
|
export const MOCK_SEARCH_QUERY_STORE = `${MY_LOCAL_STORAGE_PREFIX}.${StorageKey.MOCK_SEARCH_QUERY_STORE}`;
|
|
66
67
|
export const MOCK_FAVOURITES_STORE = `${MY_LOCAL_STORAGE_PREFIX}.${StorageKey.MOCK_FAVOURITES_STORE}`;
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/* eslint-disable react/no-array-index-key */
|
|
3
|
-
import { Stack, TableCell, Tooltip } from '@mui/material';
|
|
4
|
-
import PluginTypography from '@cccsaurora/howler-ui/components/elements/PluginTypography';
|
|
5
|
-
import { memo } from 'react';
|
|
6
|
-
import { useTranslation } from 'react-i18next';
|
|
7
|
-
const EnhancedCell = ({ value: rawValue, sx = {}, className }) => {
|
|
8
|
-
const { t } = useTranslation();
|
|
9
|
-
if (!rawValue) {
|
|
10
|
-
return _jsx(TableCell, { style: { borderBottom: 'none' }, children: t('none') });
|
|
11
|
-
}
|
|
12
|
-
const values = (Array.isArray(rawValue) ? rawValue : [rawValue]).filter(_value => !!_value);
|
|
13
|
-
return (_jsx(Tooltip, { title: _jsx(Stack, { spacing: 0.5, children: values.map((value, index) => (_jsx("span", { children: value }, value + index))) }), children: _jsx(TableCell, { sx: { borderBottom: 'none', borderRight: 'thin solid', borderRightColor: 'divider', fontSize: '0.8rem' }, children: _jsx(Stack, { direction: "row", className: className, spacing: 0.5, sx: [
|
|
14
|
-
{ display: 'flex', justifyContent: 'start', width: '100%', overflow: 'hidden' },
|
|
15
|
-
...(Array.isArray(sx) ? sx : [sx])
|
|
16
|
-
], children: values.map((value, index) => (_jsx(PluginTypography, { context: "table", sx: { fontSize: 'inherit', textOverflow: 'ellipsis' }, value: value, children: value }, value + index))) }) }) }));
|
|
17
|
-
};
|
|
18
|
-
export default memo(EnhancedCell);
|
|
File without changes
|