@orchestrator-ui/orchestrator-ui-components 5.9.0 → 6.1.0
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-lint.log +10 -1
- package/.turbo/turbo-test.log +10 -10
- package/CHANGELOG.md +12 -0
- package/__mocks__/@copilotkit/react-core.js +9 -0
- package/__mocks__/@copilotkit/react-ui.js +11 -0
- package/dist/index.d.ts +1432 -2
- package/dist/index.js +3006 -28
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/src/components/WfoAgent/FilterDisplay/FilterDisplay.tsx +182 -0
- package/src/components/WfoAgent/FilterDisplay/index.ts +1 -0
- package/src/components/WfoAgent/FilterDisplay/styles.ts +62 -0
- package/src/components/WfoAgent/WfoAgent/WfoAgent.tsx +100 -0
- package/src/components/WfoAgent/WfoAgent/index.ts +1 -0
- package/src/components/WfoAgent/index.ts +2 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoConditionRow.tsx +388 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoFieldSelector.tsx +43 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoOperatorSelector.tsx +100 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoPathChips.tsx +193 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoPathSelector.tsx +54 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoRenderFunctions.tsx +107 -0
- package/src/components/WfoSearchPage/WfoConditionRow/WfoSelectedPathDisplay.tsx +75 -0
- package/src/components/WfoSearchPage/WfoConditionRow/index.ts +11 -0
- package/src/components/WfoSearchPage/WfoConditionRow/types.ts +84 -0
- package/src/components/WfoSearchPage/WfoConditionRow/utils.ts +63 -0
- package/src/components/WfoSearchPage/WfoFilterGroup/WfoFilterGroup.tsx +238 -0
- package/src/components/WfoSearchPage/WfoFilterGroup/index.ts +1 -0
- package/src/components/WfoSearchPage/WfoSearch/WfoSearch.tsx +453 -0
- package/src/components/WfoSearchPage/WfoSearch/index.ts +1 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoHighlightedText.tsx +63 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoPathBreadcrumb.tsx +80 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchEmptyState.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchLoadingState.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchMetadataHeader.tsx +24 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchPaginationInfo.tsx +107 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResultItem.tsx +157 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSearchResults.tsx +65 -0
- package/src/components/WfoSearchPage/WfoSearchResults/WfoSubscriptionDetailModal.tsx +55 -0
- package/src/components/WfoSearchPage/WfoSearchResults/index.ts +10 -0
- package/src/components/WfoSearchPage/WfoValueControl/WfoValueControl.tsx +247 -0
- package/src/components/WfoSearchPage/WfoValueControl/index.ts +1 -0
- package/src/components/WfoSearchPage/constants.ts +17 -0
- package/src/components/WfoSearchPage/index.ts +6 -0
- package/src/components/WfoSearchPage/utils.ts +271 -0
- package/src/components/index.ts +2 -0
- package/src/configuration/version.ts +1 -1
- package/src/hooks/useDebounce.ts +21 -0
- package/src/hooks/usePathAutoComplete.ts +133 -0
- package/src/hooks/useSearch.ts +83 -0
- package/src/hooks/useSearchPagination.ts +148 -0
- package/src/hooks/useUrlParams.ts +120 -0
- package/src/icons/WfoPencil.tsx +23 -4
- package/src/messages/en-GB.json +79 -1
- package/src/messages/nl-NL.json +2 -1
- package/src/rtk/endpoints/index.ts +1 -0
- package/src/rtk/endpoints/search.ts +90 -0
- package/src/types/index.ts +1 -0
- package/src/types/search.ts +215 -0
- package/src/utils/optionalArray.spec.ts +27 -0
- package/src/utils/optionalArray.ts +5 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import React, { FC, useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
import { css } from '@emotion/react';
|
|
4
|
+
|
|
5
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
6
|
+
|
|
7
|
+
interface WfoHighlightedTextProps {
|
|
8
|
+
text: string;
|
|
9
|
+
highlight_indices: [number, number][];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const WfoHighlightedText: FC<WfoHighlightedTextProps> = ({
|
|
13
|
+
text,
|
|
14
|
+
highlight_indices,
|
|
15
|
+
}) => {
|
|
16
|
+
const { theme } = useOrchestratorTheme();
|
|
17
|
+
|
|
18
|
+
const highlightStyles = css`
|
|
19
|
+
background-color: ${theme.colors.warning};
|
|
20
|
+
color: ${theme.colors.plainDark};
|
|
21
|
+
padding: 0 ${theme.size.xs};
|
|
22
|
+
font-family: ${theme.font.family};
|
|
23
|
+
font-weight: ${theme.font.weight.bold};
|
|
24
|
+
border-radius: ${theme.size.xs};
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const highlightedParts = useMemo(() => {
|
|
28
|
+
if (!highlight_indices || highlight_indices.length === 0) {
|
|
29
|
+
return text;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sorted = [...highlight_indices].sort((a, b) => a[0] - b[0]);
|
|
33
|
+
const parts: React.ReactNode[] = [];
|
|
34
|
+
let lastIndex = 0;
|
|
35
|
+
|
|
36
|
+
sorted.forEach(([start, end], idx) => {
|
|
37
|
+
// Plain text
|
|
38
|
+
if (start > lastIndex) {
|
|
39
|
+
parts.push(
|
|
40
|
+
<span key={`plain-${idx}`}>
|
|
41
|
+
{text.slice(lastIndex, start)}
|
|
42
|
+
</span>,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
// Highlighted text
|
|
46
|
+
parts.push(
|
|
47
|
+
<span key={`hl-${idx}`} css={highlightStyles}>
|
|
48
|
+
{text.slice(start, end)}
|
|
49
|
+
</span>,
|
|
50
|
+
);
|
|
51
|
+
lastIndex = end;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Remaining plain text
|
|
55
|
+
if (lastIndex < text.length) {
|
|
56
|
+
parts.push(<span key="plain-last">{text.slice(lastIndex)}</span>);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return parts;
|
|
60
|
+
}, [text, highlight_indices, highlightStyles]);
|
|
61
|
+
|
|
62
|
+
return <>{highlightedParts}</>;
|
|
63
|
+
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import React, { FC, Fragment } from 'react';
|
|
2
|
+
|
|
3
|
+
import { EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
|
4
|
+
|
|
5
|
+
import { WfoBadge } from '@/components/WfoBadges';
|
|
6
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
7
|
+
|
|
8
|
+
interface WfoPathBreadcrumbProps {
|
|
9
|
+
path: string;
|
|
10
|
+
size?: 's' | 'm';
|
|
11
|
+
maxSegments?: number;
|
|
12
|
+
showArrows?: boolean;
|
|
13
|
+
color?: string;
|
|
14
|
+
stripFirstSegment?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const WfoPathBreadcrumb: FC<WfoPathBreadcrumbProps> = ({
|
|
18
|
+
path,
|
|
19
|
+
size = 'm',
|
|
20
|
+
maxSegments,
|
|
21
|
+
showArrows = true,
|
|
22
|
+
color,
|
|
23
|
+
stripFirstSegment = false,
|
|
24
|
+
}) => {
|
|
25
|
+
const { theme } = useOrchestratorTheme();
|
|
26
|
+
|
|
27
|
+
if (!path) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let segments = path.split('.').filter((segment) => !/^\d+$/.test(segment));
|
|
32
|
+
|
|
33
|
+
// Strip the first segment (the entity type)
|
|
34
|
+
if (stripFirstSegment && segments.length > 1) {
|
|
35
|
+
segments = segments.slice(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const displaySegments =
|
|
39
|
+
maxSegments && segments.length > maxSegments
|
|
40
|
+
? [
|
|
41
|
+
...segments.slice(0, maxSegments - 1),
|
|
42
|
+
'...',
|
|
43
|
+
segments[segments.length - 1],
|
|
44
|
+
]
|
|
45
|
+
: segments;
|
|
46
|
+
|
|
47
|
+
const badgeColor = color || theme.colors.primary;
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<EuiFlexGroup
|
|
51
|
+
gutterSize={size}
|
|
52
|
+
alignItems="center"
|
|
53
|
+
wrap={false}
|
|
54
|
+
responsive={false}
|
|
55
|
+
>
|
|
56
|
+
{displaySegments.map((segment, index) => (
|
|
57
|
+
<Fragment key={index}>
|
|
58
|
+
<EuiFlexItem grow={false}>
|
|
59
|
+
<WfoBadge
|
|
60
|
+
color={badgeColor}
|
|
61
|
+
textColor={theme.colors.ghost}
|
|
62
|
+
size={size}
|
|
63
|
+
>
|
|
64
|
+
{segment}
|
|
65
|
+
</WfoBadge>
|
|
66
|
+
</EuiFlexItem>
|
|
67
|
+
{showArrows && index < displaySegments.length - 1 && (
|
|
68
|
+
<EuiFlexItem grow={false}>
|
|
69
|
+
<EuiIcon
|
|
70
|
+
type="arrowRight"
|
|
71
|
+
size={size}
|
|
72
|
+
color={theme.colors.link}
|
|
73
|
+
/>
|
|
74
|
+
</EuiFlexItem>
|
|
75
|
+
)}
|
|
76
|
+
</Fragment>
|
|
77
|
+
))}
|
|
78
|
+
</EuiFlexGroup>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
|
|
6
|
+
|
|
7
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
8
|
+
|
|
9
|
+
export const WfoSearchEmptyState: FC = () => {
|
|
10
|
+
const t = useTranslations('search.page');
|
|
11
|
+
const { theme } = useOrchestratorTheme();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<EuiPanel paddingSize="l" color="transparent" hasShadow={false}>
|
|
15
|
+
<EuiFlexGroup justifyContent="center" alignItems="center">
|
|
16
|
+
<EuiFlexItem grow={false}>
|
|
17
|
+
<EuiText size="m" color={theme.colors.textSubdued}>
|
|
18
|
+
{t('noResultsFound')}
|
|
19
|
+
</EuiText>
|
|
20
|
+
</EuiFlexItem>
|
|
21
|
+
</EuiFlexGroup>
|
|
22
|
+
</EuiPanel>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
import { EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
|
|
6
|
+
|
|
7
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
8
|
+
|
|
9
|
+
export const WfoSearchLoadingState: FC = () => {
|
|
10
|
+
const t = useTranslations('search.page');
|
|
11
|
+
const { theme } = useOrchestratorTheme();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<EuiPanel paddingSize="l" color="transparent" hasShadow={false}>
|
|
15
|
+
<EuiFlexGroup justifyContent="center" alignItems="center">
|
|
16
|
+
<EuiFlexItem grow={false}>
|
|
17
|
+
<EuiText size="m" color={theme.colors.textSubdued}>
|
|
18
|
+
{t('loadingSearchResults')}
|
|
19
|
+
</EuiText>
|
|
20
|
+
</EuiFlexItem>
|
|
21
|
+
</EuiFlexGroup>
|
|
22
|
+
</EuiPanel>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { WfoBadge, WfoToolTip } from '@/components';
|
|
4
|
+
|
|
5
|
+
interface WfoSearchMetadataHeaderProps {
|
|
6
|
+
search_metadata: {
|
|
7
|
+
search_type: string | null;
|
|
8
|
+
description: string | null;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const WfoSearchMetadataHeader: FC<WfoSearchMetadataHeaderProps> = ({
|
|
13
|
+
search_metadata,
|
|
14
|
+
}) => {
|
|
15
|
+
if (!search_metadata.search_type) return null;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<WfoToolTip tooltipContent={search_metadata.description || ''}>
|
|
19
|
+
<WfoBadge color="hollow" iconType="search" textColor="default">
|
|
20
|
+
{search_metadata.search_type}
|
|
21
|
+
</WfoBadge>
|
|
22
|
+
</WfoToolTip>
|
|
23
|
+
);
|
|
24
|
+
};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
EuiButtonIcon,
|
|
7
|
+
EuiFlexGroup,
|
|
8
|
+
EuiFlexItem,
|
|
9
|
+
EuiText,
|
|
10
|
+
} from '@elastic/eui';
|
|
11
|
+
|
|
12
|
+
import { WfoBadge } from '@/components/WfoBadges';
|
|
13
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
14
|
+
|
|
15
|
+
interface WfoSearchPaginationInfoProps {
|
|
16
|
+
has_next_page: boolean;
|
|
17
|
+
next_page_cursor: number | null;
|
|
18
|
+
onNextPage?: (nextPageCursor: number) => void;
|
|
19
|
+
onPrevPage?: () => void;
|
|
20
|
+
isLoading?: boolean;
|
|
21
|
+
currentPage?: number;
|
|
22
|
+
hasPrevPage?: boolean;
|
|
23
|
+
resultCount?: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const WfoSearchPaginationInfo: FC<WfoSearchPaginationInfoProps> = ({
|
|
27
|
+
has_next_page,
|
|
28
|
+
next_page_cursor,
|
|
29
|
+
onNextPage,
|
|
30
|
+
onPrevPage,
|
|
31
|
+
isLoading = false,
|
|
32
|
+
currentPage = 1,
|
|
33
|
+
hasPrevPage = false,
|
|
34
|
+
resultCount,
|
|
35
|
+
}) => {
|
|
36
|
+
const t = useTranslations('search.page');
|
|
37
|
+
const { theme } = useOrchestratorTheme();
|
|
38
|
+
|
|
39
|
+
const handleNextPage = () => {
|
|
40
|
+
if (!isLoading && next_page_cursor && onNextPage)
|
|
41
|
+
onNextPage(next_page_cursor);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const handlePrevPage = () => {
|
|
45
|
+
if (!isLoading && onPrevPage) onPrevPage();
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (!has_next_page && !hasPrevPage) return null;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<EuiFlexGroup
|
|
52
|
+
justifyContent="flexEnd"
|
|
53
|
+
alignItems="center"
|
|
54
|
+
gutterSize="xs"
|
|
55
|
+
responsive={false}
|
|
56
|
+
style={{
|
|
57
|
+
whiteSpace: 'nowrap',
|
|
58
|
+
padding: 0,
|
|
59
|
+
background: 'transparent',
|
|
60
|
+
border: 'none',
|
|
61
|
+
}}
|
|
62
|
+
role="navigation"
|
|
63
|
+
aria-label={t('searchResultsPagination')}
|
|
64
|
+
>
|
|
65
|
+
<EuiFlexItem grow={false}>
|
|
66
|
+
<EuiButtonIcon
|
|
67
|
+
iconType="arrowLeft"
|
|
68
|
+
aria-label={t('previousPage')}
|
|
69
|
+
onClick={handlePrevPage}
|
|
70
|
+
disabled={!hasPrevPage || isLoading}
|
|
71
|
+
color="text"
|
|
72
|
+
size="s"
|
|
73
|
+
/>
|
|
74
|
+
</EuiFlexItem>
|
|
75
|
+
|
|
76
|
+
<EuiFlexItem grow={false}>
|
|
77
|
+
<EuiText size="xs" color={theme.colors.textSubdued}>
|
|
78
|
+
{t('page')} {currentPage}
|
|
79
|
+
</EuiText>
|
|
80
|
+
</EuiFlexItem>
|
|
81
|
+
|
|
82
|
+
<EuiFlexItem grow={false}>
|
|
83
|
+
<EuiButtonIcon
|
|
84
|
+
iconType="arrowRight"
|
|
85
|
+
aria-label={t('nextPage')}
|
|
86
|
+
onClick={handleNextPage}
|
|
87
|
+
disabled={!has_next_page || isLoading}
|
|
88
|
+
color="text"
|
|
89
|
+
size="s"
|
|
90
|
+
isLoading={isLoading}
|
|
91
|
+
/>
|
|
92
|
+
</EuiFlexItem>
|
|
93
|
+
|
|
94
|
+
{resultCount && resultCount > 0 && (
|
|
95
|
+
<EuiFlexItem grow={false}>
|
|
96
|
+
<WfoBadge
|
|
97
|
+
className="wfoPagination__badge"
|
|
98
|
+
color="hollow"
|
|
99
|
+
textColor="default"
|
|
100
|
+
>
|
|
101
|
+
{t('resultsOnPage', { resultCount })}
|
|
102
|
+
</WfoBadge>
|
|
103
|
+
</EuiFlexItem>
|
|
104
|
+
)}
|
|
105
|
+
</EuiFlexGroup>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import React, { FC, MouseEvent, useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
EuiButtonIcon,
|
|
7
|
+
EuiFlexGroup,
|
|
8
|
+
EuiFlexItem,
|
|
9
|
+
EuiPanel,
|
|
10
|
+
EuiSpacer,
|
|
11
|
+
EuiText,
|
|
12
|
+
} from '@elastic/eui';
|
|
13
|
+
|
|
14
|
+
import { WfoBadge } from '@/components/WfoBadges';
|
|
15
|
+
import { useOrchestratorTheme } from '@/hooks';
|
|
16
|
+
import { AnySearchResult } from '@/types';
|
|
17
|
+
|
|
18
|
+
import { getDescription, getDetailUrl } from '../utils';
|
|
19
|
+
import { WfoHighlightedText } from './WfoHighlightedText';
|
|
20
|
+
import { WfoPathBreadcrumb } from './WfoPathBreadcrumb';
|
|
21
|
+
|
|
22
|
+
interface WfoSearchResultItemProps {
|
|
23
|
+
result: AnySearchResult;
|
|
24
|
+
index: number;
|
|
25
|
+
isSelected?: boolean;
|
|
26
|
+
onSelect?: () => void;
|
|
27
|
+
onPositionChange?: (index: number, element: HTMLElement | null) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const WfoSearchResultItem: FC<WfoSearchResultItemProps> = ({
|
|
31
|
+
result,
|
|
32
|
+
isSelected = false,
|
|
33
|
+
onSelect,
|
|
34
|
+
onPositionChange,
|
|
35
|
+
index,
|
|
36
|
+
}) => {
|
|
37
|
+
const t = useTranslations('search.page');
|
|
38
|
+
const matchingField = result.matching_field;
|
|
39
|
+
const { theme } = useOrchestratorTheme();
|
|
40
|
+
const baseUrl = `${window.location.protocol}//${window.location.host}`;
|
|
41
|
+
const detailUrl = getDetailUrl(result, baseUrl);
|
|
42
|
+
const itemRef = useRef<HTMLDivElement>(null);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (isSelected && onPositionChange && itemRef.current) {
|
|
46
|
+
onPositionChange(index, itemRef.current);
|
|
47
|
+
}
|
|
48
|
+
}, [isSelected, index, onPositionChange]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<EuiFlexItem>
|
|
52
|
+
<EuiPanel
|
|
53
|
+
panelRef={itemRef}
|
|
54
|
+
color={isSelected ? 'primary' : 'transparent'}
|
|
55
|
+
paddingSize="m"
|
|
56
|
+
data-record-index={index}
|
|
57
|
+
onClick={() => {
|
|
58
|
+
onSelect?.();
|
|
59
|
+
}}
|
|
60
|
+
style={
|
|
61
|
+
isSelected
|
|
62
|
+
? {
|
|
63
|
+
borderTop: theme.border.thick,
|
|
64
|
+
borderBottom: theme.border.thick,
|
|
65
|
+
borderLeft: 'none',
|
|
66
|
+
borderRight: 'none',
|
|
67
|
+
borderColor: theme.colors.primary,
|
|
68
|
+
}
|
|
69
|
+
: undefined
|
|
70
|
+
}
|
|
71
|
+
>
|
|
72
|
+
<EuiFlexGroup alignItems="flexStart" gutterSize="m">
|
|
73
|
+
<EuiFlexItem>
|
|
74
|
+
<EuiFlexGroup direction="column" gutterSize="xs">
|
|
75
|
+
<EuiFlexItem>
|
|
76
|
+
<EuiText
|
|
77
|
+
size="m"
|
|
78
|
+
style={{
|
|
79
|
+
fontWeight: theme.font.weight.semiBold,
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{getDescription(result)}
|
|
83
|
+
</EuiText>
|
|
84
|
+
</EuiFlexItem>
|
|
85
|
+
{matchingField && (
|
|
86
|
+
<>
|
|
87
|
+
{matchingField.path && (
|
|
88
|
+
<EuiFlexItem>
|
|
89
|
+
<WfoPathBreadcrumb
|
|
90
|
+
path={matchingField.path}
|
|
91
|
+
size="s"
|
|
92
|
+
maxSegments={4}
|
|
93
|
+
color={theme.colors.primary}
|
|
94
|
+
/>
|
|
95
|
+
</EuiFlexItem>
|
|
96
|
+
)}
|
|
97
|
+
<EuiFlexItem>
|
|
98
|
+
<EuiText
|
|
99
|
+
style={{
|
|
100
|
+
backgroundColor: 'transparent',
|
|
101
|
+
borderRadius:
|
|
102
|
+
theme.border.radius.medium,
|
|
103
|
+
fontSize: theme.size.base,
|
|
104
|
+
}}
|
|
105
|
+
>
|
|
106
|
+
<WfoHighlightedText
|
|
107
|
+
text={matchingField.text}
|
|
108
|
+
highlight_indices={
|
|
109
|
+
matchingField.highlight_indices
|
|
110
|
+
}
|
|
111
|
+
/>
|
|
112
|
+
</EuiText>
|
|
113
|
+
</EuiFlexItem>
|
|
114
|
+
|
|
115
|
+
<EuiSpacer size="xs" />
|
|
116
|
+
</>
|
|
117
|
+
)}
|
|
118
|
+
</EuiFlexGroup>
|
|
119
|
+
</EuiFlexItem>
|
|
120
|
+
|
|
121
|
+
<EuiFlexItem grow={false}>
|
|
122
|
+
<EuiFlexGroup
|
|
123
|
+
direction="column"
|
|
124
|
+
alignItems="flexEnd"
|
|
125
|
+
gutterSize="xs"
|
|
126
|
+
>
|
|
127
|
+
<EuiFlexItem>
|
|
128
|
+
<WfoBadge
|
|
129
|
+
color={theme.colors.primary}
|
|
130
|
+
textColor={theme.colors.ghost}
|
|
131
|
+
>
|
|
132
|
+
{'score' in result && result.score
|
|
133
|
+
? result.score.toFixed(4)
|
|
134
|
+
: 'N/A'}
|
|
135
|
+
</WfoBadge>
|
|
136
|
+
</EuiFlexItem>
|
|
137
|
+
<EuiFlexItem>
|
|
138
|
+
<EuiButtonIcon
|
|
139
|
+
iconType="popout"
|
|
140
|
+
aria-label={t('viewDetails')}
|
|
141
|
+
onClick={(
|
|
142
|
+
e: MouseEvent<HTMLButtonElement>,
|
|
143
|
+
) => {
|
|
144
|
+
e.stopPropagation();
|
|
145
|
+
window.open(detailUrl, '_blank');
|
|
146
|
+
}}
|
|
147
|
+
color="text"
|
|
148
|
+
size="m"
|
|
149
|
+
/>
|
|
150
|
+
</EuiFlexItem>
|
|
151
|
+
</EuiFlexGroup>
|
|
152
|
+
</EuiFlexItem>
|
|
153
|
+
</EuiFlexGroup>
|
|
154
|
+
</EuiPanel>
|
|
155
|
+
</EuiFlexItem>
|
|
156
|
+
);
|
|
157
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { EuiFlexGroup, EuiPanel } from '@elastic/eui';
|
|
4
|
+
|
|
5
|
+
import { AnySearchResult } from '@/types';
|
|
6
|
+
|
|
7
|
+
import { WfoSearchEmptyState } from './WfoSearchEmptyState';
|
|
8
|
+
import { WfoSearchLoadingState } from './WfoSearchLoadingState';
|
|
9
|
+
import { WfoSearchResultItem } from './WfoSearchResultItem';
|
|
10
|
+
import { WfoSubscriptionDetailModal } from './WfoSubscriptionDetailModal';
|
|
11
|
+
|
|
12
|
+
interface WfoSearchResultsProps {
|
|
13
|
+
results: AnySearchResult[];
|
|
14
|
+
loading: boolean;
|
|
15
|
+
selectedRecordIndex?: number;
|
|
16
|
+
onRecordSelect?: (index: number) => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export const WfoSearchResults = ({
|
|
20
|
+
results,
|
|
21
|
+
loading,
|
|
22
|
+
selectedRecordIndex = 0,
|
|
23
|
+
onRecordSelect,
|
|
24
|
+
}: WfoSearchResultsProps) => {
|
|
25
|
+
const [modalData, setModalData] = useState<{
|
|
26
|
+
subscription: unknown;
|
|
27
|
+
matchingField?: unknown;
|
|
28
|
+
} | null>(null);
|
|
29
|
+
|
|
30
|
+
const handleCloseModal = () => {
|
|
31
|
+
setModalData(null);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
if (loading) {
|
|
35
|
+
return <WfoSearchLoadingState />;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!results || results.length === 0) {
|
|
39
|
+
return <WfoSearchEmptyState />;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<>
|
|
44
|
+
<EuiPanel paddingSize="m" hasShadow={false}>
|
|
45
|
+
<EuiFlexGroup direction="column" gutterSize="s">
|
|
46
|
+
{results.map((result, idx) => (
|
|
47
|
+
<WfoSearchResultItem
|
|
48
|
+
key={idx}
|
|
49
|
+
result={result}
|
|
50
|
+
index={idx}
|
|
51
|
+
isSelected={idx === selectedRecordIndex}
|
|
52
|
+
onSelect={() => onRecordSelect?.(idx)}
|
|
53
|
+
/>
|
|
54
|
+
))}
|
|
55
|
+
</EuiFlexGroup>
|
|
56
|
+
</EuiPanel>
|
|
57
|
+
<WfoSubscriptionDetailModal
|
|
58
|
+
isVisible={!!modalData}
|
|
59
|
+
onClose={handleCloseModal}
|
|
60
|
+
subscriptionData={modalData?.subscription}
|
|
61
|
+
matchingField={modalData?.matchingField}
|
|
62
|
+
/>
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { FC } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useTranslations } from 'next-intl';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
EuiButton,
|
|
7
|
+
EuiModal,
|
|
8
|
+
EuiModalBody,
|
|
9
|
+
EuiModalFooter,
|
|
10
|
+
EuiModalHeader,
|
|
11
|
+
EuiModalHeaderTitle,
|
|
12
|
+
} from '@elastic/eui';
|
|
13
|
+
|
|
14
|
+
import { WfoSubscription } from '@/components';
|
|
15
|
+
import { TreeProvider } from '@/contexts';
|
|
16
|
+
|
|
17
|
+
interface WfoSubscriptionDetailModalProps {
|
|
18
|
+
isVisible: boolean;
|
|
19
|
+
onClose: () => void;
|
|
20
|
+
subscriptionData: unknown | null;
|
|
21
|
+
matchingField?: unknown;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const WfoSubscriptionDetailModal: FC<
|
|
25
|
+
WfoSubscriptionDetailModalProps
|
|
26
|
+
> = ({ isVisible, onClose, subscriptionData }) => {
|
|
27
|
+
const t = useTranslations('search.page');
|
|
28
|
+
if (!isVisible || !subscriptionData) return null;
|
|
29
|
+
|
|
30
|
+
const subscriptionId =
|
|
31
|
+
subscriptionData &&
|
|
32
|
+
(subscriptionData as { subscription_id: string }).subscription_id;
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<EuiModal onClose={onClose} maxWidth={800}>
|
|
36
|
+
<EuiModalHeader>
|
|
37
|
+
<EuiModalHeaderTitle>
|
|
38
|
+
{t('subscriptionDetails')}
|
|
39
|
+
</EuiModalHeaderTitle>
|
|
40
|
+
</EuiModalHeader>
|
|
41
|
+
|
|
42
|
+
<EuiModalBody>
|
|
43
|
+
<TreeProvider>
|
|
44
|
+
<WfoSubscription subscriptionId={subscriptionId} />
|
|
45
|
+
</TreeProvider>
|
|
46
|
+
</EuiModalBody>
|
|
47
|
+
|
|
48
|
+
<EuiModalFooter>
|
|
49
|
+
<EuiButton onClick={onClose} fill>
|
|
50
|
+
{t('closeButton')}
|
|
51
|
+
</EuiButton>
|
|
52
|
+
</EuiModalFooter>
|
|
53
|
+
</EuiModal>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './WfoSearchResults';
|
|
2
|
+
|
|
3
|
+
export * from './WfoSearchResultItem';
|
|
4
|
+
export * from './WfoSearchEmptyState';
|
|
5
|
+
export * from './WfoSearchLoadingState';
|
|
6
|
+
export * from './WfoSearchMetadataHeader';
|
|
7
|
+
export * from './WfoSearchPaginationInfo';
|
|
8
|
+
export * from './WfoHighlightedText';
|
|
9
|
+
export * from './WfoPathBreadcrumb';
|
|
10
|
+
export * from './WfoSubscriptionDetailModal';
|