mirror of
https://github.com/makeplane/plane.git
synced 2026-07-14 14:31:37 +02:00
* chore: added code split for the analytics store * chore: done some refactor * refactor: update entity keys in analytics and translations * chore: updated the translations * refactor: simplify AnalyticsStoreV2 class by removing unnecessary constructor * feat: add AnalyticsStoreV2 class and interface for enhanced analytics functionality * feat: enhance WorkItemsModal and analytics store with isEpic functionality * feat: integrate isEpic state into TotalInsights and WorkItemsModal components * refactor: remove isEpic state from WorkItemsModalMainContent component * refactor: removed old analytics components and related services * refactor: new analytics * refactor: removed all nivo chart dependencies * chore: resolved coderabbit comments * fix: remove duplicate isEpic prop in WorkItemsModal component * fix: update type for tab parameter in processUrl method of AnalyticsV2Service * fix: update processUrl to handle custom-work-items in peek view * feat: implement CSV export functionality in InsightTable component * feat: add user analytics components and extend analytics functionality * feat: enhance analytics service with filter parameters and improve data handling in InsightTable * feat: add projects analytics components * feat: add cycles, modules, and intake analytics components with corresponding data handling and visualization * feat: add new translation keys for various statuses across multiple languages * chore: update analytics components and translations * [WEB-4246] fix: enhance analytics components to include 'isEpic' parameter for improved data fetching * refactor: clean up imports and update translation keys in analytics components * chore: added project logo props in charts * feat: integrate project logo display in analytics tables * chore: added feature flag * chore: disabled users * chore: added feature flag * chore: added payment failure vaidation * feat: enhance analytics page with subscription upgrade tooltip and update translations * updated translations * chore: added additional parameters in payload * added state icons in module and cycle table * fix: handle empty date values in cycles and modules insight tables * chore: added the keys for module * chore: added custom tooltip for cycles and modules * chore: added color to the scatter points * refactor: centralize module status colors and update chart point colors and yaxis domain * fix: update comment to clarify display name usage in WorkItemInsightColumns * feat: add completed work items count to analytics and update chart components * refactor: update trend-piece component to use new status variants for analytics * feat: enhance ee analytics tables with export functionality and added project logo in intake * refactor: clean up fetch keys and simplify analytics component structure * refactor: rename analytics fields constants for consistency and clarity * refactor: update analytics feature flag for improved clarity and consistency * chore: changed the feature flag of analytics * refactor: streamline analytics tab structure and introduce locked tab labels for feature flag check while rendering tab label * chore: remove unused translation key for entity count in English locale * fix: import of intake * fix: append percentage sign to completion percentage label in cycles insight table * refactor: simplify completion percentage calculation in ModulesCyclesTooltip and enhance Avatar component styling in UserAvatarName * refactor: update AnalyticsWrapper titles to use i18n keys and clean up imports in analytics components * fix: update isEpic handling in WorkItemsModal to consider isPeekView state * fix: update i18n title in AnalyticsWrapper for Intake component --------- Co-authored-by: NarayanBavisetti <narayan3119@gmail.com> Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
162 lines
6.0 KiB
TypeScript
162 lines
6.0 KiB
TypeScript
import React, { useCallback, useMemo } from "react";
|
|
import { observer } from "mobx-react";
|
|
import { useParams } from "next/navigation";
|
|
import useSWR from "swr";
|
|
import { MODULE_STATUS, MODULE_STATUS_COLORS } from "@plane/constants";
|
|
import { useTranslation } from "@plane/i18n";
|
|
import { ScatterChart } from "@plane/propel/charts/scatter-chart";
|
|
import { IChartResponse, TChartData, TModuleStatus, TScatterPointItem } from "@plane/types";
|
|
import { IModuleProgressData } from "@plane/types/src/analytics-extended";
|
|
import { renderFormattedDate } from "@plane/utils";
|
|
import AnalyticsSectionWrapper from "@/components/analytics/analytics-section-wrapper";
|
|
import AnalyticsEmptyState from "@/components/analytics/empty-state";
|
|
import { ChartLoader } from "@/components/analytics/loaders";
|
|
import { useAnalytics } from "@/hooks/store/use-analytics";
|
|
import { useResolvedAssetPath } from "@/hooks/use-resolved-asset-path";
|
|
import { AnalyticsService } from "@/services/analytics.service";
|
|
import ModulesCyclesTooltip, { ICycleModuleTooltipProps } from "../modules-cycles-tooltip";
|
|
|
|
const analyticsService = new AnalyticsService();
|
|
const ModuleProgress = observer(() => {
|
|
const params = useParams();
|
|
const { t } = useTranslation();
|
|
const workspaceSlug = params.workspaceSlug.toString();
|
|
const { selectedDuration, selectedDurationLabel, selectedProjects, selectedCycle, selectedModule, isPeekView } =
|
|
useAnalytics();
|
|
const resolvedPath = useResolvedAssetPath({ basePath: "/empty-state/analytics/empty-chart-radar" });
|
|
|
|
const { data: moduleInsightsData, isLoading: isLoadingModuleInsight } = useSWR(
|
|
`radar-chart-module-progress-${workspaceSlug}-${selectedDuration}-${selectedProjects}-${selectedCycle}-${selectedModule}-${isPeekView}`,
|
|
() =>
|
|
analyticsService.getAdvanceAnalyticsCharts<IChartResponse>(
|
|
workspaceSlug,
|
|
"modules",
|
|
{
|
|
// date_filter: selectedDuration,
|
|
...(selectedProjects?.length > 0 && { project_ids: selectedProjects?.join(",") }),
|
|
...(selectedCycle ? { cycle_id: selectedCycle } : {}),
|
|
...(selectedModule ? { module_id: selectedModule } : {}),
|
|
},
|
|
isPeekView
|
|
)
|
|
);
|
|
|
|
const parsedData: TChartData<string, string>[] = useMemo(() => {
|
|
if (!moduleInsightsData?.data) return [];
|
|
return moduleInsightsData.data.map((datum) => ({
|
|
...datum,
|
|
[datum.key]: datum.count,
|
|
name: renderFormattedDate(datum.key) ?? datum.key,
|
|
}));
|
|
}, [moduleInsightsData]);
|
|
|
|
const scatterPoints: TScatterPointItem<string>[] = useMemo(() => {
|
|
if (!parsedData) return [];
|
|
return parsedData.map((datum) => ({
|
|
key: datum.key,
|
|
label: datum.name,
|
|
x: datum.key,
|
|
y: datum.count,
|
|
fill: MODULE_STATUS_COLORS[datum.status.toLowerCase() as TModuleStatus],
|
|
stroke: MODULE_STATUS_COLORS[datum.status.toLowerCase() as TModuleStatus],
|
|
}));
|
|
}, [parsedData]);
|
|
|
|
const tooltipRows = useCallback(
|
|
(data: IModuleProgressData) => [
|
|
{
|
|
label: t("workspace_analytics.total", {
|
|
entity: t("common.work_items"),
|
|
}),
|
|
value: data.total_issues,
|
|
},
|
|
{
|
|
label: t("workspace_projects.state.completed"),
|
|
value: data.completed_issues,
|
|
},
|
|
{
|
|
label: t("workspace_projects.state.started"),
|
|
value: data.started_issues,
|
|
},
|
|
{
|
|
label: t("workspace_projects.state.unstarted"),
|
|
value: data.unstarted_issues,
|
|
},
|
|
{
|
|
label: t("workspace_projects.state.cancelled"),
|
|
value: data.cancelled_issues,
|
|
},
|
|
],
|
|
[t]
|
|
);
|
|
|
|
return (
|
|
<AnalyticsSectionWrapper title="Module Progress" subtitle={selectedDurationLabel} className="col-span-1 ">
|
|
{isLoadingModuleInsight ? (
|
|
<ChartLoader />
|
|
) : moduleInsightsData && moduleInsightsData?.data?.length > 0 ? (
|
|
<div className="h-[350px] flex flex-col gap-4">
|
|
<ScatterChart
|
|
data={parsedData}
|
|
scatterPoints={scatterPoints}
|
|
className="h-full"
|
|
xAxis={{
|
|
key: "name",
|
|
label: t("common.modules"),
|
|
}}
|
|
yAxis={{
|
|
key: "count",
|
|
label: t("common.completion") + " " + "%",
|
|
offset: -30,
|
|
dx: -25,
|
|
domain: [0, 100],
|
|
}}
|
|
tickCount={{
|
|
y: 100,
|
|
}}
|
|
margin={{
|
|
top: 20,
|
|
right: 20,
|
|
bottom: 35,
|
|
left: 20,
|
|
}}
|
|
showTooltip
|
|
customTooltipContent={({ active, label, payload }) => {
|
|
if (!active) return null;
|
|
const data: IModuleProgressData = payload[0]?.payload as IModuleProgressData;
|
|
const tooltipProps: ICycleModuleTooltipProps = {
|
|
title: data.name,
|
|
startDate: data.start_date,
|
|
endDate: data.target_date,
|
|
rows: tooltipRows(data),
|
|
totalCount: data.total_issues,
|
|
completedCount: data.completed_issues,
|
|
};
|
|
return <ModulesCyclesTooltip {...tooltipProps} />;
|
|
}}
|
|
/>
|
|
<div className="flex gap-4 pl-12">
|
|
{Object.entries(MODULE_STATUS_COLORS).map(([key, value]) => (
|
|
<div key={key} className="flex items-center gap-2 ">
|
|
<div className="w-2 h-2 rounded-full" style={{ backgroundColor: value }} />
|
|
<span className="text-sm opacity-50">
|
|
{t(MODULE_STATUS.find((status) => status.value === key)?.i18n_label || "")}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : (
|
|
<AnalyticsEmptyState
|
|
title={t("workspace_analytics.empty_state.module_progress.title")}
|
|
description={t("workspace_analytics.empty_state.module_progress.description")}
|
|
className="h-[350px]"
|
|
assetPath={resolvedPath}
|
|
/>
|
|
)}
|
|
</AnalyticsSectionWrapper>
|
|
);
|
|
});
|
|
|
|
export default ModuleProgress;
|