snapshot of https://github.com/makeplane/plane.git @ d868349a55de79979eb8b98701bf1702c67ad634 (2026-09-15)
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 16s
CodeQL / Analyze (python) (push) Failing after 3s
i18n sync check / check:sync (push) Failing after 3s
React Doctor / react-doctor (push) Failing after 16s
Branch Build CE / Build-Push Admin Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Admin Manifest (push) Has been cancelled
Branch Build CE / Build-Push Web Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Web Manifest (push) Has been cancelled
Branch Build CE / Build-Push Space Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Proxy Manifest (push) Has been cancelled
Branch Build CE / Merge Space Manifest (push) Has been cancelled
Branch Build CE / Prepare AIO Assets (push) Has been cancelled
Branch Build CE / Build Setup (push) Has been cancelled
Branch Build CE / Build-Push Live Collaboration Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge Live Collaboration Manifest (push) Has been cancelled
Branch Build CE / Build-Push API Server Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge API Server Manifest (push) Has been cancelled
Branch Build CE / Build-Push Proxy Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Build-Push AIO Docker Image (${{ matrix.arch }}) (push) Has been cancelled
Branch Build CE / Merge AIO Manifest (push) Has been cancelled
Branch Build CE / Upload Build Assets (push) Has been cancelled
Branch Build CE / Build Release (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:14:58 +05:00
commit 23059a1de3
5252 changed files with 573834 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
// plane package imports
import { Logo } from "@plane/propel/emoji-icon-picker";
import { ProjectsOutline } from "@makeplane/propel/icons";
import { Tooltip } from "@makeplane/propel/components/tooltip";
import { cn } from "@plane/utils";
// plane web hooks
import { useProject } from "@/hooks/store/use-project";
type Props = {
project: {
id: string;
completed_issues?: number;
total_issues?: number;
};
isLoading?: boolean;
};
function CompletionPercentage({ percentage }: { percentage: number }) {
const percentageColor =
percentage > 50 ? "bg-success-subtle text-success-primary" : "bg-danger-subtle text-danger-primary";
return (
<div className={cn("flex items-center gap-2 rounded-sm p-1 text-11", percentageColor)}>
<span>{percentage}%</span>
</div>
);
}
function ActiveProjectItem(props: Props) {
const { project } = props;
const { getProjectById } = useProject();
const { id, completed_issues, total_issues } = project;
const projectDetails = getProjectById(id);
if (!projectDetails) return null;
return (
<div className="flex w-full items-center justify-between gap-2">
<div className="flex flex-1 items-center gap-2 overflow-hidden">
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-xl bg-layer-1">
<span className="grid h-4 w-4 flex-shrink-0 place-items-center">
{projectDetails?.logo_props ? (
<Logo logo={projectDetails?.logo_props} size={16} />
) : (
<span className="grid h-4 w-4 flex-shrink-0 place-items-center">
<ProjectsOutline className="h-4 w-4" />
</span>
)}
</span>
</div>
<Tooltip label={projectDetails?.name} layout="stacked" align="start">
<p className="truncate text-13 font-medium">{projectDetails?.name}</p>
</Tooltip>
</div>
<CompletionPercentage
percentage={completed_issues && total_issues ? Math.round((completed_issues / total_issues) * 100) : 0}
/>
</div>
);
}
export default ActiveProjectItem;

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane package imports
import { useTranslation } from "@plane/i18n";
import { Loader } from "@plane/ui";
// plane web hooks
import { useAnalytics } from "@/hooks/store/use-analytics";
import { useProject } from "@/hooks/store/use-project";
// plane web components
import AnalyticsSectionWrapper from "../analytics-section-wrapper";
import ActiveProjectItem from "./active-project-item";
const ActiveProjects = observer(function ActiveProjects() {
const { t } = useTranslation();
const { fetchProjectAnalyticsCount } = useProject();
const { workspaceSlug } = useParams();
const { selectedDurationLabel } = useAnalytics();
const { data: projectAnalyticsCount, isLoading: isProjectAnalyticsCountLoading } = useSWR(
workspaceSlug ? ["projectAnalyticsCount", workspaceSlug] : null,
workspaceSlug
? () =>
fetchProjectAnalyticsCount(workspaceSlug.toString(), {
fields: "total_work_items,total_completed_work_items",
})
: null
);
return (
<AnalyticsSectionWrapper
title={`${t("workspace_analytics.active_projects")}`}
subtitle={selectedDurationLabel}
className="md:col-span-2"
>
<div className="flex h-[350px] flex-col gap-4 overflow-auto">
{isProjectAnalyticsCountLoading &&
Array.from({ length: 5 }).map((_, index) => <Loader.Item key={index} height="40px" width="100%" />)}
{!isProjectAnalyticsCountLoading &&
projectAnalyticsCount?.map((project) => <ActiveProjectItem key={project.id} project={project} />)}
</div>
</AnalyticsSectionWrapper>
);
});
export default ActiveProjects;

View File

@@ -0,0 +1,7 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
export * from "./root";

View File

@@ -0,0 +1,123 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { lazy, Suspense } from "react";
import { observer } from "mobx-react";
import { useParams } from "next/navigation";
import useSWR from "swr";
// plane package imports
import { useTranslation } from "@plane/i18n";
import { EmptyStateCompact } from "@plane/propel/empty-state";
import type { TChartData } from "@plane/types";
// hooks
import { useAnalytics } from "@/hooks/store/use-analytics";
// services
import { AnalyticsService } from "@/services/analytics.service";
// plane web components
import AnalyticsSectionWrapper from "../analytics-section-wrapper";
import { ProjectInsightsLoader } from "../loaders";
const RadarChart = lazy(function RadarChart() {
return import("@plane/propel/charts/radar-chart").then((mod) => ({
default: mod.RadarChart,
}));
});
const analyticsService = new AnalyticsService();
const ProjectInsights = observer(function ProjectInsights() {
const params = useParams();
const { t } = useTranslation();
const workspaceSlug = params.workspaceSlug.toString();
const { selectedDuration, selectedDurationLabel, selectedProjects, selectedCycle, selectedModule, isPeekView } =
useAnalytics();
const { data: projectInsightsData, isLoading: isLoadingProjectInsight } = useSWR(
`radar-chart-project-insights-${workspaceSlug}-${selectedDuration}-${selectedProjects}-${selectedCycle}-${selectedModule}-${isPeekView}`,
() =>
analyticsService.getAdvanceAnalyticsCharts<TChartData<string, string>[]>(
workspaceSlug,
"projects",
{
// date_filter: selectedDuration,
...(selectedProjects?.length > 0 && { project_ids: selectedProjects?.join(",") }),
...(selectedCycle ? { cycle_id: selectedCycle } : {}),
...(selectedModule ? { module_id: selectedModule } : {}),
},
isPeekView
)
);
return (
<AnalyticsSectionWrapper
title={`${t("workspace_analytics.project_insights")}`}
subtitle={selectedDurationLabel}
className="md:col-span-3"
>
{isLoadingProjectInsight ? (
<ProjectInsightsLoader />
) : projectInsightsData && projectInsightsData?.length == 0 ? (
<EmptyStateCompact
assetKey="unknown"
assetClassName="size-20"
rootClassName="border border-subtle px-5 py-10 md:py-20 md:px-20"
title={t("workspace_empty_state.analytics_work_items.title")}
/>
) : (
<div className="gap-8 lg:flex">
{projectInsightsData && (
<Suspense fallback={<ProjectInsightsLoader />}>
<RadarChart
className="h-[350px] w-full text-accent-primary lg:w-3/5"
data={projectInsightsData}
dataKey="key"
radars={[
{
key: "count",
name: "Count",
fill: "var(--text-color-accent-primary)",
stroke: "var(--text-color-accent-primary)",
fillOpacity: 0.6,
dot: {
r: 4,
fillOpacity: 1,
},
},
]}
margin={{ top: 0, right: 40, bottom: 10, left: 40 }}
showTooltip
angleAxis={{
key: "name",
}}
/>
</Suspense>
)}
<div className="w-full lg:w-2/5">
<div className="text-13 text-tertiary">{t("workspace_analytics.summary_of_projects")}</div>
<div className="mb-3 border-b border-subtle py-2">{t("workspace_analytics.all_projects")}</div>
<div className="flex flex-col gap-4">
<div className="flex items-center justify-between text-13 text-tertiary">
<div>{t("workspace_analytics.trend_on_charts")}</div>
<div>{t("common.work_items")}</div>
</div>
{projectInsightsData?.map((item) => (
<div key={item.key} className="flex items-center justify-between text-13 text-primary">
<div>{item.name}</div>
<div className="flex items-center gap-1">
{/* <TrendPiece key={item.key} size='xs' /> */}
<div className="text-secondary">{item.count}</div>
</div>
</div>
))}
</div>
</div>
</div>
)}
</AnalyticsSectionWrapper>
);
});
export default ProjectInsights;

View File

@@ -0,0 +1,27 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import React from "react";
import AnalyticsWrapper from "../analytics-wrapper";
import TotalInsights from "../total-insights";
import ActiveProjects from "./active-projects";
import ProjectInsights from "./project-insights";
function Overview() {
return (
<AnalyticsWrapper i18nTitle="common.overview">
<div className="flex flex-col gap-14">
<TotalInsights analyticsType="overview" />
<div className="grid grid-cols-1 gap-14 md:grid-cols-5">
<ProjectInsights />
<ActiveProjects />
</div>
</div>
</AnalyticsWrapper>
);
}
export { Overview };