plane/web/components/analytics/custom-analytics/sidebar/sidebar.tsx
2024-03-06 20:50:38 +05:30

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 { 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";
import { IAnalyticsParams, IAnalyticsResponse, IExportAnalyticsFormData, IWorkspace } from "@plane/types";
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>
);
});