@cccsaurora/howler-ui 2.13.0-dev.144 → 2.13.0-dev.148

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.
@@ -59,7 +59,6 @@ import { createBrowserRouter, Outlet, RouterProvider, useLocation, useNavigate }
59
59
  import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
60
60
  import useMySearch from '../hooks/useMySearch';
61
61
  import AppContainer from './AppContainer';
62
- import AnalyticProvider from './providers/AnalyticProvider';
63
62
  import ApiConfigProvider, { ApiConfigContext } from './providers/ApiConfigProvider';
64
63
  import AvatarProvider from './providers/AvatarProvider';
65
64
  import CustomPluginProvider from './providers/CustomPluginProvider';
@@ -138,7 +137,7 @@ const MyAppProvider = ({ children }) => {
138
137
  const mySitemap = useMySitemap();
139
138
  const myUser = useMyUser();
140
139
  const mySearch = useMySearch();
141
- return (_jsx(ErrorBoundary, { children: _jsx(AppProvider, { preferences: myPreferences, theme: myTheme, sitemap: mySitemap, user: myUser, search: mySearch, children: _jsx(CustomPluginProvider, { children: _jsx(ErrorBoundary, { children: _jsx(ErrorBoundary, { children: _jsx(ViewProvider, { children: _jsx(AvatarProvider, { children: _jsx(ModalProvider, { children: _jsx(FieldProvider, { children: _jsx(LocalStorageProvider, { children: _jsx(SocketProvider, { children: _jsx(HitProvider, { children: _jsx(OverviewProvider, { children: _jsx(AnalyticProvider, { children: _jsx(FavouriteProvider, { children: _jsx(UserListProvider, { children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
140
+ return (_jsx(ErrorBoundary, { children: _jsx(AppProvider, { preferences: myPreferences, theme: myTheme, sitemap: mySitemap, user: myUser, search: mySearch, children: _jsx(CustomPluginProvider, { children: _jsx(ErrorBoundary, { children: _jsx(ErrorBoundary, { children: _jsx(ViewProvider, { children: _jsx(AvatarProvider, { children: _jsx(ModalProvider, { children: _jsx(FieldProvider, { children: _jsx(LocalStorageProvider, { children: _jsx(SocketProvider, { children: _jsx(HitProvider, { children: _jsx(OverviewProvider, { children: _jsx(FavouriteProvider, { children: _jsx(UserListProvider, { children: children }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
142
141
  };
143
142
  const AppProviderWrapper = () => {
144
143
  return (_jsx(I18nextProvider, { i18n: i18n, defaultNS: "translation", children: _jsx(ApiConfigProvider, { children: _jsx(PluginProvider, { pluginStore: howlerPluginStore.pluginStore, children: _jsxs(MyAppProvider, { children: [_jsx(MyApp, {}), _jsx(Modal, {})] }) }) }) }));
@@ -4,5 +4,6 @@ declare const useMatchers: () => {
4
4
  getMatchingDossiers: (hit: WithMetadata<Hit>) => Promise<import("../../../models/entities/generated/Dossier").Dossier[]>;
5
5
  getMatchingOverview: (hit: WithMetadata<Hit>) => Promise<import("../../../models/entities/generated/Overview").Overview>;
6
6
  getMatchingTemplate: (hit: WithMetadata<Hit>) => Promise<import("../../../models/entities/generated/Template").Template>;
7
+ getMatchingAnalytic: (hit: WithMetadata<Hit>) => Promise<import("../../../models/entities/generated/Analytic").Analytic>;
7
8
  };
8
9
  export default useMatchers;
@@ -37,10 +37,21 @@ const useMatchers = () => {
37
37
  // should also exist
38
38
  return (await getHit(hit.howler.id, true)).__dossiers;
39
39
  }, [getHit]);
40
+ const getMatchingAnalytic = useCallback(async (hit) => {
41
+ if (!hit) {
42
+ return null;
43
+ }
44
+ if (has(hit, '__analytic')) {
45
+ return hit.__analytic;
46
+ }
47
+ // This is a fallback in case metadata is not included.
48
+ return (await getHit(hit.howler.id, true)).__analytic;
49
+ }, [getHit]);
40
50
  return {
41
51
  getMatchingDossiers,
42
52
  getMatchingOverview,
43
- getMatchingTemplate
53
+ getMatchingTemplate,
54
+ getMatchingAnalytic
44
55
  };
45
56
  };
46
57
  export default useMatchers;
@@ -3,10 +3,6 @@ import { type FC, type PropsWithChildren } from 'react';
3
3
  interface AnalyticContextType {
4
4
  ready: boolean;
5
5
  analytics: Analytic[];
6
- addFavourite: (analytic: Analytic) => Promise<void>;
7
- removeFavourite: (analytic: Analytic) => Promise<void>;
8
- getIdFromName: (name: string) => Promise<string>;
9
- getAnalyticFromName: (name: string) => Promise<Analytic>;
10
6
  getAnalyticFromId: (id: string) => Promise<Analytic>;
11
7
  }
12
8
  export declare const AnalyticContext: import("react").Context<AnalyticContextType>;
@@ -2,7 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import api from '@cccsaurora/howler-ui/api';
3
3
  import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
4
4
  import { createContext, useCallback, useEffect, useState } from 'react';
5
- import { sanitizeLuceneQuery } from '@cccsaurora/howler-ui/utils/stringUtils';
6
5
  export const AnalyticContext = createContext(null);
7
6
  /**
8
7
  * A set of promises for each analytic search. This is to stop several identical
@@ -32,20 +31,6 @@ const AnalyticProvider = ({ children }) => {
32
31
  fetchAnalytics();
33
32
  }
34
33
  }, [ready, appUser, fetchAnalytics]);
35
- const addFavourite = useCallback(async (analytic) => {
36
- await api.analytic.favourite.post(analytic.analytic_id);
37
- appUser.setUser({
38
- ...appUser.user,
39
- favourite_analytics: [...appUser.user.favourite_analytics, analytic.analytic_id]
40
- });
41
- }, [appUser]);
42
- const removeFavourite = useCallback(async (analytic) => {
43
- await api.analytic.favourite.del(analytic.analytic_id);
44
- appUser.setUser({
45
- ...appUser.user,
46
- favourite_analytics: appUser.user.favourite_analytics.filter(v => v !== analytic.analytic_id)
47
- });
48
- }, [appUser]);
49
34
  const getAnalyticFromId = useCallback(async (id) => {
50
35
  const candidate = analytics?.find(_analytic => _analytic.analytic_id === id);
51
36
  if (candidate) {
@@ -71,34 +56,6 @@ const AnalyticProvider = ({ children }) => {
71
56
  }
72
57
  return null;
73
58
  }, [analytics]);
74
- const getAnalyticFromName = useCallback(async (name) => {
75
- const candidate = analytics.find(_analytic => _analytic.name === name);
76
- if (candidate) {
77
- return candidate;
78
- }
79
- // We check to see if there's already a request in progress
80
- if (!PROMISES[name]) {
81
- PROMISES[name] = api.search.analytic.post({
82
- query: `name:(${sanitizeLuceneQuery(name)})`
83
- });
84
- }
85
- try {
86
- const result = await PROMISES[name];
87
- const analytic = result.items?.[0];
88
- if (analytic) {
89
- setAnalytics([...analytics, analytic]);
90
- return analytic;
91
- }
92
- }
93
- catch (e) {
94
- // eslint-disable-next-line no-console
95
- console.error(e);
96
- }
97
- return null;
98
- }, [analytics]);
99
- const getIdFromName = useCallback(async (name) => {
100
- return (await getAnalyticFromName(name))?.analytic_id;
101
- }, [getAnalyticFromName]);
102
- return (_jsx(AnalyticContext.Provider, { value: { analytics, ready, addFavourite, removeFavourite, getAnalyticFromName, getAnalyticFromId, getIdFromName }, children: children }));
59
+ return (_jsx(AnalyticContext.Provider, { value: { analytics, ready, getAnalyticFromId }, children: children }));
103
60
  };
104
61
  export default AnalyticProvider;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { MoreHoriz } from '@mui/icons-material';
3
3
  import { Box, CircularProgress, Divider, FormControl, FormControlLabel, FormLabel, IconButton, Menu, Radio, RadioGroup, Stack, Switch, useMediaQuery } from '@mui/material';
4
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
4
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
5
5
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
6
6
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
7
7
  import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
@@ -27,7 +27,7 @@ const HitActions = ({ hit, orientation = 'horizontal' }) => {
27
27
  const { config } = useContext(ApiConfigContext);
28
28
  const { values, set } = useMyLocalStorageProvider();
29
29
  const pluginStore = usePluginStore();
30
- const { getAnalyticFromName } = useContext(AnalyticContext);
30
+ const { getMatchingAnalytic } = useMatchers();
31
31
  const getCurrentView = useContextSelector(ViewContext, ctx => ctx.getCurrentView);
32
32
  const selected = useContextSelector(ParameterContext, ctx => ctx?.selected);
33
33
  const setSelected = useContextSelector(ParameterContext, ctx => ctx?.setSelected);
@@ -112,11 +112,9 @@ const HitActions = ({ hit, orientation = 'horizontal' }) => {
112
112
  }
113
113
  }, [keyboardDownHandler]);
114
114
  useEffect(() => {
115
- (async () => {
116
- setAnalytic(await getAnalyticFromName(hit.howler.analytic));
117
- })();
115
+ getMatchingAnalytic(hit).then(setAnalytic);
118
116
  // eslint-disable-next-line react-hooks/exhaustive-deps
119
- }, [hit.howler.analytic]);
117
+ }, [hit?.howler.analytic]);
120
118
  const handleOpenSetting = useCallback((e) => setOpenSetting(e.currentTarget), []);
121
119
  const handleCloseSetting = useCallback(() => setOpenSetting(null), []);
122
120
  const onShortcutChange = useCallback((__, s) => set(StorageKey.HIT_SHORTCUTS, s), [set]);
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Chip, Divider, Grid, Stack, Tooltip, Typography, avatarClasses, iconButtonClasses, useTheme } from '@mui/material';
3
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
3
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
4
4
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
5
5
  import { uniq } from 'lodash-es';
6
6
  import howlerPluginStore from '@cccsaurora/howler-ui/plugins/store';
@@ -19,15 +19,19 @@ import { HitLayout } from './HitLayout';
19
19
  const HitBanner = ({ hit, layout = HitLayout.NORMAL, showAssigned = true }) => {
20
20
  const { t } = useTranslation();
21
21
  const { config } = useContext(ApiConfigContext);
22
- const { getIdFromName } = useContext(AnalyticContext);
23
22
  const theme = useTheme();
24
23
  const pluginStore = usePluginStore();
24
+ const { getMatchingAnalytic } = useMatchers();
25
25
  const [analyticId, setAnalyticId] = useState();
26
26
  const compressed = useMemo(() => layout === HitLayout.DENSE, [layout]);
27
27
  const textVariant = useMemo(() => (layout === HitLayout.COMFY ? 'body1' : 'caption'), [layout]);
28
28
  useEffect(() => {
29
- getIdFromName(hit?.howler.analytic).then(setAnalyticId);
30
- }, [getIdFromName, hit]);
29
+ if (!hit?.howler.analytic) {
30
+ return;
31
+ }
32
+ getMatchingAnalytic(hit).then(analytic => setAnalyticId(analytic.analytic_id));
33
+ // eslint-disable-next-line react-hooks/exhaustive-deps
34
+ }, [hit?.howler.analytic]);
31
35
  const providerColor = useMemo(() => PROVIDER_COLORS[hit.event?.provider ?? 'unknown'] ?? stringToColor(hit.event.provider), [hit.event?.provider]);
32
36
  const mitreId = useMemo(() => {
33
37
  if (hit.threat?.framework?.toLowerCase().startsWith('mitre')) {
@@ -3,7 +3,7 @@ import { Clear, KeyboardArrowDown, Send } from '@mui/icons-material';
3
3
  import { Accordion, AccordionDetails, AccordionSummary, AvatarGroup, Chip, IconButton, Skeleton, Stack, TextField, 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
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
7
  import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
8
8
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
9
9
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
@@ -20,8 +20,8 @@ const HitComments = ({ hit, users }) => {
20
20
  const { t } = useTranslation();
21
21
  const navigate = useNavigate();
22
22
  const { dispatchApi } = useMyApi();
23
- const { getAnalyticFromName } = useContext(AnalyticContext);
24
23
  const { addListener, removeListener, emit } = useContext(SocketContext);
24
+ const { getMatchingAnalytic } = useMatchers();
25
25
  const [typers, setTypers] = useState([]);
26
26
  const [loading, setLoading] = useState(false);
27
27
  const [showClear, setShowClear] = useState(false);
@@ -51,12 +51,13 @@ const HitComments = ({ hit, users }) => {
51
51
  }, [handler]);
52
52
  useEffect(() => {
53
53
  if (hit?.howler?.analytic) {
54
- getAnalyticFromName(hit?.howler?.analytic).then(analytic => {
54
+ getMatchingAnalytic(hit).then(analytic => {
55
55
  setAnalyticId(analytic?.analytic_id);
56
56
  setAnalyticComments(sortByTimestamp(analytic?.comment ?? []));
57
57
  });
58
58
  }
59
- }, [getAnalyticFromName, hit?.howler?.analytic]);
59
+ // eslint-disable-next-line react-hooks/exhaustive-deps
60
+ }, [getMatchingAnalytic, hit?.howler?.analytic]);
60
61
  const onSubmit = useCallback(async () => {
61
62
  if (!input.current?.value || !hit || input.current.value.length > MAX_LENGTH)
62
63
  return;
@@ -4,7 +4,6 @@ import { AvatarGroup, Card, CardContent, CardHeader, Chip, Divider, Grid, IconBu
4
4
  import api from '@cccsaurora/howler-ui/api';
5
5
  import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks';
6
6
  import useLocalStorageItem from '@cccsaurora/howler-ui/commons/components/utils/hooks/useLocalStorageItem';
7
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
8
7
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
9
8
  import { TuiListProvider } from '@cccsaurora/howler-ui/components/elements/addons/lists';
10
9
  import { TuiListMethodContext } from '@cccsaurora/howler-ui/components/elements/addons/lists/TuiListProvider';
@@ -26,13 +25,26 @@ const AnalyticSearchBase = () => {
26
25
  const [searchParams, setSearchParams] = useSearchParams();
27
26
  const pageCount = useMyLocalStorageItem(StorageKey.PAGE_COUNT, 25)[0];
28
27
  const appUser = useAppUser();
29
- const { addFavourite, removeFavourite } = useContext(AnalyticContext);
30
28
  const [onlyRules, setOnlyRules] = useLocalStorageItem(StorageKey.ONLY_RULES, 0);
31
29
  const [searching, setSearching] = useState(false);
32
30
  const [hasError, setHasError] = useState(false);
33
31
  const [phrase, setPhrase] = useState(searchParams.get('phrase') || '');
34
32
  const [offset, setOffset] = useState(parseInt(searchParams.get('offset')) || 0);
35
33
  const [response, setResponse] = useState(null);
34
+ const addFavourite = useCallback(async (analytic) => {
35
+ await dispatchApi(api.analytic.favourite.post(analytic.analytic_id));
36
+ appUser.setUser({
37
+ ...appUser.user,
38
+ favourite_analytics: [...appUser.user.favourite_analytics, analytic.analytic_id]
39
+ });
40
+ }, [appUser, dispatchApi]);
41
+ const removeFavourite = useCallback(async (analytic) => {
42
+ await dispatchApi(api.analytic.favourite.del(analytic.analytic_id));
43
+ appUser.setUser({
44
+ ...appUser.user,
45
+ favourite_analytics: appUser.user.favourite_analytics.filter(v => v !== analytic.analytic_id)
46
+ });
47
+ }, [appUser, dispatchApi]);
36
48
  // Search Handler.
37
49
  const onSearch = useCallback(async () => {
38
50
  setSearching(true);
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Assignment, Edit, HowToVote, KeyboardArrowRight, OpenInNew, QueryStats, SettingsSuggest, Terminal } from '@mui/icons-material';
3
3
  import { Box, Divider, Fade, ListItemIcon, ListItemText, Menu, MenuItem, MenuList, Paper } from '@mui/material';
4
4
  import api from '@cccsaurora/howler-ui/api';
5
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
5
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
6
6
  import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
7
7
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
8
  import { TOP_ROW, VOTE_OPTIONS } from '@cccsaurora/howler-ui/components/elements/hit/actions/SharedComponents';
@@ -24,11 +24,11 @@ const ICON_MAP = {
24
24
  };
25
25
  const HitContextMenu = ({ children, getSelectedId, Component = Box }) => {
26
26
  const { t } = useTranslation();
27
- const analyticContext = useContext(AnalyticContext);
28
27
  const { dispatchApi } = useMyApi();
29
28
  const { executeAction } = useMyActionFunctions();
30
29
  const { config } = useContext(ApiConfigContext);
31
30
  const pluginStore = usePluginStore();
31
+ const { getMatchingAnalytic } = useMatchers();
32
32
  const [id, setId] = useState(null);
33
33
  const hit = useContextSelector(HitContext, ctx => ctx.hits[id]);
34
34
  const selectedHits = useContextSelector(HitContext, ctx => ctx.selectedHits);
@@ -90,13 +90,12 @@ const HitContextMenu = ({ children, getSelectedId, Component = Box }) => {
90
90
  return Object.entries(groupBy(_actions, 'type')).sort(([a], [b]) => ORDER.indexOf(a) - ORDER.indexOf(b));
91
91
  }, [analytic, assess, availableTransitions, canAssess, canVote, config.lookups, vote, pluginActions]);
92
92
  useEffect(() => {
93
- if (!hit) {
93
+ if (!hit?.howler.analytic) {
94
94
  return;
95
95
  }
96
- (async () => {
97
- setAnalytic(await analyticContext.getAnalyticFromName(hit.howler.analytic));
98
- })();
99
- }, [analyticContext, hit]);
96
+ getMatchingAnalytic(hit).then(setAnalytic);
97
+ // eslint-disable-next-line react-hooks/exhaustive-deps
98
+ }, [hit?.howler.analytic]);
100
99
  useEffect(() => {
101
100
  if (!anchorEl) {
102
101
  setClickLocation([-1, -1]);
@@ -4,7 +4,6 @@ import { Badge, Box, Divider, Skeleton, Stack, Tab, Tabs, Tooltip, useTheme } fr
4
4
  import TuiIconButton from '@cccsaurora/howler-ui/components/elements/addons/buttons/CustomIconButton';
5
5
  import { Icon } from '@iconify/react/dist/iconify.js';
6
6
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
8
7
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
9
8
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
10
9
  import { SocketContext } from '@cccsaurora/howler-ui/components/app/providers/SocketProvider';
@@ -46,8 +45,7 @@ const InformationPane = ({ onClose }) => {
46
45
  const theme = useTheme();
47
46
  const location = useLocation();
48
47
  const { emit, isOpen } = useContext(SocketContext);
49
- const { getAnalyticFromName } = useContext(AnalyticContext);
50
- const { getMatchingOverview, getMatchingDossiers } = useMatchers();
48
+ const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
51
49
  const selected = useContextSelector(ParameterContext, ctx => ctx.selected);
52
50
  const pluginStore = usePluginStore();
53
51
  const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
@@ -75,10 +73,10 @@ const InformationPane = ({ onClose }) => {
75
73
  return;
76
74
  }
77
75
  setUserIds(getUserList(hit));
78
- getAnalyticFromName(hit.howler.analytic).then(setAnalytic);
76
+ getMatchingAnalytic(hit).then(setAnalytic);
79
77
  getMatchingDossiers(hit).then(setDossiers);
80
78
  // eslint-disable-next-line react-hooks/exhaustive-deps
81
- }, [getAnalyticFromName, getHit, selected]);
79
+ }, [getHit, selected]);
82
80
  useEffect(() => {
83
81
  if (tab === 'hit_aggregate' && !hit?.howler.is_bundle) {
84
82
  setTab('overview');
@@ -3,7 +3,7 @@ import { DndContext, KeyboardSensor, PointerSensor, pointerWithin, useSensor, us
3
3
  import { arrayMove, SortableContext, sortableKeyboardCoordinates } from '@dnd-kit/sortable';
4
4
  import { Add, FormatIndentDecrease, FormatIndentIncrease, Info, List, Search, TableChart } from '@mui/icons-material';
5
5
  import { IconButton, LinearProgress, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow, ToggleButton, ToggleButtonGroup, Typography, useTheme } from '@mui/material';
6
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
6
+ import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
7
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
8
8
  import { HitSearchContext } from '@cccsaurora/howler-ui/components/app/providers/HitSearchProvider';
9
9
  import { ParameterContext } from '@cccsaurora/howler-ui/components/app/providers/ParameterProvider';
@@ -14,7 +14,7 @@ import DevelopmentBanner from '@cccsaurora/howler-ui/components/elements/display
14
14
  import DevelopmentIcon from '@cccsaurora/howler-ui/components/elements/display/features/DevelopmentIcon';
15
15
  import useHitSelection from '@cccsaurora/howler-ui/components/hooks/useHitSelection';
16
16
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
17
- import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
17
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
18
18
  import { useTranslation } from 'react-i18next';
19
19
  import { useContextSelector } from 'use-context-selector';
20
20
  import { StorageKey } from '@cccsaurora/howler-ui/utils/constants';
@@ -27,10 +27,10 @@ import ColumnHeader from './ColumnHeader';
27
27
  import HitRow from './HitRow';
28
28
  const HitGrid = () => {
29
29
  const { t } = useTranslation();
30
- const { getIdFromName } = useContext(AnalyticContext);
31
30
  const theme = useTheme();
32
31
  const sensors = useSensors(useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }));
33
32
  const { onClick } = useHitSelection();
33
+ const { getMatchingAnalytic } = useMatchers();
34
34
  const search = useContextSelector(HitSearchContext, ctx => ctx.search);
35
35
  const displayType = useContextSelector(HitSearchContext, ctx => ctx.displayType);
36
36
  const setDisplayType = useContextSelector(HitSearchContext, ctx => ctx.setDisplayType);
@@ -65,10 +65,11 @@ const HitGrid = () => {
65
65
  useEffect(() => {
66
66
  response?.items.forEach(hit => {
67
67
  if (!analyticIds[hit.howler.analytic]) {
68
- getIdFromName(hit.howler.analytic).then(_analyticId => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [hit.howler.analytic]: _analyticId })));
68
+ getMatchingAnalytic(hit).then(_analytic => setAnalyticIds(_analyticIds => ({ ..._analyticIds, [hit.howler.analytic]: _analytic.analytic_id })));
69
69
  }
70
70
  });
71
- }, [analyticIds, getIdFromName, response]);
71
+ // eslint-disable-next-line react-hooks/exhaustive-deps
72
+ }, [analyticIds, response]);
72
73
  const onMouseMove = useCallback((event) => {
73
74
  event.stopPropagation();
74
75
  event.preventDefault();
@@ -4,7 +4,6 @@ import { Code, Comment, DataObject, History, LinkSharp, QueryStats, ViewAgenda }
4
4
  import { Badge, Box, CardContent, Collapse, IconButton, Skeleton, Stack, Tab, Tabs, Tooltip, useMediaQuery, useTheme } from '@mui/material';
5
5
  import PageCenter from '@cccsaurora/howler-ui/commons/components/pages/PageCenter';
6
6
  import useMatchers from '@cccsaurora/howler-ui/components/app/hooks/useMatchers';
7
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
8
7
  import { HitContext } from '@cccsaurora/howler-ui/components/app/providers/HitProvider';
9
8
  import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
10
9
  import HowlerCard from '@cccsaurora/howler-ui/components/elements/display/HowlerCard';
@@ -27,7 +26,7 @@ import RelatedLink from '@cccsaurora/howler-ui/components/elements/hit/related/R
27
26
  import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
28
27
  import useMyUserList from '@cccsaurora/howler-ui/components/hooks/useMyUserList';
29
28
  import uniqBy from 'lodash-es/uniqBy';
30
- import { useCallback, useContext, useEffect, useMemo, useState } from 'react';
29
+ import { useCallback, useEffect, useMemo, useState } from 'react';
31
30
  import { useTranslation } from 'react-i18next';
32
31
  import { useNavigate, useParams } from 'react-router-dom';
33
32
  import { useContextSelector } from 'use-context-selector';
@@ -47,8 +46,7 @@ const HitViewer = () => {
47
46
  const theme = useTheme();
48
47
  const isUnderLg = useMediaQuery(theme.breakpoints.down('lg'));
49
48
  const [orientation, setOrientation] = useMyLocalStorageItem(StorageKey.VIEWER_ORIENTATION, Orientation.VERTICAL);
50
- const { getAnalyticFromName } = useContext(AnalyticContext);
51
- const { getMatchingOverview, getMatchingDossiers } = useMatchers();
49
+ const { getMatchingOverview, getMatchingDossiers, getMatchingAnalytic } = useMatchers();
52
50
  const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
53
51
  const hit = useContextSelector(HitContext, ctx => ctx.hits[params.id]);
54
52
  const [userIds, setUserIds] = useState(new Set());
@@ -64,14 +62,14 @@ const HitViewer = () => {
64
62
  return;
65
63
  }
66
64
  setUserIds(getUserList(hit));
67
- setAnalytic(await getAnalyticFromName(hit.howler.analytic));
65
+ setAnalytic(await getMatchingAnalytic(hit));
68
66
  }
69
67
  catch (err) {
70
68
  if (err.cause?.api_status_code === 404) {
71
69
  navigate('/404');
72
70
  }
73
71
  }
74
- }, [hit, getAnalyticFromName, getHit, params.id, navigate]);
72
+ }, [hit, getMatchingAnalytic, getHit, params.id, navigate]);
75
73
  useEffect(() => {
76
74
  if (isUnderLg) {
77
75
  setOrientation(Orientation.HORIZONTAL);
@@ -8,7 +8,6 @@ import { Check, DarkMode, Delete, SsidChart, WbSunny } from '@mui/icons-material
8
8
  import { useApp } from '@cccsaurora/howler-ui/commons/components/app/hooks';
9
9
  import AppInfoPanel from '@cccsaurora/howler-ui/commons/components/display/AppInfoPanel';
10
10
  import useThemeBuilder from '@cccsaurora/howler-ui/commons/components/utils/hooks/useThemeBuilder';
11
- import { AnalyticContext } from '@cccsaurora/howler-ui/components/app/providers/AnalyticProvider';
12
11
  import { OverviewContext } from '@cccsaurora/howler-ui/components/app/providers/OverviewProvider';
13
12
  import HitOverview from '@cccsaurora/howler-ui/components/elements/hit/HitOverview';
14
13
  import useMyApi from '@cccsaurora/howler-ui/components/hooks/useMyApi';
@@ -38,7 +37,6 @@ const OverviewViewer = () => {
38
37
  const [overviewLoading, setOverviewLoading] = useState(false);
39
38
  const [exampleHit, setExampleHit] = useState(null);
40
39
  const [x, setX] = useState(0);
41
- const analyticContext = useContext(AnalyticContext);
42
40
  const wrapper = useRef();
43
41
  const startingTemplate = useStartingTemplate();
44
42
  useEffect(() => {
@@ -64,19 +62,14 @@ const OverviewViewer = () => {
64
62
  // eslint-disable-next-line react-hooks/exhaustive-deps
65
63
  }, [analytic, dispatchApi]);
66
64
  useEffect(() => {
67
- if (analytic) {
68
- setLoading(true);
69
- analyticContext
70
- .getAnalyticFromName(analytic)
71
- .then(foundAnalytic => {
72
- setDetections(foundAnalytic.detections);
73
- })
74
- .catch(() => {
65
+ if (analytic && analytics) {
66
+ const _detections = analytics.find(_analytic => _analytic.name.toLowerCase() === analytic.toLowerCase())?.detections ?? [];
67
+ setDetections(_detections);
68
+ if (detection && !_detections.map(_detection => _detection.toLowerCase()).includes(detection.toLowerCase())) {
75
69
  setDetection('ANY');
76
- })
77
- .finally(() => setLoading(false));
70
+ }
78
71
  }
79
- }, [analytic, analyticContext, detection, dispatchApi, params, setParams]);
72
+ }, [analytic, analytics, detection]);
80
73
  useEffect(() => {
81
74
  (async () => {
82
75
  const result = await dispatchApi(api.search.hit.post({
@@ -1,8 +1,10 @@
1
+ import type { Analytic } from './entities/generated/Analytic';
1
2
  import type { Dossier } from './entities/generated/Dossier';
2
3
  import type { Overview } from './entities/generated/Overview';
3
4
  import type { Template } from './entities/generated/Template';
4
5
  export type WithMetadata<T> = T & {
5
- __template?: Template;
6
+ __analytic?: Analytic;
6
7
  __overview?: Overview;
8
+ __template?: Template;
7
9
  __dossiers?: Dossier[];
8
10
  };
package/package.json CHANGED
@@ -96,7 +96,7 @@
96
96
  "internal-slot": "1.0.7"
97
97
  },
98
98
  "type": "module",
99
- "version": "2.13.0-dev.144",
99
+ "version": "2.13.0-dev.148",
100
100
  "exports": {
101
101
  "./i18n": "./i18n.js",
102
102
  "./index.css": "./index.css",