From 4e8650dd46d0338bce814a6e9f85dc49f0967ac2 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Wed, 20 Mar 2024 11:10:28 +0100 Subject: [PATCH 01/13] Mark insights as read --- .../InsightsCatalog.stories.tsx | 41 ++++ .../Insights/InsightsCatalog/index.tsx | 200 ++++++++++++------ .../Insights/InsightsCatalog/styles.ts | 56 ++++- .../Insights/InsightsCatalog/types.ts | 2 + .../InsightsCatalog/useMarkingAllAsRead.ts | 43 ++++ src/components/Insights/actions.ts | 6 +- .../InsightCard/InsightCard.stories.tsx | 45 +++- .../useDismissal.ts} | 25 +-- .../InsightCard/hooks/useMarkingAsRead.ts | 46 ++++ .../Insights/common/InsightCard/index.tsx | 46 +++- .../Insights/common/InsightCard/styles.ts | 12 ++ .../Insights/common/InsightCard/types.ts | 23 ++ src/components/Insights/index.tsx | 20 +- src/components/Insights/types.ts | 4 + src/components/Navigation/mockData.ts | 2 +- src/components/common/App/types.ts | 16 +- src/components/common/v3/Card/index.tsx | 2 +- src/components/common/v3/Card/types.ts | 1 + src/featureFlags.ts | 3 +- src/types.ts | 3 +- 20 files changed, 487 insertions(+), 109 deletions(-) create mode 100644 src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx create mode 100644 src/components/Insights/InsightsCatalog/useMarkingAllAsRead.ts rename src/components/Insights/common/InsightCard/{useDismissalHandler.ts => hooks/useDismissal.ts} (72%) create mode 100644 src/components/Insights/common/InsightCard/hooks/useMarkingAsRead.ts diff --git a/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx new file mode 100644 index 000000000..3ea49e5f6 --- /dev/null +++ b/src/components/Insights/InsightsCatalog/InsightsCatalog.stories.tsx @@ -0,0 +1,41 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { InsightsCatalog } from "."; +import { SORTING_ORDER } from "../../common/SortingSelector/types"; +import { mockedSpanBottleneckInsight } from "../common/insights/EndpointBottleneckInsight/mockData"; +import { SORTING_CRITERION } from "./types"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Insights/InsightsCatalog", + component: InsightsCatalog, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + args: { + insights: [{ ...mockedSpanBottleneckInsight, isRead: false }], + totalCount: 1, + dismissedCount: 1, + defaultQuery: { + page: 0, + sorting: { + criterion: SORTING_CRITERION.LATEST, + order: SORTING_ORDER.DESC + }, + searchQuery: null, + showDismissed: false, + insightViewType: "Issues", + showUnreadOnly: false + }, + isDismissalEnabled: true, + unreadCount: 1, + isMarkingAsReadEnabled: true + } +}; diff --git a/src/components/Insights/InsightsCatalog/index.tsx b/src/components/Insights/InsightsCatalog/index.tsx index 32fce607e..5c5c99c4f 100644 --- a/src/components/Insights/InsightsCatalog/index.tsx +++ b/src/components/Insights/InsightsCatalog/index.tsx @@ -4,6 +4,7 @@ import { usePrevious } from "../../../hooks/usePrevious"; import { useTheme } from "styled-components"; import { useDebounce } from "../../../hooks/useDebounce"; import { isNumber } from "../../../typeGuards/isNumber"; +import { isString } from "../../../typeGuards/isString"; import { isUndefined } from "../../../typeGuards/isUndefined"; import { sendTrackingEvent } from "../../../utils/sendTrackingEvent"; import { ConfigContext } from "../../common/App/ConfigContext"; @@ -21,11 +22,13 @@ import { InsightsPage } from "../InsightsPage"; import { trackingEvents } from "../tracking"; import * as s from "./styles"; import { InsightsCatalogProps, SORTING_CRITERION } from "./types"; +import { useMarkingAllAsRead } from "./useMarkingAllAsRead"; const PAGE_SIZE = 10; enum ViewMode { All, - OnlyDismissed + OnlyDismissed, + OnlyUnread } export const InsightsCatalog = (props: InsightsCatalogProps) => { @@ -50,11 +53,22 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { const [mode, setMode] = useState(ViewMode.All); const previousMode = usePrevious(mode); const theme = useTheme(); + const { isMarkingAllAsReadInProgress, markAllAsRead } = useMarkingAllAsRead( + config.scope?.span || null + ); + const previousIsMarkingAllAsReadInProgress = usePrevious( + isMarkingAllAsReadInProgress + ); - const isViewModeButtonVisible = + const isDismissalViewModeButtonVisible = props.isDismissalEnabled && (isUndefined(props.dismissedCount) || props.dismissedCount > 0); // isUndefined - check for backward compatibility, always show when BE does not return this counter + const isMarkingAsReadToolbarVisible = + props.isMarkingAsReadEnabled && + isNumber(props.unreadCount) && + props.unreadCount > 0; + const refreshData = useCallback( () => props.onQueryChange({ @@ -62,7 +76,8 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { page, sorting, searchQuery: debouncedSearchInputValue, - showDismissed: mode === ViewMode.OnlyDismissed + showDismissed: mode === ViewMode.OnlyDismissed, + showUnreadOnly: mode === ViewMode.OnlyUnread }), [ page, @@ -81,12 +96,35 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { refreshData(); }; - const handleViewModeChange = () => { + + const handleDismissalViewModeButtonClick = () => { const newMode = mode === ViewMode.All ? ViewMode.OnlyDismissed : ViewMode.All; setMode(newMode); }; + const handleUnreadOnlyLinkClick = () => { + setMode(ViewMode.OnlyUnread); + }; + + const handleReadAllLinkClick = () => { + markAllAsRead(); + }; + + const handleBackToAllInsightsButtonClick = () => { + setMode(ViewMode.All); + }; + + useEffect(() => { + if (previousIsMarkingAllAsReadInProgress && !isMarkingAllAsReadInProgress) { + refreshData(); + } + }, [ + isMarkingAllAsReadInProgress, + previousIsMarkingAllAsReadInProgress, + refreshData + ]); + useEffect(() => { if (!previousScope || previousScope !== config.scope?.span) { setSearchInputValue(""); @@ -108,12 +146,19 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { refreshData(); }, []); + useEffect(() => { + if (previousMode && previousMode !== mode) { + setPage(0); + } + }, [previousMode, mode]); + useEffect(() => { if ( (isNumber(previousPage) && previousPage !== page) || (previousSorting && previousSorting !== sorting) || - previousSearchQuery !== debouncedSearchInputValue || - previousMode !== mode + (isString(previousSearchQuery) && + previousSearchQuery !== debouncedSearchInputValue) || + (previousMode && previousMode !== mode) ) { refreshData(); } @@ -132,59 +177,96 @@ export const InsightsCatalog = (props: InsightsCatalogProps) => { return ( <> - { - setSearchInputValue(val); - }} - value={searchInputValue} - /> - { - setSorting(val); - }} - options={[ - ...(defaultQuery.insightViewType === "Issues" - ? [ - { - value: SORTING_CRITERION.CRITICAL_INSIGHTS, - label: "Critical issues", - defaultOrder: SORTING_ORDER.DESC - } - ] - : []), - { - value: SORTING_CRITERION.LATEST, - label: "Latest", - defaultOrder: SORTING_ORDER.DESC - } - ]} - default={defaultQuery.sorting} - /> - - + { + setSearchInputValue(val); + }} + value={searchInputValue} /> - - - {mode === ViewMode.OnlyDismissed && ( - -