@cccsaurora/howler-ui 2.17.0-dev.420 → 2.17.0-dev.470
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/commons/components/app/hooks/useAppConfigs.d.ts +1 -1
- package/components/app/providers/HitSearchProvider.d.ts +0 -1
- package/components/app/providers/HitSearchProvider.js +4 -6
- package/components/app/providers/HitSearchProvider.test.js +1 -1
- package/components/app/providers/ParameterProvider.js +3 -3
- package/components/app/providers/ViewProvider.d.ts +1 -1
- package/components/app/providers/ViewProvider.js +3 -6
- package/components/app/providers/ViewProvider.test.js +1 -1
- package/components/elements/PluginChip.d.ts +2 -0
- package/components/elements/PluginChip.js +2 -1
- package/components/elements/PluginTypography.d.ts +4 -3
- package/components/elements/PluginTypography.js +4 -3
- package/components/elements/display/modals/RationaleModal.js +1 -1
- package/components/elements/display/modals/RationaleModal.test.js +1 -1
- package/components/elements/hit/HitBanner.js +2 -2
- package/components/elements/hit/HitDetails.js +9 -9
- package/components/elements/hit/outlines/DefaultOutline.js +1 -1
- package/components/routes/hits/search/ViewLink.js +1 -1
- package/components/routes/hits/search/ViewLink.test.js +3 -3
- package/components/routes/hits/search/grid/EnhancedCell.d.ts +2 -0
- package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
- package/components/routes/hits/search/grid/HitRow.js +1 -1
- package/components/routes/views/ViewComposer.js +2 -2
- package/index.js +5 -0
- package/locales/en/translation.json +1 -0
- package/locales/fr/translation.json +1 -0
- package/models/entities/generated/Analytic.d.ts +2 -2
- package/models/entities/generated/ApiType.d.ts +2 -1
- package/models/entities/generated/Clue.d.ts +8 -0
- package/models/entities/generated/Hit.d.ts +2 -20
- package/models/entities/generated/Labels.d.ts +1 -0
- package/models/entities/generated/Type.d.ts +7 -0
- package/package.json +24 -15
- package/plugins/HowlerPlugin.js +1 -0
- package/plugins/clue/Provider.d.ts +3 -0
- package/plugins/clue/Provider.js +13 -0
- package/plugins/clue/components/ClueChip.d.ts +3 -0
- package/plugins/clue/components/ClueChip.js +29 -0
- package/plugins/clue/components/ClueLeadForm.d.ts +4 -0
- package/plugins/clue/components/ClueLeadForm.js +24 -0
- package/plugins/clue/components/CluePivot.d.ts +3 -0
- package/plugins/clue/components/CluePivot.js +145 -0
- package/plugins/clue/components/CluePivotForm.d.ts +21 -0
- package/plugins/clue/components/CluePivotForm.js +270 -0
- package/plugins/clue/components/ClueTypography.d.ts +3 -0
- package/plugins/clue/components/ClueTypography.js +53 -0
- package/plugins/clue/helpers.d.ts +3 -0
- package/plugins/clue/helpers.js +196 -0
- package/plugins/clue/index.d.ts +21 -0
- package/plugins/clue/index.js +66 -0
- package/plugins/clue/locales/clue.en.json +8 -0
- package/plugins/clue/locales/clue.fr.json +8 -0
- package/plugins/clue/setup.d.ts +2 -0
- package/plugins/clue/setup.js +46 -0
- package/plugins/clue/utils.d.ts +2 -0
- package/plugins/clue/utils.js +19 -0
- package/plugins/store.js +3 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import EnrichedTypography from '@cccsaurora/clue-ui/components/EnrichedTypography';
|
|
3
|
+
import { Typography } from '@mui/material';
|
|
4
|
+
import { memo } from 'react';
|
|
5
|
+
import { useType } from '../utils';
|
|
6
|
+
const ClueTypography = ({ children, value, context, field, hit, ...props }) => {
|
|
7
|
+
const type = useType(hit, field, value);
|
|
8
|
+
if (!type) {
|
|
9
|
+
return _jsx(Typography, { ...props, children: children ?? value });
|
|
10
|
+
}
|
|
11
|
+
let enrichedProps = {
|
|
12
|
+
...props,
|
|
13
|
+
value
|
|
14
|
+
};
|
|
15
|
+
if (context === 'banner') {
|
|
16
|
+
enrichedProps = {
|
|
17
|
+
...enrichedProps,
|
|
18
|
+
slotProps: { stack: { component: 'span' } }
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
else if (context === 'outline') {
|
|
22
|
+
enrichedProps = {
|
|
23
|
+
...enrichedProps,
|
|
24
|
+
hideLoading: true,
|
|
25
|
+
slotProps: {
|
|
26
|
+
stack: {
|
|
27
|
+
sx: { mr: 'auto' },
|
|
28
|
+
onClick: e => {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
popover: {
|
|
34
|
+
onClick: e => {
|
|
35
|
+
e.preventDefault();
|
|
36
|
+
e.stopPropagation();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
else if (context === 'table') {
|
|
43
|
+
enrichedProps = {
|
|
44
|
+
...enrichedProps,
|
|
45
|
+
hideLoading: true,
|
|
46
|
+
slotProps: {
|
|
47
|
+
stack: { sx: { width: '100%', '& > p': { textOverflow: 'ellipsis', overflow: 'hidden' } } }
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return _jsx(EnrichedTypography, { ...enrichedProps, type: type });
|
|
52
|
+
};
|
|
53
|
+
export default memo(ClueTypography);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable no-console */
|
|
3
|
+
import EnrichedTypography, {} from '@cccsaurora/clue-ui/components/EnrichedTypography';
|
|
4
|
+
import Fetcher from '@cccsaurora/clue-ui/components/fetchers/Fetcher';
|
|
5
|
+
import Entry from '@cccsaurora/clue-ui/components/group/Entry';
|
|
6
|
+
import Group from '@cccsaurora/clue-ui/components/group/Group';
|
|
7
|
+
import { useClueEnrichSelector } from '@cccsaurora/clue-ui/hooks/selectors';
|
|
8
|
+
import { Checkbox, Paper, Stack, Table, TableBody, TableCell, TableHead, TableRow } from '@mui/material';
|
|
9
|
+
import FlexOne from '@cccsaurora/howler-ui/components/elements/addons/layout/FlexOne';
|
|
10
|
+
import i18nInstance from '@cccsaurora/howler-ui/i18n';
|
|
11
|
+
import capitalize from 'lodash-es/capitalize';
|
|
12
|
+
import groupBy from 'lodash-es/groupBy';
|
|
13
|
+
import uniq from 'lodash-es/uniq';
|
|
14
|
+
import { useState } from 'react';
|
|
15
|
+
import { useTranslation } from 'react-i18next';
|
|
16
|
+
const MarkdownTypography = ({ type, value, ...props }) => {
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
try {
|
|
19
|
+
const guessType = useClueEnrichSelector(ctx => ctx.guessType);
|
|
20
|
+
if (!type || type?.toString().toLowerCase() === 'guess') {
|
|
21
|
+
type = guessType(value.toString());
|
|
22
|
+
}
|
|
23
|
+
if (!type) {
|
|
24
|
+
return _jsx("span", { children: value });
|
|
25
|
+
}
|
|
26
|
+
return _jsx(EnrichedTypography, { ...props, type: type, value: value });
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
return (_jsxs(Stack, { children: [_jsx("strong", { style: { color: 'red' }, children: t('markdown.error') }), _jsx("strong", { children: err.toString() }), _jsx("code", { style: { fontSize: '0.8rem' }, children: _jsx("pre", { children: err.stack }) })] }));
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const ClueGroup = props => {
|
|
33
|
+
if (!props.enabled) {
|
|
34
|
+
return _jsx(_Fragment, { children: props.children });
|
|
35
|
+
}
|
|
36
|
+
if (!props.type) {
|
|
37
|
+
console.error('Missing required props for group helper');
|
|
38
|
+
return (_jsxs(Stack, { spacing: 1, children: [_jsx("strong", { style: { color: 'red' }, children: i18nInstance.t('markdown.error') }), _jsxs("code", { style: { fontSize: '0.8rem' }, children: [i18nInstance.t('markdown.props.missing'), ": type"] })] }));
|
|
39
|
+
}
|
|
40
|
+
return _jsx(Group, { type: props.type, children: props.children });
|
|
41
|
+
};
|
|
42
|
+
const ClueEntry = ({ value }) => {
|
|
43
|
+
const [checked, setChecked] = useState(false);
|
|
44
|
+
return (_jsx(Entry, { entry: value, selected: checked, children: _jsx(Paper, { sx: { p: 1 }, children: _jsxs(Stack, { direction: "row", spacing: 1, children: [_jsx(Checkbox, { checked: checked, onChange: (_event, _checked) => setChecked(_checked) }), _jsx(MarkdownTypography, { value: value }), _jsx(FlexOne, {})] }) }) }));
|
|
45
|
+
};
|
|
46
|
+
const ClueCheckbox = ({ value }) => {
|
|
47
|
+
const [checked, setChecked] = useState(false);
|
|
48
|
+
return (_jsx(Entry, { entry: value, selected: checked, children: _jsx(Checkbox, { checked: checked, onChange: (_event, _checked) => setChecked(_checked) }) }));
|
|
49
|
+
};
|
|
50
|
+
const HELPERS = [
|
|
51
|
+
{
|
|
52
|
+
keyword: 'clue',
|
|
53
|
+
documentation: {
|
|
54
|
+
en: 'Given a selector, this helper enriches the selector through clue.',
|
|
55
|
+
fr: 'Étant donné un sélecteur, cet assistant enrichit le sélecteur via clue.'
|
|
56
|
+
},
|
|
57
|
+
componentCallback: (type, value) => {
|
|
58
|
+
if (typeof type !== 'string' || typeof value !== 'string') {
|
|
59
|
+
return (_jsxs(Stack, { spacing: 1, children: [_jsx("strong", { style: { color: 'red' }, children: i18nInstance.t('markdown.error') }), _jsx("code", { style: { fontSize: '0.8rem' }, children: i18nInstance.t('markdown.helpers.clue.arguments') })] }));
|
|
60
|
+
}
|
|
61
|
+
return (_jsx(MarkdownTypography, { slotProps: { stack: { component: 'span', sx: { width: 'fit-content' } } }, component: "span", type: type, value: typeof value === 'string' ? value : null }));
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
keyword: 'fetcher',
|
|
66
|
+
documentation: {
|
|
67
|
+
en: 'Given a selector, this helper fetches data for the selector through clue.',
|
|
68
|
+
fr: 'Étant donné un sélecteur, cet assistant récupère les données pour le sélecteur via clue.'
|
|
69
|
+
},
|
|
70
|
+
componentCallback: (...args) => {
|
|
71
|
+
const options = args.pop();
|
|
72
|
+
const props = options?.hash ?? {};
|
|
73
|
+
if (!props.type || !props.value || !props.fetcherId) {
|
|
74
|
+
console.error('Missing required props for fetcher helper');
|
|
75
|
+
return (_jsxs(Stack, { spacing: 1, children: [_jsx("strong", { style: { color: 'red' }, children: i18nInstance.t('markdown.error') }), _jsxs("code", { style: { fontSize: '0.8rem' }, children: [i18nInstance.t('markdown.props.missing'), ":", ' ', ['type', 'value', 'fetcherId'].filter(key => !props[key]).join(', ')] })] }));
|
|
76
|
+
}
|
|
77
|
+
if (props.fetcherId.includes('eml')) {
|
|
78
|
+
props.fetcherId = props.fetcherId.replace('eml', 'email');
|
|
79
|
+
}
|
|
80
|
+
console.debug(`Rendering fetcher (${props.fetcherId}) for selector ${props.type}:${props.value}`);
|
|
81
|
+
return (_jsx(Fetcher, { slotProps: { stack: { component: 'span', sx: { width: 'fit-content' } } }, component: "span", ...props }));
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
keyword: 'clue_group',
|
|
86
|
+
documentation: {
|
|
87
|
+
en: 'Initializes a clue group',
|
|
88
|
+
fr: 'Initialise un groupe clue'
|
|
89
|
+
},
|
|
90
|
+
componentCallback: (values, ...args) => {
|
|
91
|
+
const options = args.pop();
|
|
92
|
+
const props = options?.hash ?? {};
|
|
93
|
+
const missing = [];
|
|
94
|
+
if (!Array.isArray(values)) {
|
|
95
|
+
missing.push('values');
|
|
96
|
+
}
|
|
97
|
+
if (!props.type) {
|
|
98
|
+
missing.push('type');
|
|
99
|
+
}
|
|
100
|
+
if (missing.length > 0) {
|
|
101
|
+
return (_jsxs(Stack, { spacing: 1, children: [_jsx("strong", { style: { color: 'red' }, children: i18nInstance.t('markdown.error') }), _jsxs("code", { style: { fontSize: '0.8rem' }, children: [i18nInstance.t('markdown.props.missing'), ": ", missing.join(', ')] })] }));
|
|
102
|
+
}
|
|
103
|
+
return (_jsx(ClueGroup, { type: props.type, enabled: true, children: _jsx(Stack, { spacing: 1, mt: 1, children: uniq(values).map(value => (_jsx(ClueEntry, { value: value }, value))) }) }));
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
keyword: 'clue_table',
|
|
108
|
+
documentation: {
|
|
109
|
+
en: `Render a table with optional Clue enrichments, fetchers and actions.
|
|
110
|
+
|
|
111
|
+
Clue enrichments are performed for cells with a clue_type and no clue_fetcher.
|
|
112
|
+
|
|
113
|
+
Clue fetchers are used for cells with a clue_type and a clue_fetcher.
|
|
114
|
+
|
|
115
|
+
Clue actions are enabled by specifying a clue action type using the optional clue_action_type parameter. If enabled, cells with clue_entity==true will be selectable for use with Clue enrichments and actions, with a value of action_value if present, and otherwise value.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
\`\`\`markdown
|
|
119
|
+
{{curly 'clue_table clue_table_cells clue_action_type="ip"'}}
|
|
120
|
+
\`\`\`
|
|
121
|
+
where clue_table_cells is an array with properties:
|
|
122
|
+
|
|
123
|
+
\`\`\`
|
|
124
|
+
column: string;
|
|
125
|
+
row: string;
|
|
126
|
+
value: string;
|
|
127
|
+
clue_type (optional): string;
|
|
128
|
+
clue_fetcher (optional): string;
|
|
129
|
+
fetcher_width (optional): string;
|
|
130
|
+
clue_entity (optional): boolean;
|
|
131
|
+
action_value (optional): string;
|
|
132
|
+
\`\`\`
|
|
133
|
+
`,
|
|
134
|
+
fr: `Affiche un tableau avec des enrichissements, des extractions et des actions Clue optionnelles.
|
|
135
|
+
|
|
136
|
+
Les enrichissements Clue sont effectués pour les cellules avec un clue_type et sans un clue_fetcher.
|
|
137
|
+
|
|
138
|
+
Clue récupère les données pour le sélecteur pour les cellules avec un clue_type et un clue_fetcher.
|
|
139
|
+
|
|
140
|
+
Les actions Clue sont activées en spécifiant un type d'action clue en utilisant le paramètre optionnel clue_action_type. Si activé, les cellules avec clue_entity==true seront sélectionnables pour utilisation avec les enrichissements et actions Clue, avec une valeur de action_value si présente, sinon la valeur.
|
|
141
|
+
|
|
142
|
+
Exemple :
|
|
143
|
+
\`\`\`markdown
|
|
144
|
+
{{curly 'clue_table clue_table_cells clue_action_type="ip"'}}
|
|
145
|
+
\`\`\`
|
|
146
|
+
où clue_table_cells est un tableau avec les propriétés :
|
|
147
|
+
|
|
148
|
+
\`\`\`
|
|
149
|
+
column: string;
|
|
150
|
+
row: string;
|
|
151
|
+
value: string;
|
|
152
|
+
clue_type (optionnel): string;
|
|
153
|
+
clue_fetcher (optionnel): string;
|
|
154
|
+
fetcher_width (optionnel): string;
|
|
155
|
+
clue_entity (optionnel): boolean;
|
|
156
|
+
action_value (optionnel): string;
|
|
157
|
+
\`\`\`
|
|
158
|
+
`
|
|
159
|
+
},
|
|
160
|
+
componentCallback: (cells, ...args) => {
|
|
161
|
+
const options = args.pop();
|
|
162
|
+
const props = options?.hash ?? {};
|
|
163
|
+
const columns = Object.keys(groupBy(cells, 'column'));
|
|
164
|
+
const rows = groupBy(cells, 'row');
|
|
165
|
+
const clueActionType = props.clue_action_type;
|
|
166
|
+
const enableClueActions = !!clueActionType;
|
|
167
|
+
return (_jsx(Paper, { sx: { width: '95%', overflowX: 'auto', m: 1 }, children: _jsx(ClueGroup, { type: clueActionType, enabled: enableClueActions, children: _jsxs(Table, { children: [_jsx(TableHead, { children: _jsx(TableRow, { children: columns.map(col => (_jsx(TableCell, { sx: { maxWidth: '150px' }, children: col
|
|
168
|
+
.split(/[_-]/)
|
|
169
|
+
.map(word => capitalize(word))
|
|
170
|
+
.join(' ') }, col))) }) }), _jsx(TableBody, { sx: { '& td': { wordBreak: 'break-word' } }, children: Object.entries(rows).map(([rowId, _cells]) => {
|
|
171
|
+
return (_jsx(TableRow, { children: columns.map(col => {
|
|
172
|
+
const cell = _cells.find(row => row.column === col);
|
|
173
|
+
if (!cell) {
|
|
174
|
+
return _jsx(TableCell, {}, col);
|
|
175
|
+
}
|
|
176
|
+
return (_jsxs(TableCell, { children: [enableClueActions && cell.clue_entity && (_jsx(ClueCheckbox, { value: cell.action_value ?? cell.value })), !!cell.clue_fetcher && !!cell.clue_type ? (_jsx(Fetcher, { slotProps: {
|
|
177
|
+
image: { width: !!cell.fetcher_width ? cell.fetcher_width : 'fit-content' },
|
|
178
|
+
stack: {
|
|
179
|
+
component: 'span',
|
|
180
|
+
sx: !!cell.fetcher_width
|
|
181
|
+
? { width: cell.fetcher_width, display: 'block' }
|
|
182
|
+
: { width: 'fit-content' }
|
|
183
|
+
}
|
|
184
|
+
}, fetcherId: cell.clue_fetcher, value: cell.value, type: cell.clue_type })) : !!cell.clue_type ? (_jsx(MarkdownTypography, { slotProps: {
|
|
185
|
+
stack: {
|
|
186
|
+
component: 'span',
|
|
187
|
+
sx: { width: 'fit-content' },
|
|
188
|
+
display: 'inline-flex'
|
|
189
|
+
}
|
|
190
|
+
}, component: "span", type: cell.clue_type, value: cell.value })) : ((cell.value ?? 'N/A'))] }, col + cell.value));
|
|
191
|
+
}) }, rowId));
|
|
192
|
+
}) })] }) }) }));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
];
|
|
196
|
+
export default HELPERS;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HowlerHelper } from '@cccsaurora/howler-ui/components/elements/display/handlebars/helpers';
|
|
2
|
+
import type { PluginChipProps } from '@cccsaurora/howler-ui/components/elements/PluginChip';
|
|
3
|
+
import type { PluginTypographyProps } from '@cccsaurora/howler-ui/components/elements/PluginTypography';
|
|
4
|
+
import { type i18n as I18N } from 'i18next';
|
|
5
|
+
import HowlerPlugin from '@cccsaurora/howler-ui/plugins/HowlerPlugin';
|
|
6
|
+
declare class CluePlugin extends HowlerPlugin {
|
|
7
|
+
name: string;
|
|
8
|
+
version: string;
|
|
9
|
+
author: string;
|
|
10
|
+
description: string;
|
|
11
|
+
activate(): void;
|
|
12
|
+
provider(): import("react").FC<{
|
|
13
|
+
children?: import("react").ReactNode | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
setup(): () => void;
|
|
16
|
+
localization(i18nInstance: I18N): void;
|
|
17
|
+
helpers(): HowlerHelper[];
|
|
18
|
+
typography(_props: PluginTypographyProps): import("react/jsx-runtime").JSX.Element;
|
|
19
|
+
chip(_props: PluginChipProps): import("react/jsx-runtime").JSX.Element;
|
|
20
|
+
}
|
|
21
|
+
export default CluePlugin;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import Fetcher from '@cccsaurora/clue-ui/components/fetchers/Fetcher';
|
|
3
|
+
import clueEN from '@cccsaurora/clue-ui/en/translation.json';
|
|
4
|
+
import clueFR from '@cccsaurora/clue-ui/fr/translation.json';
|
|
5
|
+
import { Box } from '@mui/material';
|
|
6
|
+
import {} from 'i18next';
|
|
7
|
+
import get from 'lodash-es/get';
|
|
8
|
+
import HowlerPlugin from '@cccsaurora/howler-ui/plugins/HowlerPlugin';
|
|
9
|
+
import ClueChip from './components/ClueChip';
|
|
10
|
+
import ClueLeadForm from './components/ClueLeadForm';
|
|
11
|
+
import CluePivot from './components/CluePivot';
|
|
12
|
+
import CluePivotForm from './components/CluePivotForm';
|
|
13
|
+
import ClueTypography from './components/ClueTypography';
|
|
14
|
+
import HELPERS from './helpers';
|
|
15
|
+
import howlerClueEN from './locales/clue.en.json';
|
|
16
|
+
import howlerClueFR from './locales/clue.fr.json';
|
|
17
|
+
import Provider from './Provider';
|
|
18
|
+
import useSetup from './setup';
|
|
19
|
+
class CluePlugin extends HowlerPlugin {
|
|
20
|
+
name = 'CluePlugin';
|
|
21
|
+
version = '0.0.1';
|
|
22
|
+
author = 'Matthew Rafuse <matthew.rafuse@cyber.gc.ca>';
|
|
23
|
+
description = 'This plugin enables clue enrichment in Howler.';
|
|
24
|
+
activate() {
|
|
25
|
+
super.activate();
|
|
26
|
+
const leadForm = props => _jsx(ClueLeadForm, { ...props });
|
|
27
|
+
const leadRenderer = (content, metadata, hit) => {
|
|
28
|
+
const parsedProps = JSON.parse(metadata);
|
|
29
|
+
const value = get(hit, parsedProps.value);
|
|
30
|
+
if (Array.isArray(value)) {
|
|
31
|
+
// TODO: Revisit handling for array values
|
|
32
|
+
parsedProps.value = value[0];
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
parsedProps.value = value;
|
|
36
|
+
}
|
|
37
|
+
return (_jsx(Box, { p: 1, flex: 1, display: "flex", alignItems: "stretch", children: _jsx(Fetcher, { fetcherId: content, ...parsedProps }) }));
|
|
38
|
+
};
|
|
39
|
+
super.addLead('clue', leadForm, leadRenderer);
|
|
40
|
+
const pivotForm = (props) => _jsx(CluePivotForm, { ...props });
|
|
41
|
+
const pivotRenderer = (props) => _jsx(CluePivot, { ...props });
|
|
42
|
+
super.addPivot('clue', pivotForm, pivotRenderer);
|
|
43
|
+
}
|
|
44
|
+
provider() {
|
|
45
|
+
return Provider;
|
|
46
|
+
}
|
|
47
|
+
setup() {
|
|
48
|
+
return useSetup;
|
|
49
|
+
}
|
|
50
|
+
localization(i18nInstance) {
|
|
51
|
+
i18nInstance.addResourceBundle('en', 'clue', clueEN, true, true);
|
|
52
|
+
i18nInstance.addResourceBundle('fr', 'clue', clueFR, true, true);
|
|
53
|
+
i18nInstance.addResourceBundle('en', 'translation', howlerClueEN, true, false);
|
|
54
|
+
i18nInstance.addResourceBundle('fr', 'translation', howlerClueFR, true, false);
|
|
55
|
+
}
|
|
56
|
+
helpers() {
|
|
57
|
+
return HELPERS;
|
|
58
|
+
}
|
|
59
|
+
typography(_props) {
|
|
60
|
+
return _jsx(ClueTypography, { ..._props });
|
|
61
|
+
}
|
|
62
|
+
chip(_props) {
|
|
63
|
+
return _jsx(ClueChip, { ..._props });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export default CluePlugin;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"markdown.helpers.clue.arguments": "You must provide at least two arguments: type and value.",
|
|
3
|
+
"route.dossiers.manager.clue": "Clue Fetcher ID",
|
|
4
|
+
"route.dossiers.manager.clue.type": "Selector Type",
|
|
5
|
+
"route.dossiers.manager.clue.value": "Selector Value",
|
|
6
|
+
"route.dossiers.manager.clue.value.custom": "Custom Selector Value",
|
|
7
|
+
"route.dossiers.manager.clue.value.description": "You can use handlebars notation to insert fields from the corresponding alert (i.e. {{howler.id}})."
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"markdown.helpers.clue.arguments": "Vous devez fournir au moins deux arguments : type et valeur.",
|
|
3
|
+
"route.dossiers.manager.clue": "ID du sélecteur Clue",
|
|
4
|
+
"route.dossiers.manager.clue.type": "Type de sélecteur",
|
|
5
|
+
"route.dossiers.manager.clue.value": "Valeur du sélecteur",
|
|
6
|
+
"route.dossiers.manager.clue.value.custom": "Valeur du sélecteur personnalisé",
|
|
7
|
+
"route.dossiers.manager.clue.value.description": "Vous pouvez utiliser la notation « handlebars » pour insérer des champs à partir de l'alerte correspondante (c'est-à-dire {{howler.id}})."
|
|
8
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { SNACKBAR_EVENT_ID } from '@cccsaurora/clue-ui/data/event';
|
|
2
|
+
import useClue from '@cccsaurora/clue-ui/hooks/useClue';
|
|
3
|
+
import { useAppUser } from '@cccsaurora/howler-ui/commons/components/app/hooks/useAppUser';
|
|
4
|
+
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
5
|
+
import useMySnackbar from '@cccsaurora/howler-ui/components/hooks/useMySnackbar';
|
|
6
|
+
import { useContext, useEffect } from 'react';
|
|
7
|
+
const useSetup = () => {
|
|
8
|
+
const clue = useClue();
|
|
9
|
+
const appUser = useAppUser();
|
|
10
|
+
const { config } = useContext(ApiConfigContext);
|
|
11
|
+
const { showSuccessMessage, showErrorMessage, showInfoMessage, showWarningMessage } = useMySnackbar();
|
|
12
|
+
const features = config.configuration?.features ?? {};
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.debug('Initializing clue snackbar event handler');
|
|
16
|
+
const handleMessage = (event) => {
|
|
17
|
+
const { detail } = event;
|
|
18
|
+
if (detail.level === 'success') {
|
|
19
|
+
showSuccessMessage(detail.message, detail.timeout, detail.options);
|
|
20
|
+
}
|
|
21
|
+
else if (detail.level === 'error') {
|
|
22
|
+
showErrorMessage(detail.message, detail.timeout, detail.options);
|
|
23
|
+
}
|
|
24
|
+
else if (detail.level === 'info') {
|
|
25
|
+
showInfoMessage(detail.message, detail.timeout, detail.options);
|
|
26
|
+
}
|
|
27
|
+
else if (detail.level === 'warning') {
|
|
28
|
+
showWarningMessage(detail.message, detail.timeout, detail.options);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
window.addEventListener(SNACKBAR_EVENT_ID, handleMessage);
|
|
32
|
+
return () => {
|
|
33
|
+
window.removeEventListener(SNACKBAR_EVENT_ID, handleMessage);
|
|
34
|
+
};
|
|
35
|
+
}, [showErrorMessage, showInfoMessage, showSuccessMessage, showWarningMessage]);
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (!appUser.isReady()) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (features.borealis || features.clue) {
|
|
41
|
+
clue.setReady(true);
|
|
42
|
+
}
|
|
43
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
44
|
+
}, [features, appUser.isReady()]);
|
|
45
|
+
};
|
|
46
|
+
export default useSetup;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useClueEnrichSelector } from '@cccsaurora/clue-ui';
|
|
2
|
+
import { ApiConfigContext } from '@cccsaurora/howler-ui/components/app/providers/ApiConfigProvider';
|
|
3
|
+
import { useContext } from 'react';
|
|
4
|
+
export const useType = (hit, field, value) => {
|
|
5
|
+
const guessType = useClueEnrichSelector(ctx => ctx.guessType);
|
|
6
|
+
const { config } = useContext(ApiConfigContext);
|
|
7
|
+
const typeFromHit = hit?.clue?.types?.find(mapping => mapping.field === field)?.type;
|
|
8
|
+
if (typeFromHit) {
|
|
9
|
+
return typeFromHit;
|
|
10
|
+
}
|
|
11
|
+
const typeFromConfig = config.configuration?.mapping?.[field];
|
|
12
|
+
if (typeFromConfig) {
|
|
13
|
+
return typeFromConfig;
|
|
14
|
+
}
|
|
15
|
+
if (value) {
|
|
16
|
+
return guessType(value.toString());
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
};
|
package/plugins/store.js
CHANGED
|
@@ -24,6 +24,9 @@ class HowlerPluginStore {
|
|
|
24
24
|
_routes = [];
|
|
25
25
|
_sitemaps = [];
|
|
26
26
|
install(plugin) {
|
|
27
|
+
if (this.plugins.includes(plugin.name)) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
27
30
|
console.log(`Installing plugin ${plugin.getPluginName()} by ${plugin.author}`);
|
|
28
31
|
this.plugins.push(plugin.name);
|
|
29
32
|
this.pluginStore.install(plugin);
|