@cccsaurora/howler-ui 2.18.0-dev.716 → 2.18.0-dev.723
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/api/index.d.ts +0 -2
- package/api/index.js +2 -4
- package/api/search/facet/hit.d.ts +3 -1
- package/api/search/facet/index.d.ts +1 -3
- package/api/search/index.d.ts +1 -2
- package/api/search/index.js +1 -2
- package/commons/components/leftnav/LeftNavDrawer.js +1 -1
- package/components/app/App.js +7 -39
- package/components/app/hooks/useMatchers.js +2 -2
- package/components/app/hooks/useMatchers.test.js +22 -22
- package/components/app/hooks/useTitle.js +3 -3
- package/components/app/providers/FavouritesProvider.js +2 -2
- package/components/app/providers/HitProvider.d.ts +22 -0
- package/components/app/providers/{RecordProvider.js → HitProvider.js} +41 -41
- package/components/app/providers/{RecordSearchProvider.d.ts → HitSearchProvider.d.ts} +6 -6
- package/components/app/providers/{RecordSearchProvider.js → HitSearchProvider.js} +17 -12
- package/components/app/providers/{RecordSearchProvider.test.js → HitSearchProvider.test.js} +70 -51
- package/components/app/providers/ModalProvider.d.ts +0 -1
- package/components/app/providers/ParameterProvider.d.ts +2 -9
- package/components/app/providers/ParameterProvider.js +240 -165
- package/components/app/providers/ParameterProvider.test.js +14 -307
- package/components/elements/PluginTypography.d.ts +1 -2
- package/components/elements/PluginTypography.js +2 -3
- package/components/elements/UserList.d.ts +2 -5
- package/components/elements/UserList.js +5 -14
- package/components/elements/addons/search/phrase/Phrase.js +1 -1
- package/components/elements/display/ChipPopper.d.ts +1 -1
- package/components/elements/display/HowlerCard.js +1 -1
- package/components/elements/display/Modal.js +0 -2
- package/components/elements/display/icons/BundleButton.d.ts +6 -0
- package/components/elements/display/icons/BundleButton.js +32 -0
- package/components/elements/hit/HitActions.js +4 -4
- package/components/elements/hit/HitBanner.js +48 -28
- package/components/elements/hit/HitCard.d.ts +0 -1
- package/components/elements/hit/HitCard.js +6 -6
- package/components/elements/{record/RecordComments.d.ts → hit/HitComments.d.ts} +4 -5
- package/components/elements/{record/RecordComments.js → hit/HitComments.js} +28 -29
- package/components/elements/{ObjectDetails.js → hit/HitDetails.js} +17 -17
- package/components/elements/hit/HitLabels.js +2 -2
- package/components/elements/hit/{HitPreview.d.ts → HitQuickSearch.d.ts} +3 -3
- package/components/elements/hit/{HitPreview.js → HitQuickSearch.js} +4 -10
- package/components/elements/hit/HitRelated.d.ts +6 -0
- package/components/elements/hit/HitRelated.js +7 -0
- package/components/elements/hit/HitSummary.d.ts +1 -2
- package/components/elements/hit/HitSummary.js +5 -6
- package/components/elements/{record/RecordWorklog.d.ts → hit/HitWorklog.d.ts} +3 -4
- package/components/elements/{record/RecordWorklog.js → hit/HitWorklog.js} +13 -15
- package/components/elements/hit/aggregate/HitGraph.js +8 -8
- package/components/elements/hit/outlines/DefaultOutline.js +1 -1
- package/components/elements/view/ViewTitle.d.ts +0 -1
- package/components/elements/view/ViewTitle.js +2 -9
- package/components/hooks/useHitActions.d.ts +1 -1
- package/components/hooks/useHitActions.js +4 -4
- package/components/hooks/{useRecordSelection.d.ts → useHitSelection.d.ts} +2 -2
- package/components/hooks/{useRecordSelection.js → useHitSelection.js} +33 -12
- package/components/hooks/useMyPreferences.js +1 -10
- package/components/hooks/useMySearch.js +2 -2
- package/components/hooks/useMySitemap.js +1 -4
- package/components/hooks/useMyTheme.js +2 -9
- package/components/hooks/useParamState.test.js +4 -3
- package/components/routes/action/edit/ActionEditor.js +2 -2
- package/components/routes/action/view/ActionSearch.js +1 -1
- package/components/routes/advanced/QueryBuilder.js +1 -1
- package/components/routes/advanced/QueryEditor.js +3 -3
- package/components/routes/advanced/historyCompletionProvider.js +3 -3
- package/components/routes/analytics/AnalyticDetails.js +2 -2
- package/components/routes/analytics/AnalyticSearch.js +1 -1
- package/components/routes/dossiers/DossierEditor.js +2 -2
- package/components/routes/dossiers/DossierEditor.test.js +1 -1
- package/components/routes/help/ApiDocumentation.js +1 -1
- package/components/routes/help/BundleDocumentation.d.ts +3 -0
- package/components/routes/help/BundleDocumentation.js +12 -0
- package/components/routes/help/HitBannerDocumentation.js +0 -1
- package/components/routes/help/HitDocumentation.js +3 -1
- package/components/routes/help/markdown/en/bundles.md.js +1 -0
- package/components/routes/help/markdown/fr/bundles.md.js +1 -0
- package/components/routes/hits/search/BundleParentMenu.d.ts +6 -0
- package/components/routes/hits/search/BundleParentMenu.js +32 -0
- package/components/routes/hits/search/BundleScroller.d.ts +2 -0
- package/components/routes/hits/search/BundleScroller.js +6 -0
- package/components/routes/hits/search/{RecordBrowser.js → HitBrowser.js} +9 -9
- package/components/{elements/record/RecordContextMenu.d.ts → routes/hits/search/HitContextMenu.d.ts} +3 -3
- package/components/routes/hits/search/HitContextMenu.js +227 -0
- package/components/{elements/record/RecordContextMenu.test.js → routes/hits/search/HitContextMenu.test.js} +39 -94
- package/components/routes/hits/search/{RecordQuery.d.ts → HitQuery.d.ts} +2 -2
- package/components/routes/hits/search/{RecordQuery.js → HitQuery.js} +6 -6
- package/components/routes/hits/search/InformationPane.d.ts +0 -1
- package/components/routes/hits/search/InformationPane.js +60 -47
- package/components/routes/hits/search/LayoutSettings.js +3 -3
- package/components/routes/hits/search/QuerySettings.js +1 -2
- package/components/routes/hits/search/QuerySettings.test.js +9 -14
- package/components/routes/hits/search/SearchPane.js +49 -26
- package/components/routes/hits/search/ViewLink.js +3 -3
- package/components/routes/hits/search/ViewLink.test.js +8 -8
- package/components/routes/hits/search/grid/AddColumnModal.js +4 -5
- package/components/routes/hits/search/grid/EnhancedCell.d.ts +1 -2
- package/components/routes/hits/search/grid/EnhancedCell.js +2 -2
- package/components/routes/hits/search/grid/HitGrid.js +18 -20
- package/components/routes/hits/search/grid/{RecordRow.d.ts → HitRow.d.ts} +2 -3
- package/components/routes/hits/search/grid/{RecordRow.js → HitRow.js} +8 -10
- package/components/routes/hits/view/HitViewer.js +13 -12
- package/components/routes/home/ViewCard.js +41 -47
- package/components/{elements/MarkdownEditor.js → routes/overviews/OverviewEditor.js} +3 -3
- package/components/routes/overviews/OverviewViewer.js +2 -2
- package/components/routes/views/ViewComposer.js +19 -46
- package/locales/en/translation.json +3 -88
- package/locales/fr/translation.json +3 -86
- package/models/WithMetadata.d.ts +1 -2
- package/models/entities/generated/{ThreatEnrichment.d.ts → Enrichment.d.ts} +1 -1
- package/models/entities/generated/Hit.d.ts +0 -1
- package/models/entities/generated/Howler.d.ts +4 -0
- package/models/entities/generated/Rule.d.ts +10 -2
- package/models/entities/generated/Threat.d.ts +2 -2
- package/models/entities/generated/View.d.ts +0 -1
- package/package.json +1 -18
- package/plugins/clue/components/ClueTypography.js +2 -2
- package/plugins/clue/utils.d.ts +1 -2
- package/tests/server-handlers.js +1 -6
- package/tests/utils.d.ts +0 -4
- package/tests/utils.js +0 -20
- package/utils/constants.d.ts +3 -3
- package/utils/hitFunctions.d.ts +1 -2
- package/utils/hitFunctions.js +4 -4
- package/utils/viewUtils.js +0 -3
- package/api/search/case.d.ts +0 -4
- package/api/search/case.js +0 -8
- package/api/v2/case/index.d.ts +0 -8
- package/api/v2/case/index.js +0 -20
- package/api/v2/case/items.d.ts +0 -6
- package/api/v2/case/items.js +0 -18
- package/api/v2/index.d.ts +0 -4
- package/api/v2/index.js +0 -6
- package/api/v2/search/facet.d.ts +0 -3
- package/api/v2/search/facet.js +0 -12
- package/api/v2/search/index.d.ts +0 -5
- package/api/v2/search/index.js +0 -24
- package/components/app/providers/RecordProvider.d.ts +0 -23
- package/components/elements/ContextMenu.d.ts +0 -56
- package/components/elements/ContextMenu.js +0 -109
- package/components/elements/ContextMenu.test.js +0 -215
- package/components/elements/ObjectDetails.d.ts +0 -6
- package/components/elements/case/CaseCard.d.ts +0 -12
- package/components/elements/case/CaseCard.js +0 -42
- package/components/elements/case/CasePreview.d.ts +0 -6
- package/components/elements/case/CasePreview.js +0 -17
- package/components/elements/case/StatusIcon.d.ts +0 -5
- package/components/elements/case/StatusIcon.js +0 -13
- package/components/elements/hit/elements/AnalyticLink.d.ts +0 -8
- package/components/elements/hit/elements/AnalyticLink.js +0 -22
- package/components/elements/hit/related/RelatedRecords.js +0 -63
- package/components/elements/observable/ObservableCard.d.ts +0 -6
- package/components/elements/observable/ObservableCard.js +0 -22
- package/components/elements/observable/ObservablePreview.d.ts +0 -6
- package/components/elements/observable/ObservablePreview.js +0 -12
- package/components/elements/record/RecordContextMenu.js +0 -247
- package/components/elements/record/RecordContextMenu.test.d.ts +0 -1
- package/components/elements/record/RecordRelated.d.ts +0 -7
- package/components/elements/record/RecordRelated.js +0 -34
- package/components/hooks/useRelatedRecords.d.ts +0 -13
- package/components/hooks/useRelatedRecords.js +0 -32
- package/components/routes/cases/CaseViewer.d.ts +0 -2
- package/components/routes/cases/CaseViewer.js +0 -22
- package/components/routes/cases/Cases.d.ts +0 -2
- package/components/routes/cases/Cases.js +0 -101
- package/components/routes/cases/constants.d.ts +0 -5
- package/components/routes/cases/constants.js +0 -5
- package/components/routes/cases/detail/AlertPanel.d.ts +0 -6
- package/components/routes/cases/detail/AlertPanel.js +0 -33
- package/components/routes/cases/detail/CaseAssets.d.ts +0 -11
- package/components/routes/cases/detail/CaseAssets.js +0 -104
- package/components/routes/cases/detail/CaseAssets.test.d.ts +0 -1
- package/components/routes/cases/detail/CaseAssets.test.js +0 -167
- package/components/routes/cases/detail/CaseDashboard.d.ts +0 -7
- package/components/routes/cases/detail/CaseDashboard.js +0 -54
- package/components/routes/cases/detail/CaseDetails.d.ts +0 -6
- package/components/routes/cases/detail/CaseDetails.js +0 -61
- package/components/routes/cases/detail/CaseOverview.d.ts +0 -7
- package/components/routes/cases/detail/CaseOverview.js +0 -43
- package/components/routes/cases/detail/CaseSidebar.d.ts +0 -8
- package/components/routes/cases/detail/CaseSidebar.js +0 -50
- package/components/routes/cases/detail/CaseTask.d.ts +0 -11
- package/components/routes/cases/detail/CaseTask.js +0 -57
- package/components/routes/cases/detail/CaseTimeline.d.ts +0 -12
- package/components/routes/cases/detail/CaseTimeline.js +0 -106
- package/components/routes/cases/detail/CaseTimeline.test.d.ts +0 -1
- package/components/routes/cases/detail/CaseTimeline.test.js +0 -227
- package/components/routes/cases/detail/ItemPage.d.ts +0 -6
- package/components/routes/cases/detail/ItemPage.js +0 -99
- package/components/routes/cases/detail/RelatedCasePanel.d.ts +0 -6
- package/components/routes/cases/detail/RelatedCasePanel.js +0 -31
- package/components/routes/cases/detail/TaskPanel.d.ts +0 -7
- package/components/routes/cases/detail/TaskPanel.js +0 -52
- package/components/routes/cases/detail/aggregates/CaseAggregate.d.ts +0 -12
- package/components/routes/cases/detail/aggregates/CaseAggregate.js +0 -19
- package/components/routes/cases/detail/aggregates/SourceAggregate.d.ts +0 -6
- package/components/routes/cases/detail/aggregates/SourceAggregate.js +0 -30
- package/components/routes/cases/detail/assets/Asset.d.ts +0 -14
- package/components/routes/cases/detail/assets/Asset.js +0 -12
- package/components/routes/cases/detail/assets/Asset.test.d.ts +0 -1
- package/components/routes/cases/detail/assets/Asset.test.js +0 -72
- package/components/routes/cases/detail/sidebar/CaseFolder.d.ts +0 -14
- package/components/routes/cases/detail/sidebar/CaseFolder.js +0 -136
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.d.ts +0 -34
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.js +0 -105
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.d.ts +0 -1
- package/components/routes/cases/detail/sidebar/CaseFolderContextMenu.test.js +0 -351
- package/components/routes/cases/detail/sidebar/types.d.ts +0 -3
- package/components/routes/cases/detail/sidebar/utils.d.ts +0 -3
- package/components/routes/cases/detail/sidebar/utils.js +0 -25
- package/components/routes/cases/hooks/useCase.d.ts +0 -13
- package/components/routes/cases/hooks/useCase.js +0 -51
- package/components/routes/cases/modals/AddToCaseModal.d.ts +0 -7
- package/components/routes/cases/modals/AddToCaseModal.js +0 -62
- package/components/routes/cases/modals/RenameItemModal.d.ts +0 -9
- package/components/routes/cases/modals/RenameItemModal.js +0 -48
- package/components/routes/cases/modals/ResolveModal.d.ts +0 -7
- package/components/routes/cases/modals/ResolveModal.js +0 -115
- package/components/routes/cases/modals/ResolveModal.test.d.ts +0 -1
- package/components/routes/cases/modals/ResolveModal.test.js +0 -384
- package/components/routes/hits/search/shared/IndexPicker.d.ts +0 -2
- package/components/routes/hits/search/shared/IndexPicker.js +0 -20
- package/components/routes/observables/ObservableViewer.d.ts +0 -7
- package/components/routes/observables/ObservableViewer.js +0 -27
- package/models/entities/generated/AttachmentsFile.d.ts +0 -12
- package/models/entities/generated/Case.d.ts +0 -28
- package/models/entities/generated/DestinationOriginal.d.ts +0 -19
- package/models/entities/generated/EmailAttachment.d.ts +0 -8
- package/models/entities/generated/EmailParent.d.ts +0 -19
- package/models/entities/generated/Enrichments.d.ts +0 -7
- package/models/entities/generated/EnrichmentsIndicator.d.ts +0 -21
- package/models/entities/generated/HttpResponse.d.ts +0 -11
- package/models/entities/generated/Item.d.ts +0 -9
- package/models/entities/generated/Observable.d.ts +0 -85
- package/models/entities/generated/ObservableCloud.d.ts +0 -20
- package/models/entities/generated/ObservableDestination.d.ts +0 -23
- package/models/entities/generated/ObservableEmail.d.ts +0 -30
- package/models/entities/generated/ObservableFile.d.ts +0 -36
- package/models/entities/generated/ObservableHowler.d.ts +0 -43
- package/models/entities/generated/ObservableHttp.d.ts +0 -11
- package/models/entities/generated/ObservableObserver.d.ts +0 -21
- package/models/entities/generated/ObservableOrganization.d.ts +0 -7
- package/models/entities/generated/ObservableProcess.d.ts +0 -34
- package/models/entities/generated/ObservableSource.d.ts +0 -23
- package/models/entities/generated/ObservableThreat.d.ts +0 -21
- package/models/entities/generated/ObservableTls.d.ts +0 -12
- package/models/entities/generated/ObserverIngress.d.ts +0 -9
- package/models/entities/generated/Task.d.ts +0 -10
- package/utils/typeUtils.d.ts +0 -7
- package/utils/typeUtils.js +0 -27
- /package/components/app/providers/{RecordSearchProvider.test.d.ts → HitSearchProvider.test.d.ts} +0 -0
- /package/components/elements/hit/{related/RelatedRecords.d.ts → HitDetails.d.ts} +0 -0
- /package/components/routes/hits/search/{RecordBrowser.d.ts → HitBrowser.d.ts} +0 -0
- /package/components/{elements/ContextMenu.test.d.ts → routes/hits/search/HitContextMenu.test.d.ts} +0 -0
- /package/components/{elements/MarkdownEditor.d.ts → routes/overviews/OverviewEditor.d.ts} +0 -0
package/api/index.d.ts
CHANGED
|
@@ -10,7 +10,6 @@ import * as overview from '@cccsaurora/howler-ui/api/overview';
|
|
|
10
10
|
import * as search from '@cccsaurora/howler-ui/api/search';
|
|
11
11
|
import * as template from '@cccsaurora/howler-ui/api/template';
|
|
12
12
|
import * as user from '@cccsaurora/howler-ui/api/user';
|
|
13
|
-
import * as v2 from '@cccsaurora/howler-ui/api/v2';
|
|
14
13
|
import * as view from '@cccsaurora/howler-ui/api/view';
|
|
15
14
|
/**
|
|
16
15
|
* Defining the default export exposing all children routes of '/api/v1/'.
|
|
@@ -29,7 +28,6 @@ declare const api: {
|
|
|
29
28
|
user: typeof user;
|
|
30
29
|
view: typeof view;
|
|
31
30
|
notebook: typeof notebook;
|
|
32
|
-
v2: typeof v2;
|
|
33
31
|
};
|
|
34
32
|
/**
|
|
35
33
|
* The specification interface of an Howler HTTP response.
|
package/api/index.js
CHANGED
|
@@ -10,7 +10,6 @@ import * as overview from '@cccsaurora/howler-ui/api/overview';
|
|
|
10
10
|
import * as search from '@cccsaurora/howler-ui/api/search';
|
|
11
11
|
import * as template from '@cccsaurora/howler-ui/api/template';
|
|
12
12
|
import * as user from '@cccsaurora/howler-ui/api/user';
|
|
13
|
-
import * as v2 from '@cccsaurora/howler-ui/api/v2';
|
|
14
13
|
import * as view from '@cccsaurora/howler-ui/api/view';
|
|
15
14
|
import AxiosClient from '@cccsaurora/howler-ui/rest/AxiosClient';
|
|
16
15
|
import urlJoin from 'url-join';
|
|
@@ -39,8 +38,7 @@ const api = {
|
|
|
39
38
|
template,
|
|
40
39
|
user,
|
|
41
40
|
view,
|
|
42
|
-
notebook
|
|
43
|
-
v2
|
|
41
|
+
notebook
|
|
44
42
|
};
|
|
45
43
|
/**
|
|
46
44
|
* The base section of the Howler API uri.
|
|
@@ -59,7 +57,7 @@ export const uri = () => {
|
|
|
59
57
|
* @returns `string` - properly formatted howler uri.
|
|
60
58
|
*/
|
|
61
59
|
const format = (_uri) => {
|
|
62
|
-
return _uri.startsWith(
|
|
60
|
+
return _uri.startsWith(uri()) ? _uri : `${uri()}/${_uri.replace(/\/$/, '')}`;
|
|
63
61
|
};
|
|
64
62
|
/**
|
|
65
63
|
* Append series of search parameters to the specified uri.
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { HowlerFacetSearchRequest, HowlerFacetSearchResponse } from '@cccsaurora/howler-ui/api/search/facet';
|
|
2
2
|
export declare const uri: () => string;
|
|
3
|
-
export declare const post: (request?: HowlerFacetSearchRequest) => Promise<
|
|
3
|
+
export declare const post: (request?: HowlerFacetSearchRequest) => Promise<{
|
|
4
|
+
[index: string]: HowlerFacetSearchResponse;
|
|
5
|
+
}>;
|
package/api/search/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as action from '@cccsaurora/howler-ui/api/search/action';
|
|
2
2
|
import * as analytic from '@cccsaurora/howler-ui/api/search/analytic';
|
|
3
|
-
import * as case_ from '@cccsaurora/howler-ui/api/search/case';
|
|
4
3
|
import * as count from '@cccsaurora/howler-ui/api/search/count';
|
|
5
4
|
import * as dossier from '@cccsaurora/howler-ui/api/search/dossier';
|
|
6
5
|
import * as facet from '@cccsaurora/howler-ui/api/search/facet';
|
|
@@ -61,4 +60,4 @@ export type HowlerExplainSearchResponse = {
|
|
|
61
60
|
explanation: string;
|
|
62
61
|
}[];
|
|
63
62
|
};
|
|
64
|
-
export { action, analytic,
|
|
63
|
+
export { action, analytic, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
|
package/api/search/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { joinUri, uri as parentUri } from '@cccsaurora/howler-ui/api';
|
|
2
2
|
import * as action from '@cccsaurora/howler-ui/api/search/action';
|
|
3
3
|
import * as analytic from '@cccsaurora/howler-ui/api/search/analytic';
|
|
4
|
-
import * as case_ from '@cccsaurora/howler-ui/api/search/case';
|
|
5
4
|
import * as count from '@cccsaurora/howler-ui/api/search/count';
|
|
6
5
|
import * as dossier from '@cccsaurora/howler-ui/api/search/dossier';
|
|
7
6
|
import * as facet from '@cccsaurora/howler-ui/api/search/facet';
|
|
@@ -16,4 +15,4 @@ import * as view from '@cccsaurora/howler-ui/api/search/view';
|
|
|
16
15
|
export const uri = () => {
|
|
17
16
|
return joinUri(parentUri(), 'search');
|
|
18
17
|
};
|
|
19
|
-
export { action, analytic,
|
|
18
|
+
export { action, analytic, count, dossier, facet, fields, grouped, histogram, hit, overview, template, user, view };
|
|
@@ -75,7 +75,7 @@ const LeftNavDrawer = () => {
|
|
|
75
75
|
}
|
|
76
76
|
}, children: _jsx(AppName, {}) }), !isTopLayout && _jsx(Divider, {})] }));
|
|
77
77
|
const hide = (_jsx(List, { disablePadding: true, children: _jsxs(ListItemButton, { onClick: leftnav.toggle, children: [_jsx(ListItemIcon, { children: leftnav.open ? _jsx(ChevronLeftIcon, {}) : _jsx(ChevronRightIcon, {}) }), _jsx(ListItemText, { primary: t('drawer.collapse') })] }, "chevron") }));
|
|
78
|
-
return (_jsx(ClickAwayListener, { mouseEvent: "onMouseDown", touchEvent: "onTouchStart", onClickAway: onCloseDrawerIfOpen, children: _jsxs(StyledDrawer, { variant: "permanent", style: { height: '100%' }, width: preferences.leftnav.width, open: leftnav.open, children: [leftnav.open ? (header) : (_jsx(Tooltip, { title: preferences.appName, "aria-label": preferences.appName, placement: "right", children: header })), _jsx(List, { disablePadding: true, children: leftnav.elements.map((e, i) => {
|
|
78
|
+
return (_jsx(ClickAwayListener, { mouseEvent: "onMouseDown", touchEvent: "onTouchStart", onClickAway: onCloseDrawerIfOpen, children: _jsxs(StyledDrawer, { PaperProps: { elevation: 1 }, variant: "permanent", style: { height: '100%' }, width: preferences.leftnav.width, open: leftnav.open, children: [leftnav.open ? (header) : (_jsx(Tooltip, { title: preferences.appName, "aria-label": preferences.appName, placement: "right", children: header })), _jsx(List, { disablePadding: true, children: leftnav.elements.map((e, i) => {
|
|
79
79
|
if (e.type === 'item') {
|
|
80
80
|
const item = e.element;
|
|
81
81
|
return _jsx(LeftNavItem, { item: item, onClick: isSmDown && onCloseDrawerIfOpen }, item.id);
|
package/components/app/App.js
CHANGED
|
@@ -25,12 +25,6 @@ import UserSearchProvider from '@cccsaurora/howler-ui/components/routes/admin/us
|
|
|
25
25
|
import QueryBuilder from '@cccsaurora/howler-ui/components/routes/advanced/QueryBuilder';
|
|
26
26
|
import AnalyticDetails from '@cccsaurora/howler-ui/components/routes/analytics/AnalyticDetails';
|
|
27
27
|
import AnalyticSearch from '@cccsaurora/howler-ui/components/routes/analytics/AnalyticSearch';
|
|
28
|
-
import CaseViewer from '@cccsaurora/howler-ui/components/routes/cases/CaseViewer';
|
|
29
|
-
import Cases from '@cccsaurora/howler-ui/components/routes/cases/Cases';
|
|
30
|
-
import CaseAssets from '@cccsaurora/howler-ui/components/routes/cases/detail/CaseAssets';
|
|
31
|
-
import CaseDashboard from '@cccsaurora/howler-ui/components/routes/cases/detail/CaseDashboard';
|
|
32
|
-
import CaseTimeline from '@cccsaurora/howler-ui/components/routes/cases/detail/CaseTimeline';
|
|
33
|
-
import ItemPage from '@cccsaurora/howler-ui/components/routes/cases/detail/ItemPage';
|
|
34
28
|
import DossierEditor from '@cccsaurora/howler-ui/components/routes/dossiers/DossierEditor';
|
|
35
29
|
import Dossiers from '@cccsaurora/howler-ui/components/routes/dossiers/Dossiers';
|
|
36
30
|
import ActionDocumentation from '@cccsaurora/howler-ui/components/routes/help/ActionDocumentation';
|
|
@@ -45,7 +39,7 @@ import RetentionDocumentation from '@cccsaurora/howler-ui/components/routes/help
|
|
|
45
39
|
import SearchDocumentation from '@cccsaurora/howler-ui/components/routes/help/SearchDocumentation';
|
|
46
40
|
import TemplateDocumentation from '@cccsaurora/howler-ui/components/routes/help/TemplateDocumentation';
|
|
47
41
|
import ViewDocumentation from '@cccsaurora/howler-ui/components/routes/help/ViewDocumentation';
|
|
48
|
-
import
|
|
42
|
+
import HitBrowser from '@cccsaurora/howler-ui/components/routes/hits/search/HitBrowser';
|
|
49
43
|
import HitViewer from '@cccsaurora/howler-ui/components/routes/hits/view/HitViewer';
|
|
50
44
|
import Home from '@cccsaurora/howler-ui/components/routes/home';
|
|
51
45
|
import OverviewViewer from '@cccsaurora/howler-ui/components/routes/overviews/OverviewViewer';
|
|
@@ -77,11 +71,11 @@ import AvatarProvider from './providers/AvatarProvider';
|
|
|
77
71
|
import CustomPluginProvider from './providers/CustomPluginProvider';
|
|
78
72
|
import FavouriteProvider from './providers/FavouritesProvider';
|
|
79
73
|
import FieldProvider from './providers/FieldProvider';
|
|
74
|
+
import HitProvider from './providers/HitProvider';
|
|
80
75
|
import LocalStorageProvider from './providers/LocalStorageProvider';
|
|
81
76
|
import ModalProvider from './providers/ModalProvider';
|
|
82
77
|
import OverviewProvider from './providers/OverviewProvider';
|
|
83
78
|
import ParameterProvider from './providers/ParameterProvider';
|
|
84
|
-
import RecordProvider from './providers/RecordProvider';
|
|
85
79
|
import SocketProvider from './providers/SocketProvider';
|
|
86
80
|
import UserListProvider from './providers/UserListProvider';
|
|
87
81
|
import ViewProvider from './providers/ViewProvider';
|
|
@@ -154,7 +148,7 @@ const MyAppProvider = ({ children }) => {
|
|
|
154
148
|
const mySitemap = useMySitemap();
|
|
155
149
|
const myUser = useMyUser();
|
|
156
150
|
const mySearch = useMySearch();
|
|
157
|
-
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(
|
|
151
|
+
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 }) }) }) }) }) }) }) }) }) }) }) }) }) }) }) }));
|
|
158
152
|
};
|
|
159
153
|
const AppProviderWrapper = () => {
|
|
160
154
|
return (_jsx(I18nextProvider, { i18n: i18n, defaultNS: "translation", children: _jsx(ApiConfigProvider, { children: _jsx(PluginProvider, { pluginStore: howlerPluginStore.pluginStore, children: _jsx(AppBarProvider, { children: _jsxs(MyAppProvider, { children: [_jsx(MyApp, {}), _jsx(Modal, {})] }) }) }) }) }));
|
|
@@ -178,11 +172,11 @@ const router = createBrowserRouter([
|
|
|
178
172
|
},
|
|
179
173
|
{
|
|
180
174
|
path: 'hits',
|
|
181
|
-
element: _jsx(
|
|
175
|
+
element: _jsx(HitBrowser, {})
|
|
182
176
|
},
|
|
183
177
|
{
|
|
184
178
|
path: 'search',
|
|
185
|
-
element: _jsx(
|
|
179
|
+
element: _jsx(HitBrowser, {})
|
|
186
180
|
},
|
|
187
181
|
{
|
|
188
182
|
path: 'hits/:id',
|
|
@@ -190,33 +184,7 @@ const router = createBrowserRouter([
|
|
|
190
184
|
},
|
|
191
185
|
{
|
|
192
186
|
path: 'bundles/:id',
|
|
193
|
-
element: _jsx(
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
path: 'cases',
|
|
197
|
-
element: _jsx(Cases, {})
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
path: 'cases/:id',
|
|
201
|
-
element: _jsx(CaseViewer, {}),
|
|
202
|
-
children: [
|
|
203
|
-
{
|
|
204
|
-
index: true,
|
|
205
|
-
element: _jsx(CaseDashboard, {})
|
|
206
|
-
},
|
|
207
|
-
{
|
|
208
|
-
path: 'assets',
|
|
209
|
-
element: _jsx(CaseAssets, {})
|
|
210
|
-
},
|
|
211
|
-
{
|
|
212
|
-
path: 'timeline',
|
|
213
|
-
element: _jsx(CaseTimeline, {})
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
path: '*',
|
|
217
|
-
element: _jsx(ItemPage, {})
|
|
218
|
-
}
|
|
219
|
-
]
|
|
187
|
+
element: _jsx(HitBrowser, {})
|
|
220
188
|
},
|
|
221
189
|
{
|
|
222
190
|
path: 'templates',
|
|
@@ -256,7 +224,7 @@ const router = createBrowserRouter([
|
|
|
256
224
|
},
|
|
257
225
|
{
|
|
258
226
|
path: 'views/:id',
|
|
259
|
-
element: _jsx(
|
|
227
|
+
element: _jsx(HitBrowser, {})
|
|
260
228
|
},
|
|
261
229
|
{
|
|
262
230
|
path: 'views/:id/edit',
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { has } from 'lodash-es';
|
|
3
3
|
import { useCallback } from 'react';
|
|
4
4
|
import { useContextSelector } from 'use-context-selector';
|
|
5
|
-
import {
|
|
5
|
+
import { HitContext } from '../providers/HitProvider';
|
|
6
6
|
const useMatchers = () => {
|
|
7
|
-
const getHit = useContextSelector(
|
|
7
|
+
const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
|
|
8
8
|
const getMatchingTemplate = useCallback(async (hit) => {
|
|
9
9
|
if (!hit) {
|
|
10
10
|
return null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderHook } from '@testing-library/react';
|
|
2
2
|
import { useContextSelector } from 'use-context-selector';
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import {
|
|
4
|
+
import { HitContext } from '../providers/HitProvider';
|
|
5
5
|
import useMatchers from './useMatchers';
|
|
6
6
|
// Mock the useContextSelector hook
|
|
7
7
|
vi.mock('use-context-selector', () => ({
|
|
@@ -40,14 +40,14 @@ const mockHitWithMetadata = {
|
|
|
40
40
|
__overview: mockOverview,
|
|
41
41
|
__dossiers: mockDossiers
|
|
42
42
|
};
|
|
43
|
-
const
|
|
43
|
+
const mockGetHit = vi.fn();
|
|
44
44
|
describe('useMatchers', () => {
|
|
45
45
|
beforeEach(() => {
|
|
46
46
|
vi.clearAllMocks();
|
|
47
47
|
// Mock useContextSelector to return our mock getHit function
|
|
48
48
|
useContextSelector.mockImplementation((context, selector) => {
|
|
49
|
-
if (context ===
|
|
50
|
-
return selector({
|
|
49
|
+
if (context === HitContext) {
|
|
50
|
+
return selector({ getHit: mockGetHit });
|
|
51
51
|
}
|
|
52
52
|
return null;
|
|
53
53
|
});
|
|
@@ -69,25 +69,25 @@ describe('useMatchers', () => {
|
|
|
69
69
|
const { result } = renderHook(() => useMatchers());
|
|
70
70
|
const template = await result.current.getMatchingTemplate(mockHitWithMetadata);
|
|
71
71
|
expect(template).toBe(mockTemplate);
|
|
72
|
-
expect(
|
|
72
|
+
expect(mockGetHit).not.toHaveBeenCalled();
|
|
73
73
|
});
|
|
74
74
|
it('should fetch hit with metadata when template is not present', async () => {
|
|
75
75
|
const { has } = await import('lodash-es');
|
|
76
76
|
has.mockReturnValue(false);
|
|
77
77
|
const hitWithFetchedMetadata = { ...mockHit, __template: mockTemplate };
|
|
78
|
-
|
|
78
|
+
mockGetHit.mockResolvedValue(hitWithFetchedMetadata);
|
|
79
79
|
const { result } = renderHook(() => useMatchers());
|
|
80
80
|
const template = await result.current.getMatchingTemplate(mockHit);
|
|
81
81
|
expect(template).toBe(mockTemplate);
|
|
82
|
-
expect(
|
|
82
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
83
83
|
});
|
|
84
84
|
it('should handle getHit rejection gracefully', async () => {
|
|
85
85
|
const { has } = await import('lodash-es');
|
|
86
86
|
has.mockReturnValue(false);
|
|
87
|
-
|
|
87
|
+
mockGetHit.mockRejectedValue(new Error('Failed to fetch hit'));
|
|
88
88
|
const { result } = renderHook(() => useMatchers());
|
|
89
89
|
await expect(result.current.getMatchingTemplate(mockHit)).resolves.toBeNull();
|
|
90
|
-
expect(
|
|
90
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
93
|
describe('getMatchingOverview', () => {
|
|
@@ -107,25 +107,25 @@ describe('useMatchers', () => {
|
|
|
107
107
|
const { result } = renderHook(() => useMatchers());
|
|
108
108
|
const overview = await result.current.getMatchingOverview(mockHitWithMetadata);
|
|
109
109
|
expect(overview).toBe(mockOverview);
|
|
110
|
-
expect(
|
|
110
|
+
expect(mockGetHit).not.toHaveBeenCalled();
|
|
111
111
|
});
|
|
112
112
|
it('should fetch hit with metadata when overview is not present', async () => {
|
|
113
113
|
const { has } = await import('lodash-es');
|
|
114
114
|
has.mockReturnValue(false);
|
|
115
115
|
const hitWithFetchedMetadata = { ...mockHit, __overview: mockOverview };
|
|
116
|
-
|
|
116
|
+
mockGetHit.mockResolvedValue(hitWithFetchedMetadata);
|
|
117
117
|
const { result } = renderHook(() => useMatchers());
|
|
118
118
|
const overview = await result.current.getMatchingOverview(mockHit);
|
|
119
119
|
expect(overview).toBe(mockOverview);
|
|
120
|
-
expect(
|
|
120
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
121
121
|
});
|
|
122
122
|
it('should handle getHit rejection gracefully', async () => {
|
|
123
123
|
const { has } = await import('lodash-es');
|
|
124
124
|
has.mockReturnValue(false);
|
|
125
|
-
|
|
125
|
+
mockGetHit.mockRejectedValue(new Error('Failed to fetch hit'));
|
|
126
126
|
const { result } = renderHook(() => useMatchers());
|
|
127
127
|
await expect(result.current.getMatchingOverview(mockHit)).resolves.toBeNull();
|
|
128
|
-
expect(
|
|
128
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
129
129
|
});
|
|
130
130
|
});
|
|
131
131
|
describe('getMatchingDossiers', () => {
|
|
@@ -145,25 +145,25 @@ describe('useMatchers', () => {
|
|
|
145
145
|
const { result } = renderHook(() => useMatchers());
|
|
146
146
|
const dossiers = await result.current.getMatchingDossiers(mockHitWithMetadata);
|
|
147
147
|
expect(dossiers).toBe(mockDossiers);
|
|
148
|
-
expect(
|
|
148
|
+
expect(mockGetHit).not.toHaveBeenCalled();
|
|
149
149
|
});
|
|
150
150
|
it('should fetch hit with metadata when dossiers are not present', async () => {
|
|
151
151
|
const { has } = await import('lodash-es');
|
|
152
152
|
has.mockReturnValue(false);
|
|
153
153
|
const hitWithFetchedMetadata = { ...mockHit, __dossiers: mockDossiers };
|
|
154
|
-
|
|
154
|
+
mockGetHit.mockResolvedValue(hitWithFetchedMetadata);
|
|
155
155
|
const { result } = renderHook(() => useMatchers());
|
|
156
156
|
const dossiers = await result.current.getMatchingDossiers(mockHit);
|
|
157
157
|
expect(dossiers).toBe(mockDossiers);
|
|
158
|
-
expect(
|
|
158
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
159
159
|
});
|
|
160
160
|
it('should handle getHit rejection gracefully', async () => {
|
|
161
161
|
const { has } = await import('lodash-es');
|
|
162
162
|
has.mockReturnValue(false);
|
|
163
|
-
|
|
163
|
+
mockGetHit.mockRejectedValue(new Error('Failed to fetch hit'));
|
|
164
164
|
const { result } = renderHook(() => useMatchers());
|
|
165
165
|
await expect(result.current.getMatchingDossiers(mockHit)).resolves.toEqual([]);
|
|
166
|
-
expect(
|
|
166
|
+
expect(mockGetHit).toHaveBeenCalledWith('test-hit-id', true);
|
|
167
167
|
});
|
|
168
168
|
});
|
|
169
169
|
describe('integration tests', () => {
|
|
@@ -177,7 +177,7 @@ describe('useMatchers', () => {
|
|
|
177
177
|
...mockHit,
|
|
178
178
|
__template: mockTemplate
|
|
179
179
|
};
|
|
180
|
-
|
|
180
|
+
mockGetHit.mockResolvedValue({
|
|
181
181
|
...mockHit,
|
|
182
182
|
__overview: mockOverview,
|
|
183
183
|
__dossiers: mockDossiers
|
|
@@ -200,7 +200,7 @@ describe('useMatchers', () => {
|
|
|
200
200
|
it('should handle empty or undefined metadata gracefully', async () => {
|
|
201
201
|
const { has } = await import('lodash-es');
|
|
202
202
|
has.mockReturnValue(false);
|
|
203
|
-
|
|
203
|
+
mockGetHit.mockResolvedValue({
|
|
204
204
|
...mockHit,
|
|
205
205
|
__template: undefined,
|
|
206
206
|
__overview: null,
|
|
@@ -213,7 +213,7 @@ describe('useMatchers', () => {
|
|
|
213
213
|
expect(template).toBeUndefined();
|
|
214
214
|
expect(overview).toBeNull();
|
|
215
215
|
expect(dossiers).toEqual([]);
|
|
216
|
-
expect(
|
|
216
|
+
expect(mockGetHit).toHaveBeenCalledTimes(3);
|
|
217
217
|
});
|
|
218
218
|
it('should maintain referential equality of returned functions', () => {
|
|
219
219
|
const { result, rerender } = renderHook(() => useMatchers());
|
|
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
import { useLocation, useParams, useSearchParams } from 'react-router-dom';
|
|
6
6
|
import { useContextSelector } from 'use-context-selector';
|
|
7
7
|
import { AnalyticContext } from '../providers/AnalyticProvider';
|
|
8
|
-
import {
|
|
8
|
+
import { HitContext } from '../providers/HitProvider';
|
|
9
9
|
const useTitle = () => {
|
|
10
10
|
const { t } = useTranslation();
|
|
11
11
|
const location = useLocation();
|
|
@@ -13,8 +13,8 @@ const useTitle = () => {
|
|
|
13
13
|
const searchParams = useSearchParams()[0];
|
|
14
14
|
const sitemap = useMySitemap();
|
|
15
15
|
const { getAnalyticFromId } = useContext(AnalyticContext);
|
|
16
|
-
const hits = useContextSelector(
|
|
17
|
-
const getHit = useContextSelector(
|
|
16
|
+
const hits = useContextSelector(HitContext, ctx => ctx.hits);
|
|
17
|
+
const getHit = useContextSelector(HitContext, ctx => ctx.getHit);
|
|
18
18
|
const setTitle = useCallback((title) => {
|
|
19
19
|
document.querySelector('title').innerHTML = title;
|
|
20
20
|
}, []);
|
|
@@ -120,11 +120,11 @@ const FavouriteProvider = ({ children }) => {
|
|
|
120
120
|
(async () => {
|
|
121
121
|
const analyticElement = processAnalyticElement();
|
|
122
122
|
if (analyticElement) {
|
|
123
|
-
newElements.splice(
|
|
123
|
+
newElements.splice(1, 0, analyticElement);
|
|
124
124
|
}
|
|
125
125
|
const viewElement = await processViewElement();
|
|
126
126
|
if (viewElement) {
|
|
127
|
-
newElements.splice(
|
|
127
|
+
newElements.splice(1, 0, viewElement);
|
|
128
128
|
}
|
|
129
129
|
leftNav.setElements(newElements);
|
|
130
130
|
})();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
2
|
+
import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
|
|
3
|
+
import type { FC, PropsWithChildren } from 'react';
|
|
4
|
+
export interface HitContextType {
|
|
5
|
+
hits: {
|
|
6
|
+
[index: string]: Hit;
|
|
7
|
+
};
|
|
8
|
+
selectedHits: Hit[];
|
|
9
|
+
addHitToSelection: (id: string) => void;
|
|
10
|
+
removeHitFromSelection: (id: string) => void;
|
|
11
|
+
clearSelectedHits: (except?: string) => void;
|
|
12
|
+
loadHits: (hits: Hit[]) => void;
|
|
13
|
+
updateHit: (newHit: Hit) => void;
|
|
14
|
+
getHit: (id: string, force?: boolean) => Promise<WithMetadata<Hit>>;
|
|
15
|
+
}
|
|
16
|
+
export declare const HitContext: import("use-context-selector").Context<HitContextType>;
|
|
17
|
+
/**
|
|
18
|
+
* Central repository for storing individual hit data across the application. Allows efficient retrieval of hits across componenents.
|
|
19
|
+
*/
|
|
20
|
+
declare const HitProvider: FC<PropsWithChildren>;
|
|
21
|
+
export declare const useHitContextSelector: <Selected>(selector: (value: HitContextType) => Selected) => Selected;
|
|
22
|
+
export default HitProvider;
|
|
@@ -5,25 +5,25 @@ import { uniq } from 'lodash-es';
|
|
|
5
5
|
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
6
|
import { createContext, useContextSelector } from 'use-context-selector';
|
|
7
7
|
import { SocketContext } from './SocketProvider';
|
|
8
|
-
export const
|
|
8
|
+
export const HitContext = createContext(null);
|
|
9
9
|
/**
|
|
10
10
|
* Central repository for storing individual hit data across the application. Allows efficient retrieval of hits across componenents.
|
|
11
11
|
*/
|
|
12
|
-
const
|
|
12
|
+
const HitProvider = ({ children }) => {
|
|
13
13
|
const { dispatchApi } = useMyApi();
|
|
14
14
|
const { addListener, removeListener } = useContext(SocketContext);
|
|
15
15
|
/**
|
|
16
16
|
* The most immediate of two levels of caching, this ref stores the raw promises for each hit.
|
|
17
17
|
* Rapidly updates, good for uses in-context where parallel updates my be occurring, i.e.
|
|
18
|
-
* when two cards request the same hit that's missing from the store. Used in
|
|
18
|
+
* when two cards request the same hit that's missing from the store. Used in getHit for this reason.
|
|
19
19
|
*/
|
|
20
|
-
const
|
|
20
|
+
const hitRequests = useRef({});
|
|
21
21
|
/**
|
|
22
22
|
* The "Authoritative" store of hits. Changes here will trigger rerenders, and essentially
|
|
23
23
|
* caches the result of the above promises. Slower to update, so used outside of the hitcontext
|
|
24
24
|
* where parallel requests aren't an issue.
|
|
25
25
|
*/
|
|
26
|
-
const [
|
|
26
|
+
const [hits, setHits] = useState({});
|
|
27
27
|
const [selectedHitIds, setSelectedHitIds] = useState([]);
|
|
28
28
|
// The central location where we propagate any changes from hits made via websockets into the repository. We just save every update,
|
|
29
29
|
// instead of caching it across many components inconsistently as before.
|
|
@@ -31,8 +31,8 @@ const RecordProvider = ({ children }) => {
|
|
|
31
31
|
if (data.hit) {
|
|
32
32
|
// eslint-disable-next-line no-console
|
|
33
33
|
console.debug('Received websocket update for hit', data.hit.howler.id);
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
hitRequests.current[data.hit.howler.id] = Promise.resolve(data.hit);
|
|
35
|
+
setHits(_hits => ({
|
|
36
36
|
..._hits,
|
|
37
37
|
[data.hit.howler.id]: {
|
|
38
38
|
..._hits[data.hit.howler.id],
|
|
@@ -46,68 +46,68 @@ const RecordProvider = ({ children }) => {
|
|
|
46
46
|
return () => removeListener('hits');
|
|
47
47
|
}, [addListener, handler, removeListener]);
|
|
48
48
|
/**
|
|
49
|
-
* A method to retrieve a
|
|
49
|
+
* A method to retrieve a hit from the context. It first checks the hit state,
|
|
50
50
|
* then checks for ongoing requests, then finally executes a new request if necessary.
|
|
51
51
|
*/
|
|
52
|
-
const
|
|
53
|
-
if (!
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
52
|
+
const getHit = useCallback(async (id, force = false) => {
|
|
53
|
+
if (!hitRequests.current[id] || force) {
|
|
54
|
+
hitRequests.current[id] = dispatchApi(api.hit.get(id, ['template', 'dossiers', 'analytic', 'overview']));
|
|
55
|
+
const newHit = await hitRequests.current[id];
|
|
56
|
+
setHits(_hits => ({ ..._hits, [id]: newHit }));
|
|
57
57
|
}
|
|
58
|
-
return
|
|
58
|
+
return hitRequests.current[id];
|
|
59
59
|
}, [dispatchApi]);
|
|
60
60
|
/**
|
|
61
61
|
* Update a hit in the context locally
|
|
62
62
|
*/
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const updateHit = useCallback((newHit) => {
|
|
64
|
+
hitRequests.current[newHit.howler.id] = Promise.resolve(newHit);
|
|
65
|
+
setHits(_hits => ({ ..._hits, [newHit.howler.id]: newHit }));
|
|
66
66
|
}, []);
|
|
67
67
|
/**
|
|
68
68
|
* Add a large number of hits to the cache. Used for results of searches.
|
|
69
69
|
*/
|
|
70
|
-
const
|
|
70
|
+
const loadHits = useCallback((newHits) => {
|
|
71
71
|
const mappedHits = newHits.map(hit => [hit.howler.id, hit]);
|
|
72
72
|
mappedHits.forEach(([id, hit]) => {
|
|
73
|
-
|
|
73
|
+
hitRequests.current[id] = Promise.resolve(hit);
|
|
74
74
|
});
|
|
75
|
-
|
|
75
|
+
setHits(_hits => ({ ..._hits, ...Object.fromEntries(mappedHits) }));
|
|
76
76
|
}, []);
|
|
77
|
-
const
|
|
77
|
+
const addHitToSelection = useCallback((id) => {
|
|
78
78
|
setSelectedHitIds(_selected => uniq([..._selected, id]));
|
|
79
79
|
}, []);
|
|
80
|
-
const
|
|
80
|
+
const removeHitFromSelection = useCallback((id) => {
|
|
81
81
|
setSelectedHitIds(_selected => _selected.filter(_id => _id !== id));
|
|
82
82
|
}, []);
|
|
83
|
-
const
|
|
83
|
+
const clearSelectedHits = useCallback((except) => {
|
|
84
84
|
setSelectedHitIds(!!except ? [except] : []);
|
|
85
85
|
}, []);
|
|
86
|
-
const
|
|
86
|
+
const selectedHits = useMemo(() => selectedHitIds.map(id => hits[id]).filter(hit => !!hit), [hits, selectedHitIds]);
|
|
87
87
|
useEffect(() => {
|
|
88
88
|
selectedHitIds.forEach(id => {
|
|
89
|
-
if (!
|
|
90
|
-
|
|
89
|
+
if (!hitRequests.current[id]) {
|
|
90
|
+
getHit(id);
|
|
91
91
|
}
|
|
92
92
|
});
|
|
93
|
-
}, [
|
|
93
|
+
}, [getHit, selectedHitIds]);
|
|
94
94
|
useEffect(() => {
|
|
95
|
-
Object.entries(
|
|
96
|
-
|
|
95
|
+
Object.entries(hits).forEach(([id, hit]) => {
|
|
96
|
+
hitRequests.current[id] = Promise.resolve(hit);
|
|
97
97
|
});
|
|
98
|
-
}, [
|
|
99
|
-
return (_jsx(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
}, [hits]);
|
|
99
|
+
return (_jsx(HitContext.Provider, { value: {
|
|
100
|
+
hits,
|
|
101
|
+
getHit,
|
|
102
|
+
updateHit,
|
|
103
|
+
selectedHits,
|
|
104
|
+
addHitToSelection,
|
|
105
|
+
removeHitFromSelection,
|
|
106
|
+
clearSelectedHits,
|
|
107
|
+
loadHits
|
|
108
108
|
}, children: children }));
|
|
109
109
|
};
|
|
110
110
|
export const useHitContextSelector = (selector) => {
|
|
111
|
-
return useContextSelector(
|
|
111
|
+
return useContextSelector(HitContext, selector);
|
|
112
112
|
};
|
|
113
|
-
export default
|
|
113
|
+
export default HitProvider;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { HowlerSearchResponse } from '@cccsaurora/howler-ui/api/search';
|
|
2
2
|
import { useMyLocalStorageItem } from '@cccsaurora/howler-ui/components/hooks/useMyLocalStorage';
|
|
3
3
|
import type { Hit } from '@cccsaurora/howler-ui/models/entities/generated/Hit';
|
|
4
|
-
import type { Observable } from '@cccsaurora/howler-ui/models/entities/generated/Observable';
|
|
5
4
|
import type { WithMetadata } from '@cccsaurora/howler-ui/models/WithMetadata';
|
|
6
5
|
import { type Dispatch, type FC, type PropsWithChildren, type SetStateAction } from 'react';
|
|
7
6
|
export interface QueryEntry {
|
|
8
7
|
[query: string]: string;
|
|
9
8
|
}
|
|
10
|
-
export interface
|
|
9
|
+
export interface HitSearchContextType {
|
|
11
10
|
displayType: 'list' | 'grid';
|
|
12
11
|
searching: boolean;
|
|
13
12
|
error: string | null;
|
|
14
|
-
response: HowlerSearchResponse<WithMetadata<Hit
|
|
13
|
+
response: HowlerSearchResponse<WithMetadata<Hit>> | null;
|
|
14
|
+
bundleId: string | null;
|
|
15
15
|
fzfSearch: boolean;
|
|
16
16
|
setDisplayType: (type: 'list' | 'grid') => void;
|
|
17
17
|
setFzfSearch: Dispatch<SetStateAction<boolean>>;
|
|
@@ -20,6 +20,6 @@ export interface RecordSearchContextType {
|
|
|
20
20
|
queryHistory: QueryEntry;
|
|
21
21
|
setQueryHistory: ReturnType<typeof useMyLocalStorageItem>[1];
|
|
22
22
|
}
|
|
23
|
-
export declare const
|
|
24
|
-
declare const
|
|
25
|
-
export default
|
|
23
|
+
export declare const HitSearchContext: import("use-context-selector").Context<HitSearchContextType>;
|
|
24
|
+
declare const HitSearchProvider: FC<PropsWithChildren>;
|
|
25
|
+
export default HitSearchProvider;
|