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
This commit is contained in:
Aaryan Khandelwal 2024-01-22 20:50:30 +05:30 committed by GitHub
parent fd5326dec6
commit 2986769f28
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 132 additions and 105 deletions

View File

@ -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";

View File

@ -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 (
<div className="col-span-2">
<div className="lg:col-span-2">
<WidgetComponent dashboardId={homeDashboardId} workspaceSlug={workspaceSlug} />
</div>
);

View File

@ -26,15 +26,10 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = 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<TAssignedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
const handleUpdateFilters = async (filters: Partial<TAssignedIssuesWidgetFilters>) => {
if (!widgetDetails) return;
@ -48,8 +43,8 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = 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<WidgetProps> = 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 <WidgetLoader widgetKey={WIDGET_KEY} />;
return (
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full hover:shadow-custom-shadow-4xl duration-300 flex flex-col">
<Link href={redirectionLink} className="flex items-center justify-between gap-2 p-6 pl-7">
<h4 className="text-lg font-semibold text-custom-text-300">All issues assigned</h4>
<div className="flex items-center justify-between gap-2 p-6 pl-7">
<Link
href={`/${workspaceSlug}/workspace-views/assigned/${filterParams}`}
className="text-lg font-semibold text-custom-text-300 hover:underline"
>
All issues assigned
</Link>
<DurationFilterDropdown
value={widgetDetails.widget_filters.target_date ?? "this_week"}
onChange={(val) =>
@ -85,7 +84,7 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
})
}
/>
</Link>
</div>
<Tab.Group
as="div"
defaultIndex={ISSUES_TABS_LIST.findIndex((t) => t.key === widgetDetails.widget_filters.tab ?? "upcoming")}

View File

@ -26,15 +26,10 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = 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<TCreatedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
const handleUpdateFilters = async (filters: Partial<TCreatedIssuesWidgetFilters>) => {
if (!widgetDetails) return;
@ -48,8 +43,8 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = 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<WidgetProps> = 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 <WidgetLoader widgetKey={WIDGET_KEY} />;
return (
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full hover:shadow-custom-shadow-4xl duration-300 flex flex-col">
<Link href={redirectionLink} className="flex items-center justify-between gap-2 p-6 pl-7">
<h4 className="text-lg font-semibold text-custom-text-300">All issues created</h4>
<div className="flex items-center justify-between gap-2 p-6 pl-7">
<Link
href={`/${workspaceSlug}/workspace-views/created/${filterParams}`}
className="text-lg font-semibold text-custom-text-300 hover:underline"
>
All issues created
</Link>
<DurationFilterDropdown
value={widgetDetails.widget_filters.target_date ?? "this_week"}
onChange={(val) =>
@ -81,7 +80,7 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
})
}
/>
</Link>
</div>
<Tab.Group
as="div"
defaultIndex={ISSUES_TABS_LIST.findIndex((t) => t.key === widgetDetails.widget_filters.tab ?? "upcoming")}

View File

@ -16,6 +16,7 @@ export const DurationFilterDropdown: React.FC<Props> = (props) => {
return (
<CustomMenu
className="flex-shrink-0"
customButton={
<div className="px-3 py-2 border-[0.5px] border-custom-border-300 hover:bg-custom-background-80 focus:bg-custom-background-80 text-xs font-medium whitespace-nowrap rounded-md outline-none flex items-center gap-2">
{DURATION_FILTER_OPTIONS.find((option) => option.key === value)?.label}
@ -23,16 +24,10 @@ export const DurationFilterDropdown: React.FC<Props> = (props) => {
</div>
}
placement="bottom-end"
closeOnSelect
>
{DURATION_FILTER_OPTIONS.map((option) => (
<CustomMenu.MenuItem
key={option.key}
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
onChange(option.key);
}}
>
<CustomMenu.MenuItem key={option.key} onClick={() => onChange(option.key)}>
{option.label}
</CustomMenu.MenuItem>
))}

View File

@ -111,10 +111,13 @@ export const WidgetIssuesList: React.FC<WidgetIssuesListProps> = (props) => {
</div>
)}
</div>
{totalIssues > issues.length && (
{issues.length > 0 && (
<Link
href={`/${workspaceSlug}/workspace-views/${type}/${filterParams}`}
className={cn(getButtonStyling("accent-primary", "sm"), "w-min my-3 mx-auto py-1 px-2 text-xs")}
className={cn(
getButtonStyling("link-primary", "sm"),
"w-min my-3 mx-auto py-1 px-2 text-xs hover:bg-custom-primary-100/20"
)}
>
View all issues
</Link>

View File

@ -72,14 +72,9 @@ const WIDGET_KEY = "issues_by_priority";
export const IssuesByPriorityWidget: React.FC<WidgetProps> = 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<TIssuesByPriorityWidgetResponse[]>(workspaceSlug, dashboardId, WIDGET_KEY);
const handleUpdateFilters = async (filters: Partial<TIssuesByPriorityWidgetFilters>) => {
if (!widgetDetails) return;
@ -91,7 +86,7 @@ export const IssuesByPriorityWidget: React.FC<WidgetProps> = 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<WidgetProps> = observer((props) =>
};
return (
<Link
href={`/${workspaceSlug}/workspace-views/assigned`}
className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300 overflow-hidden"
>
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300 overflow-hidden">
<div className="flex items-center justify-between gap-2 pl-7 pr-6">
<h4 className="text-lg font-semibold text-custom-text-300">Priority of assigned issues</h4>
<Link
href={`/${workspaceSlug}/workspace-views/assigned`}
className="text-lg font-semibold text-custom-text-300 hover:underline"
>
Priority of assigned issues
</Link>
<DurationFilterDropdown
value={widgetDetails.widget_filters.target_date ?? "this_week"}
onChange={(val) =>
@ -203,6 +200,6 @@ export const IssuesByPriorityWidget: React.FC<WidgetProps> = observer((props) =>
<IssuesByPriorityEmptyState filter={widgetDetails.widget_filters.target_date ?? "this_week"} />
</div>
)}
</Link>
</div>
);
});

View File

@ -25,21 +25,15 @@ const WIDGET_KEY = "issues_by_state_groups";
export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props) => {
const { dashboardId, workspaceSlug } = props;
// states
const [activeStateGroup, setActiveStateGroup] = useState<TStateGroups>("started");
const [defaultStateGroup, setDefaultStateGroup] = useState<TStateGroups | null>(null);
const [activeStateGroup, setActiveStateGroup] = useState<TStateGroups | null>(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<TIssuesByStateGroupsWidgetResponse[]>(workspaceSlug, dashboardId, WIDGET_KEY);
const handleUpdateFilters = async (filters: Partial<TIssuesByStateGroupsWidgetFilters>) => {
if (!widgetDetails) return;
@ -51,10 +45,11 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = 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<WidgetProps> = 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 <WidgetLoader widgetKey={WIDGET_KEY} />;
const totalCount = widgetStats?.reduce((acc, item) => acc + item?.count, 0);
@ -107,12 +129,14 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
};
return (
<Link
href={`/${workspaceSlug?.toString()}/workspace-views/assigned`}
className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300 overflow-hidden"
>
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300 overflow-hidden">
<div className="flex items-center justify-between gap-2 pl-7 pr-6">
<h4 className="text-lg font-semibold text-custom-text-300">State of assigned issues</h4>
<Link
href={`/${workspaceSlug}/workspace-views/assigned`}
className="text-lg font-semibold text-custom-text-300 hover:underline"
>
State of assigned issues
</Link>
<DurationFilterDropdown
value={widgetDetails.widget_filters.target_date ?? "this_week"}
onChange={(val) =>
@ -157,6 +181,7 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = 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]}
/>
</div>
@ -183,6 +208,6 @@ export const IssuesByStateGroupWidget: React.FC<WidgetProps> = observer((props)
<IssuesByStateGroupEmptyState filter={widgetDetails.widget_filters.target_date ?? "this_week"} />
</div>
)}
</Link>
</div>
);
});

View File

@ -21,9 +21,9 @@ const WIDGET_KEY = "overview_stats";
export const OverviewStatsWidget: React.FC<WidgetProps> = 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<TOverviewStatsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
const today = renderFormattedPayloadDate(new Date());
const STATS_LIST = [
@ -76,11 +76,14 @@ export const OverviewStatsWidget: React.FC<WidgetProps> = observer((props) => {
)}
<Link
href={stat.link}
className={cn(`py-4 hover:bg-custom-background-80 duration-300 rounded-[10px] w-full break-words`, {
"pl-11 pr-[4.725rem] mr-0.5": isFirst,
"px-[4.725rem] mx-0.5": isMiddle,
"px-[4.725rem] ml-0.5": isLast,
})}
className={cn(
`py-4 hover:bg-custom-background-80 duration-300 rounded-[10px] w-full break-words flex flex-col justify-center`,
{
"pl-11 pr-[4.725rem] mr-0.5": isFirst,
"px-[4.725rem] mx-0.5": isMiddle,
"px-[4.725rem] ml-0.5": isLast,
}
)}
>
<h5 className="font-semibold text-xl">{stat.count}</h5>
<p className="text-custom-text-300">{stat.title}</p>

View File

@ -21,8 +21,8 @@ export const RecentActivityWidget: React.FC<WidgetProps> = 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<TRecentActivityWidgetResponse[]>(workspaceSlug, dashboardId, WIDGET_KEY);
useEffect(() => {
if (!widgetStats)
@ -34,13 +34,10 @@ export const RecentActivityWidget: React.FC<WidgetProps> = observer((props) => {
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
return (
<Link
href="/profile/activity"
className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300"
>
<div className="flex items-center justify-between gap-2 px-7">
<h4 className="text-lg font-semibold text-custom-text-300">My activity</h4>
</div>
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300">
<Link href="/profile/activity" className="text-lg font-semibold text-custom-text-300 mx-7 hover:underline">
My activity
</Link>
{widgetStats.length > 0 ? (
<div className="space-y-6 mt-4 mx-7">
{widgetStats.map((activity) => (
@ -100,6 +97,6 @@ export const RecentActivityWidget: React.FC<WidgetProps> = observer((props) => {
<RecentActivityEmptyState />
</div>
)}
</Link>
</div>
);
});

View File

@ -53,10 +53,8 @@ const CollaboratorListItem: React.FC<CollaboratorListItemProps> = observer((prop
export const RecentCollaboratorsWidget: React.FC<WidgetProps> = 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<TRecentCollaboratorsWidgetResponse[]>(workspaceSlug, dashboardId, WIDGET_KEY);
useEffect(() => {
if (!widgetStats)

View File

@ -39,7 +39,7 @@ const ProjectListItem: React.FC<ProjectListItemProps> = observer((props) => {
className={`h-[3.375rem] w-[3.375rem] grid place-items-center rounded border border-transparent flex-shrink-0 ${randomBgColor}`}
>
{projectDetails.emoji ? (
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
<span className="grid h-7 w-7 flex-shrink-0 text-2xl place-items-center rounded uppercase">
{renderEmoji(projectDetails.emoji)}
</span>
) : projectDetails.icon_prop ? (
@ -75,9 +75,9 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = 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<TRecentProjectsWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
const canCreateProject = currentWorkspaceRole === EUserWorkspaceRoles.ADMIN;
useEffect(() => {
@ -90,13 +90,13 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
if (!widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
return (
<Link
href={`/${workspaceSlug}/projects`}
className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300"
>
<div className="flex items-center justify-between gap-2 px-7">
<h4 className="text-lg font-semibold text-custom-text-300">My projects</h4>
</div>
<div className="bg-custom-background-100 rounded-xl border-[0.5px] border-custom-border-200 w-full py-6 hover:shadow-custom-shadow-4xl duration-300">
<Link
href={`/${workspaceSlug}/projects`}
className="text-lg font-semibold text-custom-text-300 mx-7 hover:underline"
>
My projects
</Link>
<div className="space-y-8 mt-4 mx-7">
{canCreateProject && (
<button
@ -120,6 +120,6 @@ export const RecentProjectsWidget: React.FC<WidgetProps> = observer((props) => {
<ProjectListItem key={projectId} projectId={projectId} workspaceSlug={workspaceSlug} />
))}
</div>
</Link>
</div>
);
});

View File

@ -35,6 +35,7 @@ export interface IDashboardStore {
homeDashboardWidgets: TWidget[] | undefined;
// computed actions
getWidgetDetails: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => TWidget | undefined;
getWidgetStats: <T>(workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => T | undefined;
// actions
fetchHomeDashboardWidgets: (workspaceSlug: string) => Promise<THomeDashboardResponse>;
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 = <T>(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