Mirror of https://github.com/roostorg/osprey
github.com/roostorg/osprey
1import * as React from 'react';
2import { Empty } from 'antd';
3import NewWindow from 'react-new-window';
4import { useParams } from 'react-router-dom';
5
6import useApplicationConfigStore from '../../stores/ApplicationConfigStore';
7import { EntityViewParams } from '../../stores/EntityStore';
8import { OspreyEvent } from '../../types/QueryTypes';
9import OspreyButton from '../../uikit/OspreyButton';
10import Text, { TextColors } from '../../uikit/Text';
11import { formatUtcTimestamp, localizeAndFormatTimestamp } from '../../utils/DateUtils';
12import { wrapEntityKeysWithFeatureLocationsMenu, wrapEntityValuesWithLabelMenu } from '../../utils/EntityUtils';
13import CopyLinkButton from '../common/CopyLinkButton';
14import Feature from '../common/Feature';
15import PropertyTable, { Entries } from '../common/PropertyTable';
16
17import styles from './EventStreamCard.module.css';
18import { FeatureLocation } from '../../types/ConfigTypes';
19import Tooltip, { TooltipSizes } from '../../uikit/Tooltip';
20
21interface EventStreamCardProps {
22 eventDetails: OspreyEvent;
23 selectedFeatures: Array<readonly string[]>;
24 featureLocations: Array<FeatureLocation> | undefined;
25 isListView: boolean;
26}
27
28const EventStreamCard = ({ eventDetails, selectedFeatures, featureLocations, isListView }: EventStreamCardProps) => {
29 const [showWindow, setShowWindow] = React.useState(false);
30 const { entityId, entityType } = useParams<EntityViewParams>();
31 const decodedEntityId = entityId != null ? decodeURIComponent(entityId) : null;
32 const decodedEntityType = entityType != null ? decodeURIComponent(entityType) : null;
33 const featureNameToEntityTypeMapping = useApplicationConfigStore((state) => state.featureNameToEntityTypeMapping);
34
35 const handleShowWindow = () => {
36 setShowWindow(true);
37 };
38
39 const handleWindowClose = () => {
40 setShowWindow(false);
41 };
42
43 const renderDescriptionBlock = (features: readonly string[]) => {
44 const entries: Record<string, string> = {};
45
46 features.forEach((feature) => {
47 if (Object.prototype.hasOwnProperty.call(eventDetails.extracted_features, feature)) {
48 entries[feature] = eventDetails.extracted_features[feature];
49 }
50 });
51
52 if (Object.keys(entries).length === 0) return null;
53
54 return entries;
55 };
56
57 const renderContent = () => {
58 if (isListView) return null;
59
60 const features = selectedFeatures.map(renderDescriptionBlock).filter((block) => block != null) as Entries[];
61 const isEmpty = features.length === 0;
62 let entityViewProps = {};
63
64 if (decodedEntityId != null && decodedEntityType != null) {
65 entityViewProps = {
66 shouldHighlightCell: ([key, value]: [string, string]) => {
67 if (value == null) return false;
68 const featureEntityType = featureNameToEntityTypeMapping.get(key);
69 return decodedEntityType === featureEntityType && decodedEntityId.toString() === value.toString();
70 },
71 };
72 }
73
74 return isEmpty ? (
75 <Empty imageStyle={{ height: 40 }} description="No data for selected features" />
76 ) : (
77 <PropertyTable
78 className={styles.contentTable}
79 entries={features}
80 columns={3}
81 renderKey={([key]) => wrapEntityKeysWithFeatureLocationsMenu(key, featureLocations)}
82 renderValue={([key, value]) => wrapEntityValuesWithLabelMenu(value, key)}
83 {...entityViewProps}
84 />
85 );
86 };
87
88 const eventUrl = `${document.location.origin}/events/${eventDetails.id}`;
89
90 const cardTitle = (
91 <div className={styles.cardTitle}>
92 <Feature
93 className={styles.actionName}
94 featureName="ActionName"
95 value={eventDetails.extracted_features.ActionName}
96 />
97 <Text tag="span" color={TextColors.LIGHT_SECONDARY} className={styles.timestamp}>
98 {localizeAndFormatTimestamp(eventDetails.timestamp)}
99 </Text>
100 <Tooltip
101 content={
102 <Text tag="span" color={TextColors.LIGHT_SECONDARY} className={styles.timestamp}>
103 {formatUtcTimestamp(eventDetails.timestamp)}
104 </Text>
105 }
106 size={TooltipSizes.SMALL}
107 >
108 <span>ⓘ</span>
109 </Tooltip>
110 <CopyLinkButton link={eventUrl} />
111 </div>
112 );
113
114 return (
115 <div className={styles.card}>
116 <div className={styles.cardHeader}>
117 {cardTitle}
118 <OspreyButton onClick={handleShowWindow}>
119 See Details
120 {showWindow && (
121 <NewWindow url={eventUrl} onUnload={handleWindowClose} features={{ width: 800, height: 800 }} />
122 )}
123 </OspreyButton>
124 </div>
125 <div>{renderContent()}</div>
126 </div>
127 );
128};
129
130export default EventStreamCard;