forked from github/plane
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:
parent
fd5326dec6
commit
2986769f28
@ -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";
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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")}
|
||||
|
@ -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")}
|
||||
|
@ -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>
|
||||
))}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
@ -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)
|
||||
|
@ -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>
|
||||
);
|
||||
});
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user