diff --git a/plane-src/apps/api/plane/app/views/workspace/user.py b/plane-src/apps/api/plane/app/views/workspace/user.py index b60ae5e..2392939 100644 --- a/plane-src/apps/api/plane/app/views/workspace/user.py +++ b/plane-src/apps/api/plane/app/views/workspace/user.py @@ -41,9 +41,10 @@ from plane.app.serializers import ( from plane.app.views.base import BaseAPIView from plane.db.models import ( CycleIssue, + FileAsset, + IntakeIssue, Issue, IssueActivity, - FileAsset, IssueLink, IssueSubscriber, Project, @@ -108,6 +109,18 @@ class WorkspaceUserProfileIssuesEndpoint(BaseAPIView): CycleIssue.objects.filter(issue=OuterRef("id"), deleted_at__isnull=True).values("cycle_id")[:1] ) ) + .annotate( + created_by_display_name=F("created_by__display_name"), + created_by_avatar_url=F("created_by__avatar"), + ) + .annotate( + source_project_name=Subquery( + IntakeIssue.objects.filter( + issue_id=OuterRef("id"), + extra__bridge="external-contours", + ).values("extra__source_project_name")[:1] + ) + ) .annotate( link_count=IssueLink.objects.filter(issue=OuterRef("id")) .order_by() diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx index 5224212..a5480ef 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/kanban/roots/profile-issues-root.tsx @@ -8,15 +8,20 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; // plane imports import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import type { TProfileViews } from "@plane/types"; // hooks import { useUserPermissions } from "@/hooks/store/user"; // local imports import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; import { BaseKanBanRoot } from "../base-kanban-root"; -export const ProfileIssuesKanBanLayout = observer(function ProfileIssuesKanBanLayout() { +type Props = { + viewId: TProfileViews; +}; + +export const ProfileIssuesKanBanLayout = observer(function ProfileIssuesKanBanLayout({ viewId }: Props) { // router - const { workspaceSlug, profileViewId } = useParams(); + const { workspaceSlug } = useParams(); const { allowPermissions } = useUserPermissions(); const canEditPropertiesBasedOnProject = (projectId: string) => @@ -31,7 +36,8 @@ export const ProfileIssuesKanBanLayout = observer(function ProfileIssuesKanBanLa ); }); diff --git a/plane-src/apps/web/core/components/issues/issue-layouts/list/roots/profile-issues-root.tsx b/plane-src/apps/web/core/components/issues/issue-layouts/list/roots/profile-issues-root.tsx index cc69e7c..cec785a 100644 --- a/plane-src/apps/web/core/components/issues/issue-layouts/list/roots/profile-issues-root.tsx +++ b/plane-src/apps/web/core/components/issues/issue-layouts/list/roots/profile-issues-root.tsx @@ -7,15 +7,20 @@ import { observer } from "mobx-react"; import { useParams } from "next/navigation"; import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants"; +import type { TProfileViews } from "@plane/types"; // hooks import { useUserPermissions } from "@/hooks/store/user"; // local imports import { ProjectIssueQuickActions } from "../../quick-action-dropdowns"; import { BaseListRoot } from "../base-list-root"; -export const ProfileIssuesListLayout = observer(function ProfileIssuesListLayout() { +type Props = { + viewId: TProfileViews; +}; + +export const ProfileIssuesListLayout = observer(function ProfileIssuesListLayout({ viewId }: Props) { // router - const { workspaceSlug, profileViewId } = useParams(); + const { workspaceSlug } = useParams(); // store const { allowPermissions } = useUserPermissions(); @@ -31,7 +36,7 @@ export const ProfileIssuesListLayout = observer(function ProfileIssuesListLayout ); }); diff --git a/plane-src/apps/web/core/components/profile/overview/helpers.ts b/plane-src/apps/web/core/components/profile/overview/helpers.ts new file mode 100644 index 0000000..a65a505 --- /dev/null +++ b/plane-src/apps/web/core/components/profile/overview/helpers.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2023-present Plane Software, Inc. and contributors + * SPDX-License-Identifier: AGPL-3.0-only + * See the LICENSE file for details. + */ + +import type { TStateGroups } from "@plane/types"; + +export const PROFILE_STATE_GROUP_COLORS: Partial> = { + backlog: "#050505", + unstarted: "#7C7F85", + started: "#F5F7FB", + completed: "#C3FF66", + cancelled: "#050505", +}; + +export const PROFILE_STATE_GROUP_LABELS: Partial> = { + backlog: "Бэклог", + unstarted: "К выполнению", + started: "В работе", + completed: "Готово", + cancelled: "Отложено", +}; + +export const PROFILE_PRIORITY_COLORS: Record = { + urgent: "#C3FF66", + high: "#F5F7FB", + medium: "#7C7F85", + low: "#2A2B2E", + none: "#050505", +}; + +export const PROFILE_PRIORITY_LABELS: Record = { + urgent: "Срочно", + high: "Высокий", + medium: "Средний", + low: "Низкий", + none: "Нет", +}; + +export const getProfilePriorityKey = (priority: string | null | undefined) => priority?.trim().toLowerCase() || "none"; + +export const getProfilePriorityLabel = (priority: string | null | undefined) => + PROFILE_PRIORITY_LABELS[getProfilePriorityKey(priority)] ?? priority ?? PROFILE_PRIORITY_LABELS.none; + +export const getProfileStateGroupColor = (stateGroup: TStateGroups) => + PROFILE_STATE_GROUP_COLORS[stateGroup] ?? "#7C7F85"; + +export const getProfileStateGroupLabel = (stateGroup: TStateGroups) => + PROFILE_STATE_GROUP_LABELS[stateGroup] ?? stateGroup; diff --git a/plane-src/apps/web/core/components/profile/overview/priority-distribution.tsx b/plane-src/apps/web/core/components/profile/overview/priority-distribution.tsx index d32fca1..234e897 100644 --- a/plane-src/apps/web/core/components/profile/overview/priority-distribution.tsx +++ b/plane-src/apps/web/core/components/profile/overview/priority-distribution.tsx @@ -10,22 +10,25 @@ import { BarChart } from "@plane/propel/charts/bar-chart"; import { EmptyStateCompact } from "@plane/propel/empty-state"; import type { IUserProfileData } from "@plane/types"; import { Loader, Card } from "@plane/ui"; -import { capitalizeFirstLetter } from "@plane/utils"; +import { getProfilePriorityKey, getProfilePriorityLabel, PROFILE_PRIORITY_COLORS } from "./helpers"; type Props = { userProfile: IUserProfileData | undefined; }; -const priorityColors = { - urgent: "#991b1b", - high: "#ef4444", - medium: "#f59e0b", - low: "#16a34a", - none: "#e5e5e5", -}; - export function ProfilePriorityDistribution({ userProfile }: Props) { const { t } = useTranslation(); + const priorityChartData = + userProfile?.priority_distribution.map((priority) => { + const priorityKey = getProfilePriorityKey(priority.priority); + + return { + key: priorityKey, + name: getProfilePriorityLabel(priorityKey), + count: priority.priority_count, + }; + }) ?? []; + return (

{t("profile.stats.priority_distribution.title")}

@@ -33,21 +36,18 @@ export function ProfilePriorityDistribution({ userProfile }: Props) { {userProfile.priority_distribution.length > 0 ? ( ({ - key: priority.priority ?? "None", - name: capitalizeFirstLetter(priority.priority ?? "None"), - count: priority.priority_count, - }))} + className="nodedc-analytics-bar-chart h-[300px] w-full" + margin={{ top: 20, right: 22, bottom: 28, left: 8 }} + data={priorityChartData} bars={[ { key: "count", - label: "Count", + label: "Количество", stackId: "bar-one", - fill: (payload: any) => priorityColors[payload.key as keyof typeof priorityColors], // TODO: fix types + fill: (payload: any) => PROFILE_PRIORITY_COLORS[payload.key] ?? PROFILE_PRIORITY_COLORS.none, textClassName: "", showPercentage: false, + borderRadius: 11, showTopBorderRadius: () => true, showBottomBorderRadius: () => true, }, @@ -60,7 +60,7 @@ export function ProfilePriorityDistribution({ userProfile }: Props) { key: "count", label: "", }} - barSize={20} + barSize={86} /> ) : ( @@ -47,21 +40,20 @@ export function ProfileStateDistribution({ stateDistribution, userProfile }: Pro id: group.state_group, key: group.state_group, value: group.state_count, - name: - stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ?? - capitalizeFirstLetter(group.state_group), - color: STATE_GROUPS[group.state_group]?.color, + name: getProfileStateGroupLabel(group.state_group), + color: getProfileStateGroupColor(group.state_group), })) ?? [] } cells={userProfile.state_distribution.map((group) => ({ key: group.state_group, - fill: STATE_GROUPS[group.state_group]?.color, + fill: getProfileStateGroupColor(group.state_group), }))} showTooltip - tooltipLabel="Count" - paddingAngle={5} - cornerRadius={4} - innerRadius="50%" + tooltipLabel="Количество" + paddingAngle={6} + cornerRadius={6} + innerRadius="42%" + outerRadius="84%" showLabel={false} />
@@ -72,14 +64,10 @@ export function ProfileStateDistribution({ stateDistribution, userProfile }: Pro
-
- {stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ?? - STATE_GROUPS[group.state_group].label} -
+
{getProfileStateGroupLabel(group.state_group)}
{group.state_count}
diff --git a/plane-src/apps/web/core/components/profile/overview/workload.tsx b/plane-src/apps/web/core/components/profile/overview/workload.tsx index d4224af..7bf22d9 100644 --- a/plane-src/apps/web/core/components/profile/overview/workload.tsx +++ b/plane-src/apps/web/core/components/profile/overview/workload.tsx @@ -4,12 +4,11 @@ * See the LICENSE file for details. */ -// plane imports -import { STATE_GROUPS } from "@plane/constants"; // types import { useTranslation } from "@plane/i18n"; import type { IUserStateDistribution } from "@plane/types"; import { Card, ECardDirection, ECardSpacing } from "@plane/ui"; +import { getProfileStateGroupColor, getProfileStateGroupLabel } from "./helpers"; // constants type Props = { @@ -18,11 +17,6 @@ type Props = { export function ProfileWorkload({ stateDistribution }: Props) { const { t } = useTranslation(); - const stateGroupLabels = { - unstarted: t("yet_to_start"), - started: t("in_progress"), - completed: t("completed"), - } as const; return (
@@ -39,14 +33,11 @@ export function ProfileWorkload({ stateDistribution }: Props) {
- - {stateGroupLabels[group.state_group as keyof typeof stateGroupLabels] ?? - STATE_GROUPS[group.state_group].label} - + {getProfileStateGroupLabel(group.state_group)}

{group.state_count}

diff --git a/plane-src/apps/web/core/components/profile/profile-issues.tsx b/plane-src/apps/web/core/components/profile/profile-issues.tsx index 4822a6e..9ac5c00 100644 --- a/plane-src/apps/web/core/components/profile/profile-issues.tsx +++ b/plane-src/apps/web/core/components/profile/profile-issues.tsx @@ -66,9 +66,9 @@ export const ProfileIssuesPage = observer(function ProfileIssuesPage(props: Prop {profileWorkItemsFilter && }
{activeLayout === "list" ? ( - + ) : activeLayout === "kanban" ? ( - + ) : null}