Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import {
DataFetcherConfiguration,
useFetchData
} from "../../../../../hooks/useFetchData";
import { usePersistence } from "../../../../../hooks/usePersistence";
import { useErrorsSelector } from "../../../../../store/errors/useErrorsSelector";
import { useStore } from "../../../../../store/useStore";
import { isNull } from "../../../../../typeGuards/isNull";
import { isString } from "../../../../../typeGuards/isString";
import { sendUserActionTrackingEvent } from "../../../../../utils/actions/sendUserActionTrackingEvent";
Expand All @@ -27,13 +28,7 @@ import {
import { SpanFrameGroup } from "./SpanFrameGroup";
import { FrameItemCodeLocation } from "./SpanFrameGroup/types";
import * as s from "./styles";
import {
FlowProps as FlowStackProps,
ShowOnlyWorkspaceErrorStackTraceItemsPayload
} from "./types";

const SHOW_ONLY_WORKSPACE_ERROR_STACK_TRACE_ITEMS_PERSISTENCE_KEY =
"showOnlyWorkspaceErrorStackTraceItems";
import { FlowProps as FlowStackProps } from "./types";

const filesURIsDataFetcherConfiguration: DataFetcherConfiguration = {
requestAction: actions.GET_FILES_URIS,
Expand All @@ -42,15 +37,9 @@ const filesURIsDataFetcherConfiguration: DataFetcherConfiguration = {
};

export const FlowStack = ({ data }: FlowStackProps) => {
const [persistedShowWorkspaceItemsOnly, setPersistedShowWorkspaceItemsOnly] =
usePersistence<ShowOnlyWorkspaceErrorStackTraceItemsPayload>(
SHOW_ONLY_WORKSPACE_ERROR_STACK_TRACE_ITEMS_PERSISTENCE_KEY,
"application"
);
const showWorkspaceOnly = useMemo(
() => Boolean(persistedShowWorkspaceItemsOnly?.value),
[persistedShowWorkspaceItemsOnly]
);
const { errorDetailsWorkspaceItemsOnly: showWorkspaceOnly } =
useErrorsSelector();
const { setErrorDetailsWorkspaceItemsOnly } = useStore.getState();
const stacksContainerRef = useRef<HTMLDivElement>(null);

const frameStacks = useMemo(
Expand Down Expand Up @@ -123,10 +112,7 @@ export const FlowStack = ({ data }: FlowStackProps) => {
sendUserActionTrackingEvent(trackingEvents.WORKSPACE_ONLY_TOGGLE_SWITCHED, {
value
});

setPersistedShowWorkspaceItemsOnly({
value
});
setErrorDetailsWorkspaceItemsOnly(value);
};

const handleFrameItemLinkClick = ({
Expand Down
145 changes: 77 additions & 68 deletions src/components/Errors/NewErrorCard/OccurrenceChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,74 +95,83 @@ export const OccurrenceChart = ({
<s.HistogramHeader>
<s.HistogramTitle>Occurrence over time</s.HistogramTitle>
</s.HistogramHeader>
{chartData?.dailyOccurrence && (
<ResponsiveContainer width={"100%"} height={"100%"}>
<BarChart data={chartData.dailyOccurrence} maxBarSize={MAX_BAR_WIDTH}>
<CartesianGrid
vertical={false}
stroke={theme.colors.v3.stroke.tertiary}
horizontalCoordinatesGenerator={({
offset
}: HorizontalCoordinatesGeneratorProps) => {
if (!offset.height || !isNumber(offset.top)) {
return [];
}
let linesCount = 4;
const lines = [];
const maxTickTopOffset = offset.height + offset.top;
const interval = Math.floor(offset.height / linesCount);
let top = maxTickTopOffset - interval;

while (linesCount) {
lines.push(top);
linesCount--;
top -= interval;
}

return lines;
}}
/>
<XAxis
dataKey={"date"}
axisLine={{ stroke: theme.colors.v3.stroke.tertiary }}
tickLine={false}
tick={XAxisTickLabelStyles}
tickFormatter={(x: string) => format(new Date(x), "MM/dd")}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={YAxisTickLabelStyles}
allowDecimals={false}
/>
<Bar
isAnimationActive={false}
dataKey={"value"}
fill={theme.colors.v3.status.backgroundHigh}
/>
<Tooltip
cursor={false}
content={(x) => {
const payload = x.payload;

if (!payload || payload.length === 0) {
return;
}

const { date, value } = payload[0]
.payload as ErrorOccurrenceRecord;

return (
<s.TooltipContainer>
<span>Occurrences: {value}</span>
<span>{format(new Date(date), "MM/dd/yyyy")}</span>
</s.TooltipContainer>
);
}}
isAnimationActive={false}
/>
</BarChart>
</ResponsiveContainer>
{chartData?.dailyOccurrence ? (
chartData.dailyOccurrence.length > 0 ? (
<ResponsiveContainer width={"100%"} height={"100%"}>
<BarChart
data={chartData.dailyOccurrence}
maxBarSize={MAX_BAR_WIDTH}
>
<CartesianGrid
vertical={false}
stroke={theme.colors.v3.stroke.tertiary}
horizontalCoordinatesGenerator={({
offset
}: HorizontalCoordinatesGeneratorProps) => {
if (!offset.height || !isNumber(offset.top)) {
return [];
}
let linesCount = 4;
const lines = [];
const maxTickTopOffset = offset.height + offset.top;
const interval = Math.floor(offset.height / linesCount);
let top = maxTickTopOffset - interval;

while (linesCount) {
lines.push(top);
linesCount--;
top -= interval;
}

return lines;
}}
/>
<XAxis
dataKey={"date"}
axisLine={{ stroke: theme.colors.v3.stroke.tertiary }}
tickLine={false}
tick={XAxisTickLabelStyles}
tickFormatter={(x: string) => format(new Date(x), "MM/dd")}
/>
<YAxis
axisLine={false}
tickLine={false}
tick={YAxisTickLabelStyles}
allowDecimals={false}
/>
<Bar
isAnimationActive={false}
dataKey={"value"}
fill={theme.colors.v3.status.backgroundHigh}
/>
<Tooltip
cursor={false}
content={(x) => {
const payload = x.payload;

if (!payload || payload.length === 0) {
return;
}

const { date, value } = payload[0]
.payload as ErrorOccurrenceRecord;

return (
<s.TooltipContainer>
<span>Occurrences: {value}</span>
<span>{format(new Date(date), "MM/dd/yyyy")}</span>
</s.TooltipContainer>
);
}}
isAnimationActive={false}
/>
</BarChart>
</ResponsiveContainer>
) : (
<s.EmptyStateContainer>No data</s.EmptyStateContainer>
)
) : (
<s.EmptyStateContainer>Loading...</s.EmptyStateContainer>
)}
</s.HistogramContainer>
);
Expand Down
8 changes: 8 additions & 0 deletions src/components/Errors/NewErrorCard/OccurrenceChart/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,11 @@ export const TooltipContainer = styled(Tooltip)`
flex-direction: column;
gap: 8px;
`;

export const EmptyStateContainer = styled.div`
display: flex;
justify-content: center;
align-items: center;
text-align: center;
flex-grow: 1;
`;
63 changes: 63 additions & 0 deletions src/components/Errors/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import { getFeatureFlagValue } from "../../featureFlags";
import { usePersistence } from "../../hooks/usePersistence";
import { usePrevious } from "../../hooks/usePrevious";
import { useConfigSelector } from "../../store/config/useConfigSelector";
import { useErrorsSelector } from "../../store/errors/useErrorsSelector";
import { useStore } from "../../store/useStore";
import { trackingEvents as globalEvents } from "../../trackingEvents";
import { isUndefined } from "../../typeGuards/isUndefined";
import { FeatureFlag } from "../../types";
import { sendUserActionTrackingEvent } from "../../utils/actions/sendUserActionTrackingEvent";
import { ErrorIcon } from "../common/icons/16px/ErrorIcon";
Expand All @@ -10,12 +16,34 @@ import { NewEmptyState } from "../common/v3/NewEmptyState";
import { useHistory } from "../Main/useHistory";
import { TAB_IDS } from "../Navigation/Tabs/types";
import { ErrorDetails } from "./ErrorDetails";
import { ShowOnlyWorkspaceErrorStackTraceItemsPayload } from "./ErrorDetails/ErrorDetailsCardContent/FlowStack/types";
import { ErrorsList } from "./ErrorsList";
import { GlobalErrorsList } from "./GlobalErrorsList";
import * as s from "./styles";

const SHOW_ONLY_WORKSPACE_ERROR_STACK_TRACE_ITEMS_PERSISTENCE_KEY =
"showOnlyWorkspaceErrorStackTraceItems";

export const Errors = () => {
const [persistedShowWorkspaceItemsOnly, setPersistedShowWorkspaceItemsOnly] =
usePersistence<ShowOnlyWorkspaceErrorStackTraceItemsPayload>(
SHOW_ONLY_WORKSPACE_ERROR_STACK_TRACE_ITEMS_PERSISTENCE_KEY,
"application"
);
const [
isErrorDetailsWorkspaceItemsOnlyRehydrated,
setIsErrorDetailsWorkspaceItemsOnlyRehydrated
] = useState(false);

const previousPersistedShowWorkspaceItemsOnly = usePrevious(
persistedShowWorkspaceItemsOnly
);
const { scope, backendInfo } = useConfigSelector();
const { errorDetailsWorkspaceItemsOnly } = useErrorsSelector();
const previousErrorDetailsWorkspaceItemsOnly = usePrevious(
errorDetailsWorkspaceItemsOnly
);
const { setErrorDetailsWorkspaceItemsOnly } = useStore.getState();
const spanCodeObjectId = scope?.span?.spanCodeObjectId;
const methodId = scope?.span?.methodId ?? undefined;
const { goTo } = useHistory();
Expand All @@ -26,6 +54,41 @@ export const Errors = () => {
FeatureFlag.ARE_GLOBAL_ERRORS_ENABLED
);

// Rehydrate "Workspace only" toggle value from persistence
useEffect(() => {
if (
isUndefined(previousPersistedShowWorkspaceItemsOnly) &&
!isUndefined(persistedShowWorkspaceItemsOnly)
) {
setErrorDetailsWorkspaceItemsOnly(
Boolean(persistedShowWorkspaceItemsOnly?.value)
);
setIsErrorDetailsWorkspaceItemsOnlyRehydrated(true);
}
}, [
persistedShowWorkspaceItemsOnly,
previousPersistedShowWorkspaceItemsOnly,
setErrorDetailsWorkspaceItemsOnly
]);

// Persist "Workspace only" toggle value on its change
useEffect(() => {
if (
previousErrorDetailsWorkspaceItemsOnly !==
errorDetailsWorkspaceItemsOnly &&
isErrorDetailsWorkspaceItemsOnlyRehydrated
) {
setPersistedShowWorkspaceItemsOnly({
value: errorDetailsWorkspaceItemsOnly
});
}
}, [
previousErrorDetailsWorkspaceItemsOnly,
errorDetailsWorkspaceItemsOnly,
setPersistedShowWorkspaceItemsOnly,
isErrorDetailsWorkspaceItemsOnlyRehydrated
]);

const handleErrorSelect = (errorId: string) => {
goTo(errorId);
};
Expand Down
13 changes: 11 additions & 2 deletions src/store/errors/errorsSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export interface ErrorsState {
globalErrorsSorting: GLOBAL_ERROR_SORTING_CRITERION;
globalErrorsFilters: GlobalErrorsFiltersState;
globalErrorsSelectedFilters: GlobalErrorsSelectedFiltersState;
errorDetailsWorkspaceItemsOnly: boolean;
}

const selectedFiltersInitialState = {
Expand All @@ -50,7 +51,7 @@ const selectedFiltersInitialState = {
handlingTypes: []
};

export const initialState: ErrorsState = {
export const globalErrorsInitialState = {
globalErrorsList: null,
globalErrorsTotalCount: 0,
areGlobalErrorsLoading: false,
Expand All @@ -66,6 +67,11 @@ export const initialState: ErrorsState = {
globalErrorsSelectedFilters: selectedFiltersInitialState
};

export const initialState: ErrorsState = {
...globalErrorsInitialState,
errorDetailsWorkspaceItemsOnly: false
};

const set = (update: Partial<ErrorsState>) => (state: ErrorsState) => ({
...state,
...update
Expand Down Expand Up @@ -96,6 +102,9 @@ export const errorsSlice = createSlice({
) => set({ globalErrorsSelectedFilters: filters }),
resetGlobalErrorsSelectedFilters: () =>
set({ globalErrorsSelectedFilters: selectedFiltersInitialState }),
resetGlobalErrors: () => set(initialState)
setErrorDetailsWorkspaceItemsOnly: (
errorDetailsWorkspaceItemsOnly: boolean
) => set({ errorDetailsWorkspaceItemsOnly }),
resetGlobalErrors: () => set({ ...globalErrorsInitialState })
}
});
Loading