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,33 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { observer } from "mobx-react";
import { useTranslation } from "@plane/i18n";
import { AnalyticsOutline } from "@makeplane/propel/icons";
// plane imports
import { Breadcrumbs, Header } from "@plane/ui";
// components
import { BreadcrumbLink } from "@/components/common/breadcrumb-link";
export const WorkspaceAnalyticsHeader = observer(function WorkspaceAnalyticsHeader() {
const { t } = useTranslation();
return (
<Header>
<Header.LeftItem>
<Breadcrumbs>
<Breadcrumbs.Item
component={
<BreadcrumbLink
label={t("workspace_analytics.label")}
icon={<AnalyticsOutline className="h-4 w-4 text-tertiary" />}
/>
}
/>
</Breadcrumbs>
</Header.LeftItem>
</Header>
);
});

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
// components
import { Outlet } from "react-router";
import { AppHeader } from "@/components/core/app-header";
import { ContentWrapper } from "@/components/core/content-wrapper";
import { WorkspaceAnalyticsHeader } from "./header";
export default function WorkspaceAnalyticsTabLayout() {
return (
<>
<AppHeader header={<WorkspaceAnalyticsHeader />} />
<ContentWrapper>
<Outlet />
</ContentWrapper>
</>
);
}

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) 2023-present Plane Software, Inc. and contributors
* SPDX-License-Identifier: AGPL-3.0-only
* See the LICENSE file for details.
*/
import { useState, useEffect } from "react";
import { observer } from "mobx-react";
import { useRouter } from "next/navigation";
// plane package imports
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
import { useTranslation } from "@plane/i18n";
import { EmptyStateDetailed } from "@plane/propel/empty-state";
import { Tab, Tabs, TabsList, TabsPanel } from "@makeplane/propel/components/tabs";
// components
import { cn } from "@plane/utils";
import AnalyticsFilterActions from "@/components/analytics/analytics-filter-actions";
import { PageHead } from "@/components/core/page-title";
// hooks
import { useCommandPalette } from "@/hooks/store/use-command-palette";
import { useProject } from "@/hooks/store/use-project";
import { useWorkspace } from "@/hooks/store/use-workspace";
import { useUserPermissions } from "@/hooks/store/user";
import { useAnalyticsTabs } from "@/components/analytics/use-analytics-tabs";
import type { Route } from "./+types/page";
function AnalyticsPage({ params }: Route.ComponentProps) {
const { tabId } = params;
// hooks
const router = useRouter();
// plane imports
const { t } = useTranslation();
// store hooks
const { toggleCreateProjectModal } = useCommandPalette();
const { workspaceProjectIds, loader } = useProject();
const { currentWorkspace } = useWorkspace();
const { allowPermissions } = useUserPermissions();
const pageTitle = currentWorkspace?.name
? t(`workspace_analytics.page_label`, { workspace: currentWorkspace?.name })
: undefined;
// permissions
const canPerformEmptyStateActions = allowPermissions(
[EUserPermissions.ADMIN, EUserPermissions.MEMBER],
EUserPermissionsLevel.WORKSPACE
);
const workspaceSlug = params.workspaceSlug;
const ANALYTICS_TABS = useAnalyticsTabs(workspaceSlug.toString());
const [selectedTab, setSelectedTab] = useState(tabId || ANALYTICS_TABS[0]?.key);
useEffect(() => {
if (tabId) {
setSelectedTab(tabId);
}
}, [tabId]);
// Handle tab change
const handleTabChange = (value: string) => {
setSelectedTab(value);
router.push(`/${currentWorkspace?.slug}/analytics/${value}`);
};
return (
<>
<PageHead title={pageTitle} />
{workspaceProjectIds && (
<>
{workspaceProjectIds.length > 0 || loader === "init-loader" ? (
<div className="flex h-full overflow-hidden">
<Tabs variant="contained" value={selectedTab} onValueChange={handleTabChange}>
<div className={"flex h-full w-full flex-col"}>
<div
className={cn(
"flex w-full items-center justify-between gap-4 overflow-hidden border-b border-subtle bg-surface-1 px-6 py-2"
)}
>
<TabsList>
{ANALYTICS_TABS.map((tab) => (
<Tab key={tab.key} value={tab.key} disabled={tab.isDisabled} label={tab.label} />
))}
</TabsList>
<div className="flex-shrink-0">
<AnalyticsFilterActions />
</div>
</div>
{/* Grid wrapper: Propel's TabsPanel omits className, so the single mounted panel
gets its fill height from a one-row grid instead. */}
<div className="grid min-h-0 w-full flex-1 grid-rows-1 overflow-x-hidden overflow-y-auto px-2">
{ANALYTICS_TABS.map((tab) => (
<TabsPanel key={tab.key} value={tab.key}>
<tab.content />
</TabsPanel>
))}
</div>
</div>
</Tabs>
</div>
) : (
<EmptyStateDetailed
assetKey="project"
title={t("workspace_projects.empty_state.no_projects.title")}
description={t("workspace_projects.empty_state.no_projects.description")}
actions={[
{
label: "Create a project",
onClick: () => {
toggleCreateProjectModal(true);
},
disabled: !canPerformEmptyStateActions,
},
]}
/>
)}
</>
)}
</>
);
}
export default observer(AnalyticsPage);