forked from github/plane
chore: error state for the issues widgets (#3934)
This commit is contained in:
parent
8c9d328c24
commit
e3ac075ee2
@ -7,6 +7,7 @@ import { useDashboard } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
DurationFilterDropdown,
|
DurationFilterDropdown,
|
||||||
|
IssuesErrorState,
|
||||||
TabsList,
|
TabsList,
|
||||||
WidgetIssuesList,
|
WidgetIssuesList,
|
||||||
WidgetLoader,
|
WidgetLoader,
|
||||||
@ -26,10 +27,12 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||||||
// states
|
// states
|
||||||
const [fetching, setFetching] = useState(false);
|
const [fetching, setFetching] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard();
|
const { fetchWidgetStats, getWidgetDetails, getWidgetStats, getWidgetStatsError, updateDashboardWidgetFilters } =
|
||||||
|
useDashboard();
|
||||||
// derived values
|
// derived values
|
||||||
const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY);
|
const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
const widgetStats = getWidgetStats<TAssignedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
const widgetStats = getWidgetStats<TAssignedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
|
const widgetStatsError = getWidgetStatsError(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE;
|
const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE;
|
||||||
const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab);
|
const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab);
|
||||||
const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? [];
|
const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? [];
|
||||||
@ -73,74 +76,91 @@ export const AssignedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||||||
const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST;
|
const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST;
|
||||||
const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab);
|
const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab);
|
||||||
|
|
||||||
if (!widgetDetails || !widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
if ((!widgetDetails || !widgetStats) && !widgetStatsError) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||||
|
|
||||||
return (
|
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 min-h-96">
|
<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 min-h-96">
|
||||||
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
{widgetStatsError ? (
|
||||||
<Link
|
<IssuesErrorState
|
||||||
href={`/${workspaceSlug}/workspace-views/assigned/${filterParams}`}
|
isRefreshing={fetching}
|
||||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
onClick={() =>
|
||||||
>
|
|
||||||
Assigned to you
|
|
||||||
</Link>
|
|
||||||
<DurationFilterDropdown
|
|
||||||
customDates={selectedCustomDates}
|
|
||||||
value={selectedDurationFilter}
|
|
||||||
onChange={(val, customDates) => {
|
|
||||||
if (val === "custom" && customDates) {
|
|
||||||
handleUpdateFilters({
|
|
||||||
duration: val,
|
|
||||||
custom_dates: customDates,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val === selectedDurationFilter) return;
|
|
||||||
|
|
||||||
let newTab = selectedTab;
|
|
||||||
// switch to pending tab if target date is changed to none
|
|
||||||
if (val === "none" && selectedTab !== "completed") newTab = "pending";
|
|
||||||
// switch to upcoming tab if target date is changed to other than none
|
|
||||||
if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed") newTab = "upcoming";
|
|
||||||
|
|
||||||
handleUpdateFilters({
|
handleUpdateFilters({
|
||||||
duration: val,
|
duration: EDurationFilters.NONE,
|
||||||
tab: newTab,
|
tab: "pending",
|
||||||
});
|
})
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
<Tab.Group
|
widgetStats && (
|
||||||
as="div"
|
<>
|
||||||
selectedIndex={selectedTabIndex}
|
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
||||||
onChange={(i) => {
|
<Link
|
||||||
const newSelectedTab = tabsList[i];
|
href={`/${workspaceSlug}/workspace-views/assigned/${filterParams}`}
|
||||||
handleUpdateFilters({ tab: newSelectedTab?.key ?? "completed" });
|
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||||
}}
|
>
|
||||||
className="h-full flex flex-col"
|
Assigned to you
|
||||||
>
|
</Link>
|
||||||
<div className="px-6">
|
<DurationFilterDropdown
|
||||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
customDates={selectedCustomDates}
|
||||||
</div>
|
value={selectedDurationFilter}
|
||||||
<Tab.Panels as="div" className="h-full">
|
onChange={(val, customDates) => {
|
||||||
{tabsList.map((tab) => {
|
if (val === "custom" && customDates) {
|
||||||
if (tab.key !== selectedTab) return null;
|
handleUpdateFilters({
|
||||||
|
duration: val,
|
||||||
|
custom_dates: customDates,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
if (val === selectedDurationFilter) return;
|
||||||
<Tab.Panel key={tab.key} as="div" className="h-full flex flex-col" static>
|
|
||||||
<WidgetIssuesList
|
let newTab = selectedTab;
|
||||||
tab={tab.key}
|
// switch to pending tab if target date is changed to none
|
||||||
type="assigned"
|
if (val === "none" && selectedTab !== "completed") newTab = "pending";
|
||||||
workspaceSlug={workspaceSlug}
|
// switch to upcoming tab if target date is changed to other than none
|
||||||
widgetStats={widgetStats}
|
if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed")
|
||||||
isLoading={fetching}
|
newTab = "upcoming";
|
||||||
/>
|
|
||||||
</Tab.Panel>
|
handleUpdateFilters({
|
||||||
);
|
duration: val,
|
||||||
})}
|
tab: newTab,
|
||||||
</Tab.Panels>
|
});
|
||||||
</Tab.Group>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Tab.Group
|
||||||
|
as="div"
|
||||||
|
selectedIndex={selectedTabIndex}
|
||||||
|
onChange={(i) => {
|
||||||
|
const newSelectedTab = tabsList[i];
|
||||||
|
handleUpdateFilters({ tab: newSelectedTab?.key ?? "completed" });
|
||||||
|
}}
|
||||||
|
className="h-full flex flex-col"
|
||||||
|
>
|
||||||
|
<div className="px-6">
|
||||||
|
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||||
|
</div>
|
||||||
|
<Tab.Panels as="div" className="h-full">
|
||||||
|
{tabsList.map((tab) => {
|
||||||
|
if (tab.key !== selectedTab) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tab.Panel key={tab.key} as="div" className="h-full flex flex-col" static>
|
||||||
|
<WidgetIssuesList
|
||||||
|
tab={tab.key}
|
||||||
|
type="assigned"
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
|
widgetStats={widgetStats}
|
||||||
|
isLoading={fetching}
|
||||||
|
/>
|
||||||
|
</Tab.Panel>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tab.Panels>
|
||||||
|
</Tab.Group>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,7 @@ import { useDashboard } from "hooks/store";
|
|||||||
// components
|
// components
|
||||||
import {
|
import {
|
||||||
DurationFilterDropdown,
|
DurationFilterDropdown,
|
||||||
|
IssuesErrorState,
|
||||||
TabsList,
|
TabsList,
|
||||||
WidgetIssuesList,
|
WidgetIssuesList,
|
||||||
WidgetLoader,
|
WidgetLoader,
|
||||||
@ -26,10 +27,12 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||||||
// states
|
// states
|
||||||
const [fetching, setFetching] = useState(false);
|
const [fetching, setFetching] = useState(false);
|
||||||
// store hooks
|
// store hooks
|
||||||
const { fetchWidgetStats, getWidgetDetails, getWidgetStats, updateDashboardWidgetFilters } = useDashboard();
|
const { fetchWidgetStats, getWidgetDetails, getWidgetStats, getWidgetStatsError, updateDashboardWidgetFilters } =
|
||||||
|
useDashboard();
|
||||||
// derived values
|
// derived values
|
||||||
const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY);
|
const widgetDetails = getWidgetDetails(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
const widgetStats = getWidgetStats<TCreatedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
const widgetStats = getWidgetStats<TCreatedIssuesWidgetResponse>(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
|
const widgetStatsError = getWidgetStatsError(workspaceSlug, dashboardId, WIDGET_KEY);
|
||||||
const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE;
|
const selectedDurationFilter = widgetDetails?.widget_filters.duration ?? EDurationFilters.NONE;
|
||||||
const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab);
|
const selectedTab = getTabKey(selectedDurationFilter, widgetDetails?.widget_filters.tab);
|
||||||
const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? [];
|
const selectedCustomDates = widgetDetails?.widget_filters.custom_dates ?? [];
|
||||||
@ -70,74 +73,91 @@ export const CreatedIssuesWidget: React.FC<WidgetProps> = observer((props) => {
|
|||||||
const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST;
|
const tabsList = selectedDurationFilter === "none" ? UNFILTERED_ISSUES_TABS_LIST : FILTERED_ISSUES_TABS_LIST;
|
||||||
const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab);
|
const selectedTabIndex = tabsList.findIndex((tab) => tab.key === selectedTab);
|
||||||
|
|
||||||
if (!widgetDetails || !widgetStats) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
if ((!widgetDetails || !widgetStats) && !widgetStatsError) return <WidgetLoader widgetKey={WIDGET_KEY} />;
|
||||||
|
|
||||||
return (
|
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 min-h-96">
|
<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 min-h-96">
|
||||||
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
{widgetStatsError ? (
|
||||||
<Link
|
<IssuesErrorState
|
||||||
href={`/${workspaceSlug}/workspace-views/created/${filterParams}`}
|
isRefreshing={fetching}
|
||||||
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
onClick={() =>
|
||||||
>
|
|
||||||
Created by you
|
|
||||||
</Link>
|
|
||||||
<DurationFilterDropdown
|
|
||||||
customDates={selectedCustomDates}
|
|
||||||
value={selectedDurationFilter}
|
|
||||||
onChange={(val, customDates) => {
|
|
||||||
if (val === "custom" && customDates) {
|
|
||||||
handleUpdateFilters({
|
|
||||||
duration: val,
|
|
||||||
custom_dates: customDates,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val === selectedDurationFilter) return;
|
|
||||||
|
|
||||||
let newTab = selectedTab;
|
|
||||||
// switch to pending tab if target date is changed to none
|
|
||||||
if (val === "none" && selectedTab !== "completed") newTab = "pending";
|
|
||||||
// switch to upcoming tab if target date is changed to other than none
|
|
||||||
if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed") newTab = "upcoming";
|
|
||||||
|
|
||||||
handleUpdateFilters({
|
handleUpdateFilters({
|
||||||
duration: val,
|
duration: EDurationFilters.NONE,
|
||||||
tab: newTab,
|
tab: "pending",
|
||||||
});
|
})
|
||||||
}}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : (
|
||||||
<Tab.Group
|
widgetStats && (
|
||||||
as="div"
|
<>
|
||||||
selectedIndex={selectedTabIndex}
|
<div className="flex items-center justify-between gap-2 p-6 pl-7">
|
||||||
onChange={(i) => {
|
<Link
|
||||||
const newSelectedTab = tabsList[i];
|
href={`/${workspaceSlug}/workspace-views/created/${filterParams}`}
|
||||||
handleUpdateFilters({ tab: newSelectedTab.key ?? "completed" });
|
className="text-lg font-semibold text-custom-text-300 hover:underline"
|
||||||
}}
|
>
|
||||||
className="h-full flex flex-col"
|
Created by you
|
||||||
>
|
</Link>
|
||||||
<div className="px-6">
|
<DurationFilterDropdown
|
||||||
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
customDates={selectedCustomDates}
|
||||||
</div>
|
value={selectedDurationFilter}
|
||||||
<Tab.Panels as="div" className="h-full">
|
onChange={(val, customDates) => {
|
||||||
{tabsList.map((tab) => {
|
if (val === "custom" && customDates) {
|
||||||
if (tab.key !== selectedTab) return null;
|
handleUpdateFilters({
|
||||||
|
duration: val,
|
||||||
|
custom_dates: customDates,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
if (val === selectedDurationFilter) return;
|
||||||
<Tab.Panel key={tab.key} as="div" className="h-full flex flex-col" static>
|
|
||||||
<WidgetIssuesList
|
let newTab = selectedTab;
|
||||||
tab={tab.key}
|
// switch to pending tab if target date is changed to none
|
||||||
type="created"
|
if (val === "none" && selectedTab !== "completed") newTab = "pending";
|
||||||
workspaceSlug={workspaceSlug}
|
// switch to upcoming tab if target date is changed to other than none
|
||||||
widgetStats={widgetStats}
|
if (val !== "none" && selectedDurationFilter === "none" && selectedTab !== "completed")
|
||||||
isLoading={fetching}
|
newTab = "upcoming";
|
||||||
/>
|
|
||||||
</Tab.Panel>
|
handleUpdateFilters({
|
||||||
);
|
duration: val,
|
||||||
})}
|
tab: newTab,
|
||||||
</Tab.Panels>
|
});
|
||||||
</Tab.Group>
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Tab.Group
|
||||||
|
as="div"
|
||||||
|
selectedIndex={selectedTabIndex}
|
||||||
|
onChange={(i) => {
|
||||||
|
const newSelectedTab = tabsList[i];
|
||||||
|
handleUpdateFilters({ tab: newSelectedTab.key ?? "completed" });
|
||||||
|
}}
|
||||||
|
className="h-full flex flex-col"
|
||||||
|
>
|
||||||
|
<div className="px-6">
|
||||||
|
<TabsList durationFilter={selectedDurationFilter} selectedTab={selectedTab} />
|
||||||
|
</div>
|
||||||
|
<Tab.Panels as="div" className="h-full">
|
||||||
|
{tabsList.map((tab) => {
|
||||||
|
if (tab.key !== selectedTab) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tab.Panel key={tab.key} as="div" className="h-full flex flex-col" static>
|
||||||
|
<WidgetIssuesList
|
||||||
|
tab={tab.key}
|
||||||
|
type="created"
|
||||||
|
workspaceSlug={workspaceSlug}
|
||||||
|
widgetStats={widgetStats}
|
||||||
|
isLoading={fetching}
|
||||||
|
/>
|
||||||
|
</Tab.Panel>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Tab.Panels>
|
||||||
|
</Tab.Group>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
1
web/components/dashboard/widgets/error-states/index.ts
Normal file
1
web/components/dashboard/widgets/error-states/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from "./issues";
|
32
web/components/dashboard/widgets/error-states/issues.tsx
Normal file
32
web/components/dashboard/widgets/error-states/issues.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { AlertTriangle, RefreshCcw } from "lucide-react";
|
||||||
|
// ui
|
||||||
|
import { Button } from "@plane/ui";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isRefreshing: boolean;
|
||||||
|
onClick: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IssuesErrorState: React.FC<Props> = (props) => {
|
||||||
|
const { isRefreshing, onClick } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full w-full grid place-items-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="h-24 w-24 bg-red-500/20 rounded-full grid place-items-center mx-auto">
|
||||||
|
<AlertTriangle className="h-12 w-12 text-red-500" />
|
||||||
|
</div>
|
||||||
|
<p className="mt-7 text-custom-text-300 text-sm font-medium">There was an error in fetching widget details</p>
|
||||||
|
<Button
|
||||||
|
variant="neutral-primary"
|
||||||
|
prependIcon={<RefreshCcw className="h-3 w-3" />}
|
||||||
|
className="mt-2 mx-auto"
|
||||||
|
onClick={onClick}
|
||||||
|
loading={isRefreshing}
|
||||||
|
>
|
||||||
|
{isRefreshing ? "Retrying" : "Retry"}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
export * from "./dropdowns";
|
export * from "./dropdowns";
|
||||||
export * from "./empty-states";
|
export * from "./empty-states";
|
||||||
|
export * from "./error-states";
|
||||||
export * from "./issue-panels";
|
export * from "./issue-panels";
|
||||||
export * from "./loaders";
|
export * from "./loaders";
|
||||||
export * from "./assigned-issues";
|
export * from "./assigned-issues";
|
||||||
|
@ -15,6 +15,8 @@ import {
|
|||||||
} from "@plane/types";
|
} from "@plane/types";
|
||||||
|
|
||||||
export interface IDashboardStore {
|
export interface IDashboardStore {
|
||||||
|
// error states
|
||||||
|
widgetStatsError: { [workspaceSlug: string]: Record<string, Record<TWidgetKeys, any | null>> };
|
||||||
// observables
|
// observables
|
||||||
homeDashboardId: string | null;
|
homeDashboardId: string | null;
|
||||||
widgetDetails: { [workspaceSlug: string]: Record<string, TWidget[]> };
|
widgetDetails: { [workspaceSlug: string]: Record<string, TWidget[]> };
|
||||||
@ -36,6 +38,7 @@ export interface IDashboardStore {
|
|||||||
// computed actions
|
// computed actions
|
||||||
getWidgetDetails: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => TWidget | undefined;
|
getWidgetDetails: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => TWidget | undefined;
|
||||||
getWidgetStats: <T>(workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => T | undefined;
|
getWidgetStats: <T>(workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => T | undefined;
|
||||||
|
getWidgetStatsError: (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => any | null;
|
||||||
// actions
|
// actions
|
||||||
fetchHomeDashboardWidgets: (workspaceSlug: string) => Promise<THomeDashboardResponse>;
|
fetchHomeDashboardWidgets: (workspaceSlug: string) => Promise<THomeDashboardResponse>;
|
||||||
fetchWidgetStats: (
|
fetchWidgetStats: (
|
||||||
@ -58,6 +61,8 @@ export interface IDashboardStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardStore implements IDashboardStore {
|
export class DashboardStore implements IDashboardStore {
|
||||||
|
// error states
|
||||||
|
widgetStatsError: { [workspaceSlug: string]: Record<string, Record<TWidgetKeys, any>> } = {};
|
||||||
// observables
|
// observables
|
||||||
homeDashboardId: string | null = null;
|
homeDashboardId: string | null = null;
|
||||||
widgetDetails: { [workspaceSlug: string]: Record<string, TWidget[]> } = {};
|
widgetDetails: { [workspaceSlug: string]: Record<string, TWidget[]> } = {};
|
||||||
@ -70,6 +75,8 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
constructor(_rootStore: RootStore) {
|
constructor(_rootStore: RootStore) {
|
||||||
makeObservable(this, {
|
makeObservable(this, {
|
||||||
|
// error states
|
||||||
|
widgetStatsError: observable,
|
||||||
// observables
|
// observables
|
||||||
homeDashboardId: observable.ref,
|
homeDashboardId: observable.ref,
|
||||||
widgetDetails: observable,
|
widgetDetails: observable,
|
||||||
@ -93,7 +100,7 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get home dashboard widgets
|
* @description get home dashboard widgets
|
||||||
* @returns home dashboard widgets
|
* @returns {TWidget[] | undefined}
|
||||||
*/
|
*/
|
||||||
get homeDashboardWidgets() {
|
get homeDashboardWidgets() {
|
||||||
const workspaceSlug = this.routerStore.workspaceSlug;
|
const workspaceSlug = this.routerStore.workspaceSlug;
|
||||||
@ -104,10 +111,10 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get widget details
|
* @description get widget details
|
||||||
* @param workspaceSlug
|
* @param {string} workspaceSlug
|
||||||
* @param dashboardId
|
* @param {string} dashboardId
|
||||||
* @param widgetId
|
* @param {TWidgetKeys} widgetKey
|
||||||
* @returns widget details
|
* @returns {TWidget | undefined}
|
||||||
*/
|
*/
|
||||||
getWidgetDetails = computedFn((workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => {
|
getWidgetDetails = computedFn((workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) => {
|
||||||
const widgets = this.widgetDetails?.[workspaceSlug]?.[dashboardId];
|
const widgets = this.widgetDetails?.[workspaceSlug]?.[dashboardId];
|
||||||
@ -117,20 +124,30 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description get widget stats
|
* @description get widget stats
|
||||||
* @param workspaceSlug
|
* @param {string} workspaceSlug
|
||||||
* @param dashboardId
|
* @param {string} dashboardId
|
||||||
* @param widgetKey
|
* @param {TWidgetKeys} widgetKey
|
||||||
* @returns widget stats
|
* @returns {T | undefined}
|
||||||
*/
|
*/
|
||||||
getWidgetStats = <T>(workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys): T | undefined =>
|
getWidgetStats = <T>(workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys): T | undefined =>
|
||||||
(this.widgetStats?.[workspaceSlug]?.[dashboardId]?.[widgetKey] as unknown as T) ?? undefined;
|
(this.widgetStats?.[workspaceSlug]?.[dashboardId]?.[widgetKey] as unknown as T) ?? undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch home dashboard details and widgets
|
* @description get widget stats error
|
||||||
* @param workspaceSlug
|
* @param {string} workspaceSlug
|
||||||
* @returns home dashboard response
|
* @param {string} dashboardId
|
||||||
|
* @param {TWidgetKeys} widgetKey
|
||||||
|
* @returns {any | null}
|
||||||
*/
|
*/
|
||||||
fetchHomeDashboardWidgets = async (workspaceSlug: string) => {
|
getWidgetStatsError = (workspaceSlug: string, dashboardId: string, widgetKey: TWidgetKeys) =>
|
||||||
|
this.widgetStatsError?.[workspaceSlug]?.[dashboardId]?.[widgetKey] ?? null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description fetch home dashboard details and widgets
|
||||||
|
* @param {string} workspaceSlug
|
||||||
|
* @returns {Promise<THomeDashboardResponse>}
|
||||||
|
*/
|
||||||
|
fetchHomeDashboardWidgets = async (workspaceSlug: string): Promise<THomeDashboardResponse> => {
|
||||||
try {
|
try {
|
||||||
const response = await this.dashboardService.getHomeDashboardWidgets(workspaceSlug);
|
const response = await this.dashboardService.getHomeDashboardWidgets(workspaceSlug);
|
||||||
|
|
||||||
@ -151,27 +168,36 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description fetch widget stats
|
* @description fetch widget stats
|
||||||
* @param workspaceSlug
|
* @param {string} workspaceSlug
|
||||||
* @param dashboardId
|
* @param {string} dashboardId
|
||||||
* @param widgetKey
|
* @param {TWidgetStatsRequestParams} widgetKey
|
||||||
* @returns widget stats
|
* @returns widget stats
|
||||||
*/
|
*/
|
||||||
fetchWidgetStats = async (workspaceSlug: string, dashboardId: string, params: TWidgetStatsRequestParams) =>
|
fetchWidgetStats = async (workspaceSlug: string, dashboardId: string, params: TWidgetStatsRequestParams) =>
|
||||||
this.dashboardService.getWidgetStats(workspaceSlug, dashboardId, params).then((res) => {
|
this.dashboardService
|
||||||
runInAction(() => {
|
.getWidgetStats(workspaceSlug, dashboardId, params)
|
||||||
// @ts-ignore
|
.then((res) => {
|
||||||
if (res.issues) this.issueStore.addIssue(res.issues);
|
runInAction(() => {
|
||||||
set(this.widgetStats, [workspaceSlug, dashboardId, params.widget_key], res);
|
// @ts-ignore
|
||||||
});
|
if (res.issues) this.issueStore.addIssue(res.issues);
|
||||||
|
set(this.widgetStats, [workspaceSlug, dashboardId, params.widget_key], res);
|
||||||
|
set(this.widgetStatsError, [workspaceSlug, dashboardId, params.widget_key], null);
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
runInAction(() => {
|
||||||
|
set(this.widgetStatsError, [workspaceSlug, dashboardId, params.widget_key], error);
|
||||||
|
});
|
||||||
|
|
||||||
return res;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @description update dashboard widget
|
* @description update dashboard widget
|
||||||
* @param dashboardId
|
* @param {string} dashboardId
|
||||||
* @param widgetId
|
* @param {string} widgetId
|
||||||
* @param data
|
* @param {Partial<TWidget>} data
|
||||||
* @returns updated widget
|
* @returns updated widget
|
||||||
*/
|
*/
|
||||||
updateDashboardWidget = async (
|
updateDashboardWidget = async (
|
||||||
@ -209,9 +235,9 @@ export class DashboardStore implements IDashboardStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @description update dashboard widget filters
|
* @description update dashboard widget filters
|
||||||
* @param dashboardId
|
* @param {string} dashboardId
|
||||||
* @param widgetId
|
* @param {string} widgetId
|
||||||
* @param data
|
* @param {TWidgetFiltersFormData} data
|
||||||
* @returns updated widget
|
* @returns updated widget
|
||||||
*/
|
*/
|
||||||
updateDashboardWidgetFilters = async (
|
updateDashboardWidgetFilters = async (
|
||||||
|
Loading…
Reference in New Issue
Block a user