mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
198 lines
6.9 KiB
TypeScript
198 lines
6.9 KiB
TypeScript
import { useEffect } from "react";
|
|
import { observer } from "mobx-react-lite";
|
|
import { useRouter } from "next/router";
|
|
import { mutate } from "swr";
|
|
// services
|
|
// hooks
|
|
// components
|
|
// ui
|
|
import { CalendarDays, Download, RefreshCw } from "lucide-react";
|
|
import { IAnalyticsParams, IAnalyticsResponse, IExportAnalyticsFormData, IWorkspace } from "@plane/types";
|
|
import { Button, LayersIcon, TOAST_TYPE, setToast } from "@plane/ui";
|
|
// icons
|
|
import { CustomAnalyticsSidebarHeader, CustomAnalyticsSidebarProjectsList } from "@/components/analytics";
|
|
// helpers
|
|
// types
|
|
// fetch-keys
|
|
import { ANALYTICS } from "@/constants/fetch-keys";
|
|
import { cn } from "@/helpers/common.helper";
|
|
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
|
import { useCycle, useModule, useProject, useUser, useWorkspace } from "@/hooks/store";
|
|
import { AnalyticsService } from "@/services/analytics.service";
|
|
|
|
type Props = {
|
|
analytics: IAnalyticsResponse | undefined;
|
|
params: IAnalyticsParams;
|
|
isProjectLevel: boolean;
|
|
};
|
|
|
|
const analyticsService = new AnalyticsService();
|
|
|
|
export const CustomAnalyticsSidebar: React.FC<Props> = observer((props) => {
|
|
const { analytics, params, isProjectLevel = false } = props;
|
|
// router
|
|
const router = useRouter();
|
|
const { workspaceSlug, projectId, cycleId, moduleId } = router.query;
|
|
// store hooks
|
|
const { currentUser } = useUser();
|
|
const { workspaceProjectIds, getProjectById } = useProject();
|
|
const { getWorkspaceById } = useWorkspace();
|
|
|
|
const { fetchCycleDetails, getCycleById } = useCycle();
|
|
const { fetchModuleDetails, getModuleById } = useModule();
|
|
|
|
const projectDetails = projectId ? getProjectById(projectId.toString()) ?? undefined : undefined;
|
|
|
|
const trackExportAnalytics = () => {
|
|
if (!currentUser) return;
|
|
|
|
const eventPayload: any = {
|
|
workspaceSlug: workspaceSlug?.toString(),
|
|
params: {
|
|
x_axis: params.x_axis,
|
|
y_axis: params.y_axis,
|
|
group: params.segment,
|
|
project: params.project,
|
|
},
|
|
};
|
|
|
|
if (projectDetails) {
|
|
const workspaceDetails = projectDetails.workspace as IWorkspace;
|
|
|
|
eventPayload.workspaceId = workspaceDetails.id;
|
|
eventPayload.workspaceName = workspaceDetails.name;
|
|
eventPayload.projectId = projectDetails.id;
|
|
eventPayload.projectIdentifier = projectDetails.identifier;
|
|
eventPayload.projectName = projectDetails.name;
|
|
}
|
|
|
|
if (cycleDetails || moduleDetails) {
|
|
const details = cycleDetails || moduleDetails;
|
|
|
|
const currentProjectDetails = getProjectById(details?.project_id || "");
|
|
const currentWorkspaceDetails = getWorkspaceById(details?.workspace_id || "");
|
|
|
|
eventPayload.workspaceId = details?.workspace_id;
|
|
eventPayload.workspaceName = currentWorkspaceDetails?.name;
|
|
eventPayload.projectId = details?.project_id;
|
|
eventPayload.projectIdentifier = currentProjectDetails?.identifier;
|
|
eventPayload.projectName = currentProjectDetails?.name;
|
|
}
|
|
|
|
if (cycleDetails) {
|
|
eventPayload.cycleId = cycleDetails.id;
|
|
eventPayload.cycleName = cycleDetails.name;
|
|
}
|
|
|
|
if (moduleDetails) {
|
|
eventPayload.moduleId = moduleDetails.id;
|
|
eventPayload.moduleName = moduleDetails.name;
|
|
}
|
|
};
|
|
|
|
const exportAnalytics = () => {
|
|
if (!workspaceSlug) return;
|
|
|
|
const data: IExportAnalyticsFormData = {
|
|
x_axis: params.x_axis,
|
|
y_axis: params.y_axis,
|
|
};
|
|
|
|
if (params.segment) data.segment = params.segment;
|
|
if (params.project) data.project = params.project;
|
|
|
|
analyticsService
|
|
.exportAnalytics(workspaceSlug.toString(), data)
|
|
.then((res) => {
|
|
setToast({
|
|
type: TOAST_TYPE.SUCCESS,
|
|
title: "Success!",
|
|
message: res.message,
|
|
});
|
|
|
|
trackExportAnalytics();
|
|
})
|
|
.catch(() =>
|
|
setToast({
|
|
type: TOAST_TYPE.ERROR,
|
|
title: "Error!",
|
|
message: "There was some error in exporting the analytics. Please try again.",
|
|
})
|
|
);
|
|
};
|
|
|
|
const cycleDetails = cycleId ? getCycleById(cycleId.toString()) : undefined;
|
|
const moduleDetails = moduleId ? getModuleById(moduleId.toString()) : undefined;
|
|
|
|
// fetch cycle details
|
|
useEffect(() => {
|
|
if (!workspaceSlug || !projectId || !cycleId || cycleDetails) return;
|
|
|
|
fetchCycleDetails(workspaceSlug.toString(), projectId.toString(), cycleId.toString());
|
|
}, [cycleId, cycleDetails, fetchCycleDetails, projectId, workspaceSlug]);
|
|
|
|
// fetch module details
|
|
useEffect(() => {
|
|
if (!workspaceSlug || !projectId || !moduleId || moduleDetails) return;
|
|
|
|
fetchModuleDetails(workspaceSlug.toString(), projectId.toString(), moduleId.toString());
|
|
}, [moduleId, moduleDetails, fetchModuleDetails, projectId, workspaceSlug]);
|
|
|
|
const selectedProjects = params.project && params.project.length > 0 ? params.project : workspaceProjectIds;
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative flex h-full w-full items-start justify-between gap-2 bg-custom-sidebar-background-100 px-5 py-4",
|
|
!isProjectLevel ? "flex-col" : ""
|
|
)}
|
|
>
|
|
<div className="flex flex-wrap items-center gap-2">
|
|
<div className="flex items-center gap-1 rounded-md bg-custom-background-80 px-3 py-1 text-xs text-custom-text-200">
|
|
<LayersIcon height={14} width={14} />
|
|
{analytics ? analytics.total : "..."}
|
|
<div className={cn(isProjectLevel ? "hidden md:block" : "")}>Issues</div>
|
|
</div>
|
|
{isProjectLevel && (
|
|
<div className="flex items-center gap-1 rounded-md bg-custom-background-80 px-3 py-1 text-xs text-custom-text-200">
|
|
<CalendarDays className="h-3.5 w-3.5" />
|
|
{renderFormattedDate(
|
|
(cycleId
|
|
? cycleDetails?.created_at
|
|
: moduleId
|
|
? moduleDetails?.created_at
|
|
: projectDetails?.created_at) ?? ""
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className={cn("h-full w-full overflow-hidden", isProjectLevel ? "hidden" : "block")}>
|
|
<>
|
|
{!isProjectLevel && selectedProjects && selectedProjects.length > 0 && (
|
|
<CustomAnalyticsSidebarProjectsList projectIds={selectedProjects} />
|
|
)}
|
|
<CustomAnalyticsSidebarHeader />
|
|
</>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center justify-end gap-2">
|
|
<Button
|
|
variant="neutral-primary"
|
|
prependIcon={<RefreshCw className="h-3 w-3 md:h-3.5 md:w-3.5" />}
|
|
onClick={() => {
|
|
if (!workspaceSlug) return;
|
|
|
|
mutate(ANALYTICS(workspaceSlug.toString(), params));
|
|
}}
|
|
>
|
|
<div className={cn(isProjectLevel ? "hidden md:block" : "")}>Refresh</div>
|
|
</Button>
|
|
<Button variant="primary" prependIcon={<Download className="h-3.5 w-3.5" />} onClick={exportAnalytics}>
|
|
<div className={cn(isProjectLevel ? "hidden md:block" : "")}>Export as CSV</div>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
});
|