From 2986769f282d26480172ba74cd975720719d0020 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal <65252264+aaryan610@users.noreply.github.com> Date: Mon, 22 Jan 2024 20:50:30 +0530 Subject: [PATCH] fix: bugs in dashboard widgets (#3420) * fix: bugs in dashboard widgets * chore: updated view all issues button * refactor: make use of getWidgetDetails and getWidgetStats computed functions * fix: widgets redirection * fix: build errors --- packages/ui/src/dropdowns/custom-menu.tsx | 2 +- .../dashboard/home-dashboard-widgets.tsx | 2 +- .../dashboard/widgets/assigned-issues.tsx | 27 ++++---- .../dashboard/widgets/created-issues.tsx | 27 ++++---- .../widgets/dropdowns/duration-filter.tsx | 11 +--- .../widgets/issue-panels/issues-list.tsx | 7 ++- .../dashboard/widgets/issues-by-priority.tsx | 27 ++++---- .../widgets/issues-by-state-group.tsx | 61 +++++++++++++------ .../dashboard/widgets/overview-stats.tsx | 17 +++--- .../dashboard/widgets/recent-activity.tsx | 17 +++--- .../widgets/recent-collaborators.tsx | 6 +- .../dashboard/widgets/recent-projects.tsx | 22 +++---- web/store/dashboard.store.ts | 11 ++++ 13 files changed, 132 insertions(+), 105 deletions(-) diff --git a/packages/ui/src/dropdowns/custom-menu.tsx b/packages/ui/src/dropdowns/custom-menu.tsx index 08239f16a..df2e99a3d 100644 --- a/packages/ui/src/dropdowns/custom-menu.tsx +++ b/packages/ui/src/dropdowns/custom-menu.tsx @@ -1,6 +1,6 @@ import * as React from "react"; import ReactDOM from "react-dom"; -// react-poppper +// react-popper import { usePopper } from "react-popper"; // hooks import { useDropdownKeyDown } from "../hooks/use-dropdown-key-down"; diff --git a/web/components/dashboard/home-dashboard-widgets.tsx b/web/components/dashboard/home-dashboard-widgets.tsx index 6402877c5..fb6055a67 100644 --- a/web/components/dashboard/home-dashboard-widgets.tsx +++ b/web/components/dashboard/home-dashboard-widgets.tsx @@ -50,7 +50,7 @@ export const DashboardWidgets = observer(() => { // if the widget is full width, return it in a 2 column grid if (widget.fullWidth) return ( -
+
); diff --git a/web/components/dashboard/widgets/assigned-issues.tsx b/web/components/dashboard/widgets/assigned-issues.tsx index 5ad24ee0f..283873a5a 100644 --- a/web/components/dashboard/widgets/assigned-issues.tsx +++ b/web/components/dashboard/widgets/assigned-issues.tsx @@ -26,15 +26,10 @@ export const AssignedIssuesWidget: React.FC = observer((props) => { // states const [fetching, setFetching] = useState(false); // store hooks - const { - fetchWidgetStats, - widgetDetails: allWidgetDetails, - widgetStats: allWidgetStats, - updateDashboardWidgetFilters, - } = useDashboard(); + const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard(); // derived values - const widgetDetails = allWidgetDetails?.[workspaceSlug]?.[dashboardId]?.find((w) => w.key === WIDGET_KEY); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TAssignedIssuesWidgetResponse; + const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const handleUpdateFilters = async (filters: Partial) => { if (!widgetDetails) return; @@ -48,8 +43,8 @@ export const AssignedIssuesWidget: React.FC = observer((props) => { fetchWidgetStats(workspaceSlug, dashboardId, { widget_key: WIDGET_KEY, - issue_type: widgetDetails.widget_filters.tab ?? "upcoming", - target_date: getCustomDates(widgetDetails.widget_filters.target_date ?? "this_week"), + issue_type: filters.tab ?? widgetDetails.widget_filters.tab ?? "upcoming", + target_date: getCustomDates(filters.target_date ?? widgetDetails.widget_filters.target_date ?? "this_week"), expand: "issue_relation", }).finally(() => setFetching(false)); }; @@ -69,14 +64,18 @@ export const AssignedIssuesWidget: React.FC = observer((props) => { }, [dashboardId, fetchWidgetStats, widgetDetails, widgetStats, workspaceSlug]); const filterParams = getRedirectionFilters(widgetDetails?.widget_filters.tab ?? "upcoming"); - const redirectionLink = `/${workspaceSlug}/workspace-views/assigned/${filterParams}`; if (!widgetDetails || !widgetStats) return ; return (
- -

All issues assigned

+
+ + All issues assigned + @@ -85,7 +84,7 @@ export const AssignedIssuesWidget: React.FC = observer((props) => { }) } /> - +
t.key === widgetDetails.widget_filters.tab ?? "upcoming")} diff --git a/web/components/dashboard/widgets/created-issues.tsx b/web/components/dashboard/widgets/created-issues.tsx index c25623070..c17645696 100644 --- a/web/components/dashboard/widgets/created-issues.tsx +++ b/web/components/dashboard/widgets/created-issues.tsx @@ -26,15 +26,10 @@ export const CreatedIssuesWidget: React.FC = observer((props) => { // states const [fetching, setFetching] = useState(false); // store hooks - const { - fetchWidgetStats, - widgetDetails: allWidgetDetails, - widgetStats: allWidgetStats, - updateDashboardWidgetFilters, - } = useDashboard(); + const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard(); // derived values - const widgetDetails = allWidgetDetails?.[workspaceSlug]?.[dashboardId]?.find((w) => w.key === WIDGET_KEY); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TCreatedIssuesWidgetResponse; + const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const handleUpdateFilters = async (filters: Partial) => { if (!widgetDetails) return; @@ -48,8 +43,8 @@ export const CreatedIssuesWidget: React.FC = observer((props) => { fetchWidgetStats(workspaceSlug, dashboardId, { widget_key: WIDGET_KEY, - issue_type: widgetDetails.widget_filters.tab ?? "upcoming", - target_date: getCustomDates(widgetDetails.widget_filters.target_date ?? "this_week"), + issue_type: filters.tab ?? widgetDetails.widget_filters.tab ?? "upcoming", + target_date: getCustomDates(filters.target_date ?? widgetDetails.widget_filters.target_date ?? "this_week"), }).finally(() => setFetching(false)); }; @@ -65,14 +60,18 @@ export const CreatedIssuesWidget: React.FC = observer((props) => { }, [dashboardId, fetchWidgetStats, widgetDetails, widgetStats, workspaceSlug]); const filterParams = getRedirectionFilters(widgetDetails?.widget_filters.tab ?? "upcoming"); - const redirectionLink = `/${workspaceSlug}/workspace-views/created/${filterParams}`; if (!widgetDetails || !widgetStats) return ; return (
- -

All issues created

+
+ + All issues created + @@ -81,7 +80,7 @@ export const CreatedIssuesWidget: React.FC = observer((props) => { }) } /> - +
t.key === widgetDetails.widget_filters.tab ?? "upcoming")} diff --git a/web/components/dashboard/widgets/dropdowns/duration-filter.tsx b/web/components/dashboard/widgets/dropdowns/duration-filter.tsx index fedc92cbe..0db293a65 100644 --- a/web/components/dashboard/widgets/dropdowns/duration-filter.tsx +++ b/web/components/dashboard/widgets/dropdowns/duration-filter.tsx @@ -16,6 +16,7 @@ export const DurationFilterDropdown: React.FC = (props) => { return ( {DURATION_FILTER_OPTIONS.find((option) => option.key === value)?.label} @@ -23,16 +24,10 @@ export const DurationFilterDropdown: React.FC = (props) => {
} placement="bottom-end" + closeOnSelect > {DURATION_FILTER_OPTIONS.map((option) => ( - { - e.preventDefault(); - e.stopPropagation(); - onChange(option.key); - }} - > + onChange(option.key)}> {option.label} ))} diff --git a/web/components/dashboard/widgets/issue-panels/issues-list.tsx b/web/components/dashboard/widgets/issue-panels/issues-list.tsx index d104bbd05..e1b2967bf 100644 --- a/web/components/dashboard/widgets/issue-panels/issues-list.tsx +++ b/web/components/dashboard/widgets/issue-panels/issues-list.tsx @@ -111,10 +111,13 @@ export const WidgetIssuesList: React.FC = (props) => {
)}
- {totalIssues > issues.length && ( + {issues.length > 0 && ( View all issues diff --git a/web/components/dashboard/widgets/issues-by-priority.tsx b/web/components/dashboard/widgets/issues-by-priority.tsx index 1e0cd30da..46534a426 100644 --- a/web/components/dashboard/widgets/issues-by-priority.tsx +++ b/web/components/dashboard/widgets/issues-by-priority.tsx @@ -72,14 +72,9 @@ const WIDGET_KEY = "issues_by_priority"; export const IssuesByPriorityWidget: React.FC = observer((props) => { const { dashboardId, workspaceSlug } = props; // store hooks - const { - fetchWidgetStats, - widgetDetails: allWidgetDetails, - widgetStats: allWidgetStats, - updateDashboardWidgetFilters, - } = useDashboard(); - const widgetDetails = allWidgetDetails?.[workspaceSlug]?.[dashboardId]?.find((w) => w.key === WIDGET_KEY); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TIssuesByPriorityWidgetResponse[]; + const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard(); + const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const handleUpdateFilters = async (filters: Partial) => { if (!widgetDetails) return; @@ -91,7 +86,7 @@ export const IssuesByPriorityWidget: React.FC = observer((props) => fetchWidgetStats(workspaceSlug, dashboardId, { widget_key: WIDGET_KEY, - target_date: getCustomDates(widgetDetails.widget_filters.target_date ?? "this_week"), + target_date: getCustomDates(filters.target_date ?? widgetDetails.widget_filters.target_date ?? "this_week"), }); }; @@ -135,12 +130,14 @@ export const IssuesByPriorityWidget: React.FC = observer((props) => }; return ( - +
-

Priority of assigned issues

+ + Priority of assigned issues + @@ -203,6 +200,6 @@ export const IssuesByPriorityWidget: React.FC = observer((props) =>
)} - +
); }); diff --git a/web/components/dashboard/widgets/issues-by-state-group.tsx b/web/components/dashboard/widgets/issues-by-state-group.tsx index d4478040a..113ce9613 100644 --- a/web/components/dashboard/widgets/issues-by-state-group.tsx +++ b/web/components/dashboard/widgets/issues-by-state-group.tsx @@ -25,21 +25,15 @@ const WIDGET_KEY = "issues_by_state_groups"; export const IssuesByStateGroupWidget: React.FC = observer((props) => { const { dashboardId, workspaceSlug } = props; // states - const [activeStateGroup, setActiveStateGroup] = useState("started"); + const [defaultStateGroup, setDefaultStateGroup] = useState(null); + const [activeStateGroup, setActiveStateGroup] = useState(null); // router const router = useRouter(); // store hooks - const { - fetchWidgetStats, - widgetDetails: allWidgetDetails, - widgetStats: allWidgetStats, - updateDashboardWidgetFilters, - } = useDashboard(); + const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard(); // derived values - const widgetDetails = allWidgetDetails?.[workspaceSlug]?.[dashboardId]?.find((w) => w.key === WIDGET_KEY); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[ - WIDGET_KEY - ] as TIssuesByStateGroupsWidgetResponse[]; + const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const handleUpdateFilters = async (filters: Partial) => { if (!widgetDetails) return; @@ -51,10 +45,11 @@ export const IssuesByStateGroupWidget: React.FC = observer((props) fetchWidgetStats(workspaceSlug, dashboardId, { widget_key: WIDGET_KEY, - target_date: getCustomDates(widgetDetails.widget_filters.target_date ?? "this_week"), + target_date: getCustomDates(filters.target_date ?? widgetDetails.widget_filters.target_date ?? "this_week"), }); }; + // fetch widget stats useEffect(() => { if (!widgetDetails) return; @@ -65,6 +60,33 @@ export const IssuesByStateGroupWidget: React.FC = observer((props) }); }, [dashboardId, fetchWidgetStats, widgetDetails, widgetStats, workspaceSlug]); + // set active group for center metric + useEffect(() => { + if (!widgetStats) return; + + const startedCount = widgetStats?.find((item) => item?.state === "started")?.count ?? 0; + const unStartedCount = widgetStats?.find((item) => item?.state === "unstarted")?.count ?? 0; + const backlogCount = widgetStats?.find((item) => item?.state === "backlog")?.count ?? 0; + const completedCount = widgetStats?.find((item) => item?.state === "completed")?.count ?? 0; + const canceledCount = widgetStats?.find((item) => item?.state === "cancelled")?.count ?? 0; + + const stateGroup = + startedCount > 0 + ? "started" + : unStartedCount > 0 + ? "unstarted" + : backlogCount > 0 + ? "backlog" + : completedCount > 0 + ? "completed" + : canceledCount > 0 + ? "cancelled" + : null; + + setActiveStateGroup(stateGroup); + setDefaultStateGroup(stateGroup); + }, [widgetStats]); + if (!widgetDetails || !widgetStats) return ; const totalCount = widgetStats?.reduce((acc, item) => acc + item?.count, 0); @@ -107,12 +129,14 @@ export const IssuesByStateGroupWidget: React.FC = observer((props) }; return ( - +
-

State of assigned issues

+ + State of assigned issues + @@ -157,6 +181,7 @@ export const IssuesByStateGroupWidget: React.FC = observer((props) router.push(`/${workspaceSlug}/workspace-views/assigned/?state_group=${datum.id}`); }} onMouseEnter={(datum) => setActiveStateGroup(datum.id as TStateGroups)} + onMouseLeave={() => setActiveStateGroup(defaultStateGroup)} layers={["arcs", CenteredMetric]} />
@@ -183,6 +208,6 @@ export const IssuesByStateGroupWidget: React.FC = observer((props)
)} - + ); }); diff --git a/web/components/dashboard/widgets/overview-stats.tsx b/web/components/dashboard/widgets/overview-stats.tsx index 74630e1f8..a38e29eb8 100644 --- a/web/components/dashboard/widgets/overview-stats.tsx +++ b/web/components/dashboard/widgets/overview-stats.tsx @@ -21,9 +21,9 @@ const WIDGET_KEY = "overview_stats"; export const OverviewStatsWidget: React.FC = observer((props) => { const { dashboardId, workspaceSlug } = props; // store hooks - const { fetchWidgetStats, widgetStats: allWidgetStats } = useDashboard(); + const { fetchWidgetStats, getWidgetStats } = useDashboard(); // derived values - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TOverviewStatsWidgetResponse; + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const today = renderFormattedPayloadDate(new Date()); const STATS_LIST = [ @@ -76,11 +76,14 @@ export const OverviewStatsWidget: React.FC = observer((props) => { )}
{stat.count}

{stat.title}

diff --git a/web/components/dashboard/widgets/recent-activity.tsx b/web/components/dashboard/widgets/recent-activity.tsx index 52fca4600..2066689c3 100644 --- a/web/components/dashboard/widgets/recent-activity.tsx +++ b/web/components/dashboard/widgets/recent-activity.tsx @@ -21,8 +21,8 @@ export const RecentActivityWidget: React.FC = observer((props) => { // store hooks const { currentUser } = useUser(); // derived values - const { fetchWidgetStats, widgetStats: allWidgetStats } = useDashboard(); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TRecentActivityWidgetResponse[]; + const { fetchWidgetStats, getWidgetStats } = useDashboard(); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); useEffect(() => { if (!widgetStats) @@ -34,13 +34,10 @@ export const RecentActivityWidget: React.FC = observer((props) => { if (!widgetStats) return ; return ( - -
-

My activity

-
+
+ + My activity + {widgetStats.length > 0 ? (
{widgetStats.map((activity) => ( @@ -100,6 +97,6 @@ export const RecentActivityWidget: React.FC = observer((props) => {
)} - +
); }); diff --git a/web/components/dashboard/widgets/recent-collaborators.tsx b/web/components/dashboard/widgets/recent-collaborators.tsx index 693f9808d..47d87ffa9 100644 --- a/web/components/dashboard/widgets/recent-collaborators.tsx +++ b/web/components/dashboard/widgets/recent-collaborators.tsx @@ -53,10 +53,8 @@ const CollaboratorListItem: React.FC = observer((prop export const RecentCollaboratorsWidget: React.FC = observer((props) => { const { dashboardId, workspaceSlug } = props; // store hooks - const { fetchWidgetStats, widgetStats: allWidgetStats } = useDashboard(); - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[ - WIDGET_KEY - ] as TRecentCollaboratorsWidgetResponse[]; + const { fetchWidgetStats, getWidgetStats } = useDashboard(); + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); useEffect(() => { if (!widgetStats) diff --git a/web/components/dashboard/widgets/recent-projects.tsx b/web/components/dashboard/widgets/recent-projects.tsx index d85cf52c8..e28886460 100644 --- a/web/components/dashboard/widgets/recent-projects.tsx +++ b/web/components/dashboard/widgets/recent-projects.tsx @@ -39,7 +39,7 @@ const ProjectListItem: React.FC = observer((props) => { className={`h-[3.375rem] w-[3.375rem] grid place-items-center rounded border border-transparent flex-shrink-0 ${randomBgColor}`} > {projectDetails.emoji ? ( - + {renderEmoji(projectDetails.emoji)} ) : projectDetails.icon_prop ? ( @@ -75,9 +75,9 @@ export const RecentProjectsWidget: React.FC = observer((props) => { const { membership: { currentWorkspaceRole }, } = useUser(); - const { fetchWidgetStats, widgetStats: allWidgetStats } = useDashboard(); + const { fetchWidgetStats, getWidgetStats } = useDashboard(); // derived values - const widgetStats = allWidgetStats?.[workspaceSlug]?.[dashboardId]?.[WIDGET_KEY] as TRecentProjectsWidgetResponse; + const widgetStats = getWidgetStats(workspaceSlug, dashboardId, WIDGET_KEY); const canCreateProject = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN; useEffect(() => { @@ -90,13 +90,13 @@ export const RecentProjectsWidget: React.FC = observer((props) => { if (!widgetStats) return ; return ( - -
-

My projects

-
+
+ + My projects +
{canCreateProject && (
- +
); }); diff --git a/web/store/dashboard.store.ts b/web/store/dashboard.store.ts index c2f9c8545..ad0960c7b 100644 --- a/web/store/dashboard.store.ts +++ b/web/store/dashboard.store.ts @@ -35,6 +35,7 @@ export interface IDashboardStore { homeDashboardWidgets: TWidget[] | undefined; // computed actions getWidgetDetails: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => TWidget | undefined; + getWidgetStats: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => T | undefined; // actions fetchHomeDashboardWidgets: (workspaceSlug: string) => Promise; fetchWidgetStats: ( @@ -114,6 +115,16 @@ export class DashboardStore implements IDashboardStore { return widgets.find((widget) => widget.key === widgetKey); }); + /** + * @description get widget stats + * @param workspaceSlug + * @param dashboardId + * @param widgetKey + * @returns widget stats + */ + getWidgetStats = (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys): T | undefined => + (this.widgetStats?.[workspaceSlug]?.[dashboardId]?.[widgetKey] as unknown as T) ?? undefined; + /** * @description fetch home dashboard details and widgets * @param workspaceSlug