diff --git a/src/actions.ts b/src/actions.ts index 41663fd92..c3bfe626f 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -18,5 +18,7 @@ export const actions = addPrefix(ACTION_PREFIX, { OPEN_TROUBLESHOOTING_GUIDE: "OPEN_TROUBLESHOOTING_GUIDE", OPEN_DOCUMENTATION: "OPEN_DOCUMENTATION", SET_DIGMA_API_URL: "SET_DIGMA_API_URL", - SET_USER_EMAIL: "SET_USER_EMAIL" + SET_USER_EMAIL: "SET_USER_EMAIL", + SET_IS_OBSERVABILITY_ENABLED: "SET_IS_OBSERVABILITY_ENABLED", + SET_OBSERVABILITY: "SET_OBSERVABILITY" }); diff --git a/src/components/RecentActivity/ObservabilityStatusBadge/index.tsx b/src/components/RecentActivity/ObservabilityStatusBadge/index.tsx new file mode 100644 index 000000000..a7019f87b --- /dev/null +++ b/src/components/RecentActivity/ObservabilityStatusBadge/index.tsx @@ -0,0 +1,32 @@ +import { actions as globalActions } from "../../../actions"; +import { WarningTriangleIcon } from "../../common/icons/WarningTriangleIcon"; +import * as s from "./styles"; + +export const ObservabilityStatusBadge = () => { + const handleTurnOnLinkClick = () => { + window.sendMessageToDigma({ + action: globalActions.SET_OBSERVABILITY, + payload: { + isObservabilityEnabled: true + } + }); + }; + + return ( + + + + + + + Observability turned off + + + Turn observability on in order for Digma to collect new data + + + + Turn on + + ); +}; diff --git a/src/components/RecentActivity/ObservabilityStatusBadge/styles.ts b/src/components/RecentActivity/ObservabilityStatusBadge/styles.ts new file mode 100644 index 000000000..0f94f5ef4 --- /dev/null +++ b/src/components/RecentActivity/ObservabilityStatusBadge/styles.ts @@ -0,0 +1,73 @@ +import styled from "styled-components"; +import { Link } from "../../common/Link"; + +export const Container = styled.div` + border-radius: 8px; + border: 1px solid rgba(255 129 13 / 50%); + background: rgba(255 129 13 / 10%); + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px; +`; + +export const MessageContainer = styled.div` + display: flex; + gap: 4px; +`; + +export const Message = styled.div` + display: flex; + gap: 8px; + font-size: 14px; +`; + +export const Title = styled.span` + font-weight: 600; + text-transform: capitalize; + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#494b57"; + case "dark": + case "dark-jetbrains": + return "#dfe1e5"; + } + }}; +`; + +export const Divider = styled.div` + height: 100%; + width: 1px; + background: currentcolor; +`; + +export const Description = styled.span` + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#818594"; + case "dark": + case "dark-jetbrains": + return "#b4b8bf"; + } + }}; +`; + +export const IconContainer = styled.div` + display: flex; + align-self: center; + color: ${({ theme }) => { + switch (theme.mode) { + case "light": + return "#e06c00"; + case "dark": + case "dark-jetbrains": + return "#ff810d"; + } + }}; +`; + +export const TurnOnLink = styled(Link)` + padding: 4px; +`; diff --git a/src/components/RecentActivity/RecentActivityTable/index.tsx b/src/components/RecentActivity/RecentActivityTable/index.tsx index ca57ca0e9..38271e55b 100644 --- a/src/components/RecentActivity/RecentActivityTable/index.tsx +++ b/src/components/RecentActivity/RecentActivityTable/index.tsx @@ -147,7 +147,7 @@ export const RecentActivityTable = (props: RecentActivityTableProps) => { const columns = [ columnHelper.accessor((row) => row, { id: "recentActivity", - header: "Recent Activity", + header: "", cell: (info) => { const entry = info.getValue(); return ( @@ -203,7 +203,7 @@ export const RecentActivityTable = (props: RecentActivityTableProps) => { return props.viewMode === "table" ? ( - + {table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => ( @@ -233,7 +233,6 @@ export const RecentActivityTable = (props: RecentActivityTableProps) => { ) : ( - Recent Activity {sortedData.map((entry, i) => ( diff --git a/src/components/RecentActivity/RecentActivityTable/styles.ts b/src/components/RecentActivity/RecentActivityTable/styles.ts index 38bdb1fe4..06464bc8e 100644 --- a/src/components/RecentActivity/RecentActivityTable/styles.ts +++ b/src/components/RecentActivity/RecentActivityTable/styles.ts @@ -1,7 +1,6 @@ import styled from "styled-components"; import { getCodeFont } from "../../common/App/styles"; import { Link } from "../../common/Link"; -import { HEADER_HEIGHT } from "../styles"; export const TABLE_BORDER_SPACING = 4; // in pixels; @@ -21,9 +20,9 @@ export const Table = styled.table` }}; `; -export const TableHead = styled.thead` +export const TableHead = styled.thead<{ offset: number }>` position: sticky; - top: ${HEADER_HEIGHT + TABLE_BORDER_SPACING}px; + top: ${({ offset }) => offset + TABLE_BORDER_SPACING - 1}px; z-index: 1; font-size: 14px; height: 28px; @@ -190,45 +189,6 @@ export const ListContainer = styled.div` flex-direction: column; `; -export const ListHeader = styled.div` - position: sticky; - top: ${HEADER_HEIGHT}px; - z-index: 1; - padding: 12px 0 8px 12px; - box-shadow: -12px 0 0 - ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#f7f8fa"; - case "dark": - return "#0f0f0f"; - case "dark-jetbrains": - return "#2b2d30"; - } - }}; - font-size: 14px; - color: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#818594"; - case "dark": - return "#b9c2eb"; - case "dark-jetbrains": - return "#b4b8bf"; - } - }}; - background: ${({ theme }) => { - switch (theme.mode) { - case "light": - return "#f7f8fa"; - case "dark": - return "#0f0f0f"; - case "dark-jetbrains": - return "#2b2d30"; - } - }}; -`; - export const List = styled.ul` border-radius: 12px; margin: 0; diff --git a/src/components/RecentActivity/RecentActivityTable/types.ts b/src/components/RecentActivity/RecentActivityTable/types.ts index 5038ff583..d4c636f5f 100644 --- a/src/components/RecentActivity/RecentActivityTable/types.ts +++ b/src/components/RecentActivity/RecentActivityTable/types.ts @@ -7,4 +7,5 @@ export interface RecentActivityTableProps { onTraceButtonClick: (traceId: string, span: EntrySpan) => void; viewMode: ViewMode; isTraceButtonVisible: boolean; + headerHeight: number; } diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index cd9ab696d..aa7b252ae 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -1,6 +1,7 @@ import { Allotment } from "allotment"; import "allotment/dist/style.css"; import { KeyboardEvent, useContext, useEffect, useMemo, useState } from "react"; +import useDimensions from "react-cool-dimensions"; import { actions as globalActions } from "../../actions"; import { dispatcher } from "../../dispatcher"; import { usePrevious } from "../../hooks/usePrevious"; @@ -17,6 +18,7 @@ import { ViewMode } from "./EnvironmentPanel/types"; import { EnvironmentTypePanel } from "./EnvironmentTypePanel"; import { LiveView } from "./LiveView"; import { LiveData } from "./LiveView/types"; +import { ObservabilityStatusBadge } from "./ObservabilityStatusBadge"; import { RecentActivityTable, isRecent } from "./RecentActivityTable"; import { RegistrationPanel } from "./RegistrationPanel"; import { SetupOrgDigmaPanel } from "./SetupOrgDigmaPanel"; @@ -74,6 +76,7 @@ export const RecentActivity = (props: RecentActivityProps) => { useState(false); const config = useContext(ConfigContext); const previousUserEmail = usePrevious(config.userEmail); + const { observe, entry } = useDimensions(); const environmentActivities = useMemo( () => (data ? groupBy(data.entries, (x) => x.environment) : {}), @@ -367,16 +370,11 @@ export const RecentActivity = (props: RecentActivityProps) => { !environmentActivities[selectedEnvironment.originalName] || !environmentActivities[selectedEnvironment.originalName].length ) { - return ( - <> - - Recent Activity - - {renderNoData()} - - ); + return <>{renderNoData()}; } + const headerHeight = entry?.target.clientHeight || 0; + return ( { onSpanLinkClick={handleSpanLinkClick} onTraceButtonClick={handleTraceButtonClick} isTraceButtonVisible={config.isJaegerEnabled} + headerHeight={headerHeight} /> ); }; @@ -392,7 +391,7 @@ export const RecentActivity = (props: RecentActivityProps) => { - + { onEnvironmentAdd={handleEnvironmentAdd} onEnvironmentDelete={handleEnvironmentDelete} /> + {!selectedEnvironment?.isPending && ( + <> + + Recent Activity + + + )} + {!config.isObservabilityEnabled && } {renderContent()} diff --git a/src/components/RecentActivity/styles.ts b/src/components/RecentActivity/styles.ts index f6ec30534..45e856c50 100644 --- a/src/components/RecentActivity/styles.ts +++ b/src/components/RecentActivity/styles.ts @@ -1,8 +1,6 @@ import styled from "styled-components"; import { Button } from "../common/Button"; -export const HEADER_HEIGHT = 64; // in pixels; - export const Container = styled.div` height: 100%; position: relative; @@ -40,12 +38,15 @@ export const Container = styled.div` }}; } `; +export const RecentActivityContainer = styled.div` + height: 100%; + overflow: auto; + box-sizing: border-box; +`; export const RecentActivityHeader = styled.div` - height: ${HEADER_HEIGHT}px; box-sizing: border-box; - padding: 12px; - padding-right: 24px; + padding: 12px 12px 8px; z-index: 1; position: sticky; top: 0; @@ -59,35 +60,32 @@ export const RecentActivityHeader = styled.div` return "#2b2d30"; } }}; + display: flex; + flex-direction: column; + gap: 8px; `; -export const RecentActivityContainer = styled.div` - height: 100%; - overflow: auto; - box-sizing: border-box; -`; - -export const RecentActivityContentContainer = styled.div` - padding: 0 24px 12px 12px; -`; - -export const RecentActivityTableTitle = styled.div` - margin: 12px 0 8px; - padding-left: 12px; +export const RecentActivityToolbar = styled.div` + display: flex; + justify-content: space-between; + padding: 8px 0; font-weight: 400; font-size: 14px; color: ${({ theme }) => { switch (theme.mode) { case "light": - return "#818594"; + return "#494b57"; case "dark": - return "#b9c2eb"; case "dark-jetbrains": - return "#b4b8bf"; + return "#dfe1e5"; } }}; `; +export const RecentActivityContentContainer = styled.div` + padding: 0 12px 12px; +`; + export const NoDataContainer = styled.div` display: flex; flex-direction: column; diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx index 34db1cc8b..92de69f82 100644 --- a/src/components/common/App/index.tsx +++ b/src/components/common/App/index.tsx @@ -139,6 +139,15 @@ export const App = (props: AppProps) => { } }; + const handleIsObservabilityEnabled = (data: unknown) => { + if (isObject(data) && isBoolean(data.isObservabilityEnabled)) { + setConfig((config) => ({ + ...config, + isObservabilityEnabled: data.isObservabilityEnabled as boolean + })); + } + }; + dispatcher.addActionListener(actions.SET_THEME, handleSetTheme); dispatcher.addActionListener(actions.SET_MAIN_FONT, handleSetMainFont); dispatcher.addActionListener(actions.SET_CODE_FONT, handleSetCodeFont); @@ -171,6 +180,10 @@ export const App = (props: AppProps) => { handleSetDigmaApiUrl ); dispatcher.addActionListener(actions.SET_USER_EMAIL, handleSetUserEmail); + dispatcher.addActionListener( + actions.SET_IS_OBSERVABILITY_ENABLED, + handleIsObservabilityEnabled + ); return () => { dispatcher.removeActionListener(actions.SET_THEME, handleSetTheme); @@ -208,6 +221,10 @@ export const App = (props: AppProps) => { actions.SET_USER_EMAIL, handleSetUserEmail ); + dispatcher.removeActionListener( + actions.SET_IS_OBSERVABILITY_ENABLED, + handleIsObservabilityEnabled + ); }; }, []); diff --git a/src/components/common/icons/WarningTriangleIcon.tsx b/src/components/common/icons/WarningTriangleIcon.tsx new file mode 100644 index 000000000..3e005259b --- /dev/null +++ b/src/components/common/icons/WarningTriangleIcon.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { useIconProps } from "./hooks"; +import { IconProps } from "./types"; + +const WarningTriangleIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const WarningTriangleIcon = React.memo(WarningTriangleIconComponent);