fix: issue stats refactor (#6705)
* fix: issue stats refactor * fix: refactor * fix: ui color * fix: translation key
This commit is contained in:
parent
f01d82ad1e
commit
44af90dc6c
14 changed files with 98 additions and 53 deletions
|
|
@ -814,7 +814,8 @@
|
||||||
"sub_issue_count": "Sub-work item count",
|
"sub_issue_count": "Sub-work item count",
|
||||||
"attachment_count": "Attachment count",
|
"attachment_count": "Attachment count",
|
||||||
"created_on": "Created on",
|
"created_on": "Created on",
|
||||||
"sub_issue": "Sub-work item"
|
"sub_issue": "Sub-work item",
|
||||||
|
"work_item_count": "Work item count"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "Show sub-work items",
|
"show_sub_issues": "Show sub-work items",
|
||||||
|
|
|
||||||
|
|
@ -985,7 +985,8 @@
|
||||||
"sub_issue_count": "Cantidad de sub-elementos",
|
"sub_issue_count": "Cantidad de sub-elementos",
|
||||||
"attachment_count": "Cantidad de archivos adjuntos",
|
"attachment_count": "Cantidad de archivos adjuntos",
|
||||||
"created_on": "Creado el",
|
"created_on": "Creado el",
|
||||||
"sub_issue": "Sub-elemento de trabajo"
|
"sub_issue": "Sub-elemento de trabajo",
|
||||||
|
"work_item_count": "Recuento de elementos de trabajo"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "Mostrar sub-elementos",
|
"show_sub_issues": "Mostrar sub-elementos",
|
||||||
|
|
|
||||||
|
|
@ -983,7 +983,8 @@
|
||||||
"sub_issue_count": "Nombre de sous-éléments",
|
"sub_issue_count": "Nombre de sous-éléments",
|
||||||
"attachment_count": "Nombre de pièces jointes",
|
"attachment_count": "Nombre de pièces jointes",
|
||||||
"created_on": "Créé le",
|
"created_on": "Créé le",
|
||||||
"sub_issue": "Sous-élément de travail"
|
"sub_issue": "Sous-élément de travail",
|
||||||
|
"work_item_count": "Nombre d'éléments de travail"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "Afficher les sous-éléments",
|
"show_sub_issues": "Afficher les sous-éléments",
|
||||||
|
|
|
||||||
|
|
@ -980,8 +980,9 @@
|
||||||
"sub_issue_count": "Numero di sotto-elementi di lavoro",
|
"sub_issue_count": "Numero di sotto-elementi di lavoro",
|
||||||
"attachment_count": "Numero di allegati",
|
"attachment_count": "Numero di allegati",
|
||||||
"created_on": "Creato il",
|
"created_on": "Creato il",
|
||||||
"sub_issue": "Sotto-elemento di lavoro"
|
"sub_issue": "Sotto-elemento di lavoro",
|
||||||
},
|
"work_item_count": "Conteggio degli elementi di lavoro"
|
||||||
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "Mostra sotto-elementi di lavoro",
|
"show_sub_issues": "Mostra sotto-elementi di lavoro",
|
||||||
"show_empty_groups": "Mostra gruppi vuoti"
|
"show_empty_groups": "Mostra gruppi vuoti"
|
||||||
|
|
|
||||||
|
|
@ -983,8 +983,9 @@
|
||||||
"sub_issue_count": "サブ作業項目数",
|
"sub_issue_count": "サブ作業項目数",
|
||||||
"attachment_count": "添付ファイル数",
|
"attachment_count": "添付ファイル数",
|
||||||
"created_on": "作成日",
|
"created_on": "作成日",
|
||||||
"sub_issue": "サブ作業項目"
|
"sub_issue": "サブ作業項目",
|
||||||
},
|
"work_item_count": "作業項目数"
|
||||||
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "サブ作業項目を表示",
|
"show_sub_issues": "サブ作業項目を表示",
|
||||||
"show_empty_groups": "空のグループを表示"
|
"show_empty_groups": "空のグループを表示"
|
||||||
|
|
|
||||||
|
|
@ -982,7 +982,8 @@
|
||||||
"sub_issue_count": "Количество подэлементов",
|
"sub_issue_count": "Количество подэлементов",
|
||||||
"attachment_count": "Количество вложений",
|
"attachment_count": "Количество вложений",
|
||||||
"created_on": "Дата создания",
|
"created_on": "Дата создания",
|
||||||
"sub_issue": "Подэлемент"
|
"sub_issue": "Подэлемент",
|
||||||
|
"work_item_count": "Количество рабочих элементов"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "Показывать подэлементы",
|
"show_sub_issues": "Показывать подэлементы",
|
||||||
|
|
|
||||||
|
|
@ -983,7 +983,8 @@
|
||||||
"sub_issue_count": "子工作项数量",
|
"sub_issue_count": "子工作项数量",
|
||||||
"attachment_count": "附件数量",
|
"attachment_count": "附件数量",
|
||||||
"created_on": "创建于",
|
"created_on": "创建于",
|
||||||
"sub_issue": "子工作项"
|
"sub_issue": "子工作项",
|
||||||
|
"work_item_count": "工作项数量"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"show_sub_issues": "显示子工作项",
|
"show_sub_issues": "显示子工作项",
|
||||||
|
|
|
||||||
|
|
@ -4,9 +4,10 @@ import React, { FC } from "react";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issueId: string;
|
issueId: string;
|
||||||
|
className?: string;
|
||||||
|
size?: number;
|
||||||
|
showProgressText?: boolean;
|
||||||
|
showLabel?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const IssueStats: FC<Props> = (props) => {
|
export const IssueStats: FC<Props> = (props) => <></>;
|
||||||
const { issueId } = props;
|
|
||||||
return <></>;
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ export const FilterDisplayProperties: React.FC<Props> = observer((props) => {
|
||||||
}
|
}
|
||||||
}).map((property) => {
|
}).map((property) => {
|
||||||
if (isEpic && property.key === "sub_issue_count") {
|
if (isEpic && property.key === "sub_issue_count") {
|
||||||
return { ...property, title: "Work item count" };
|
return { ...property, titleTranslationKey: "issue.display.properties.work_item_count" };
|
||||||
}
|
}
|
||||||
return property;
|
return property;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import { useParams } from "next/navigation";
|
||||||
// ui
|
// ui
|
||||||
import { Tooltip, ControlLink } from "@plane/ui";
|
import { Tooltip, ControlLink } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
|
import { findTotalDaysInRange } from "@plane/utils";
|
||||||
import { SIDEBAR_WIDTH } from "@/components/gantt-chart/constants";
|
import { SIDEBAR_WIDTH } from "@/components/gantt-chart/constants";
|
||||||
// helpers
|
// helpers
|
||||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
|
||||||
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
import { generateWorkItemLink } from "@/helpers/issue.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useIssueDetail, useIssues, useProject, useProjectState } from "@/hooks/store";
|
import { useIssueDetail, useIssues, useProject, useProjectState } from "@/hooks/store";
|
||||||
|
|
@ -17,6 +17,7 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
// plane web components
|
// plane web components
|
||||||
import { IssueIdentifier } from "@/plane-web/components/issues";
|
import { IssueIdentifier } from "@/plane-web/components/issues";
|
||||||
//
|
//
|
||||||
|
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
||||||
import { getBlockViewDetails } from "../utils";
|
import { getBlockViewDetails } from "../utils";
|
||||||
import { GanttStoreType } from "./base-gantt-root";
|
import { GanttStoreType } from "./base-gantt-root";
|
||||||
|
|
||||||
|
|
@ -48,6 +49,8 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
|
||||||
|
|
||||||
const handleIssuePeekOverview = () => handleRedirection(workspaceSlug, issueDetails, isMobile);
|
const handleIssuePeekOverview = () => handleRedirection(workspaceSlug, issueDetails, isMobile);
|
||||||
|
|
||||||
|
const duration = findTotalDaysInRange(issueDetails?.start_date, issueDetails?.target_date) || 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
|
|
@ -62,17 +65,24 @@ export const IssueGanttBlock: React.FC<Props> = observer((props) => {
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
id={`issue-${issueId}`}
|
id={`issue-${issueId}`}
|
||||||
className="relative flex h-full w-full cursor-pointer items-center rounded"
|
className="relative flex h-full w-full cursor-pointer items-center rounded space-between"
|
||||||
style={blockStyle}
|
style={blockStyle}
|
||||||
onClick={handleIssuePeekOverview}
|
onClick={handleIssuePeekOverview}
|
||||||
>
|
>
|
||||||
<div className="absolute left-0 top-0 h-full w-full bg-custom-background-100/50" />
|
<div className="absolute left-0 top-0 h-full w-full bg-custom-background-100/50 " />
|
||||||
<div
|
<div
|
||||||
className="sticky w-auto overflow-hidden truncate px-2.5 py-1 text-sm text-custom-text-100"
|
className="sticky w-auto overflow-hidden truncate px-2.5 py-1 text-sm text-custom-text-100 flex-1"
|
||||||
style={{ left: `${SIDEBAR_WIDTH}px` }}
|
style={{ left: `${SIDEBAR_WIDTH}px` }}
|
||||||
>
|
>
|
||||||
{issueDetails?.name}
|
{issueDetails?.name}
|
||||||
</div>
|
</div>
|
||||||
|
{isEpic && (
|
||||||
|
<IssueStats
|
||||||
|
issueId={issueId}
|
||||||
|
className="sticky mx-2 font-medium text-custom-text-100 overflow-hidden truncate w-auto justify-end flex-shrink-0"
|
||||||
|
showProgressText={duration >= 2}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,10 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
// plane web components
|
// plane web components
|
||||||
import { IssueIdentifier } from "@/plane-web/components/issues";
|
import { IssueIdentifier } from "@/plane-web/components/issues";
|
||||||
// local components
|
// local components
|
||||||
|
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
||||||
import { TRenderQuickActions } from "../list/list-view-types";
|
import { TRenderQuickActions } from "../list/list-view-types";
|
||||||
import { IssueProperties } from "../properties/all-properties";
|
import { IssueProperties } from "../properties/all-properties";
|
||||||
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
import { getIssueBlockId } from "../utils";
|
import { getIssueBlockId } from "../utils";
|
||||||
|
|
||||||
interface IssueBlockProps {
|
interface IssueBlockProps {
|
||||||
|
|
@ -61,6 +63,9 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||||
// hooks
|
// hooks
|
||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
|
|
||||||
|
// derived values
|
||||||
|
const subIssueCount = issue?.sub_issues_count ?? 0;
|
||||||
|
|
||||||
const handleEventPropagation = (e: React.MouseEvent) => {
|
const handleEventPropagation = (e: React.MouseEvent) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
@ -105,6 +110,16 @@ const KanbanIssueDetailsBlock: React.FC<IssueDetailsBlockProps> = observer((prop
|
||||||
isReadOnly={isReadOnly}
|
isReadOnly={isReadOnly}
|
||||||
isEpic={isEpic}
|
isEpic={isEpic}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{isEpic && displayProperties && (
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="sub_issue_count"
|
||||||
|
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!subIssueCount}
|
||||||
|
>
|
||||||
|
<IssueStats issueId={issue.id} className="mt-2 font-medium text-custom-text-350" />
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||||
import { IssueIdentifier } from "@/plane-web/components/issues";
|
import { IssueIdentifier } from "@/plane-web/components/issues";
|
||||||
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
||||||
// types
|
// types
|
||||||
|
import { WithDisplayPropertiesHOC } from "../properties/with-display-properties-HOC";
|
||||||
import { TRenderQuickActions } from "./list-view-types";
|
import { TRenderQuickActions } from "./list-view-types";
|
||||||
|
|
||||||
interface IssueBlockProps {
|
interface IssueBlockProps {
|
||||||
|
|
@ -269,7 +270,15 @@ export const IssueBlock = observer((props: IssueBlockProps) => {
|
||||||
>
|
>
|
||||||
<p className="truncate cursor-pointer text-sm text-custom-text-100">{issue.name}</p>
|
<p className="truncate cursor-pointer text-sm text-custom-text-100">{issue.name}</p>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{isEpic && <IssueStats issueId={issue.id} />}
|
{isEpic && displayProperties && (
|
||||||
|
<WithDisplayPropertiesHOC
|
||||||
|
displayProperties={displayProperties}
|
||||||
|
displayPropertyKey="sub_issue_count"
|
||||||
|
shouldRenderProperty={(properties) => !!properties.sub_issue_count}
|
||||||
|
>
|
||||||
|
<IssueStats issueId={issue.id} className="ml-2 font-medium text-custom-text-350" />
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{!issue?.tempId && (
|
{!issue?.tempId && (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -429,36 +429,38 @@ export const IssueProperties: React.FC<IIssueProperties> = observer((props) => {
|
||||||
|
|
||||||
{/* extra render properties */}
|
{/* extra render properties */}
|
||||||
{/* sub-issues */}
|
{/* sub-issues */}
|
||||||
<WithDisplayPropertiesHOC
|
{!isEpic && (
|
||||||
displayProperties={displayProperties}
|
<WithDisplayPropertiesHOC
|
||||||
displayPropertyKey="sub_issue_count"
|
displayProperties={displayProperties}
|
||||||
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!subIssueCount}
|
displayPropertyKey="sub_issue_count"
|
||||||
>
|
shouldRenderProperty={(properties) => !!properties.sub_issue_count && !!subIssueCount}
|
||||||
<Tooltip
|
|
||||||
tooltipHeading={isEpic ? t("issues.label", { count: 2 }) : t("common.sub_work_items")}
|
|
||||||
tooltipContent={`${subIssueCount}`}
|
|
||||||
isMobile={isMobile}
|
|
||||||
renderByDefault={false}
|
|
||||||
>
|
>
|
||||||
<div
|
<Tooltip
|
||||||
onFocus={handleEventPropagation}
|
tooltipHeading={t("common.sub_work_items")}
|
||||||
onClick={(e) => {
|
tooltipContent={`${subIssueCount}`}
|
||||||
e.stopPropagation();
|
isMobile={isMobile}
|
||||||
e.preventDefault();
|
renderByDefault={false}
|
||||||
if (subIssueCount) redirectToIssueDetail();
|
|
||||||
}}
|
|
||||||
className={cn(
|
|
||||||
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1",
|
|
||||||
{
|
|
||||||
"hover:bg-custom-background-80 cursor-pointer": subIssueCount,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
<div
|
||||||
<div className="text-xs">{subIssueCount}</div>
|
onFocus={handleEventPropagation}
|
||||||
</div>
|
onClick={(e) => {
|
||||||
</Tooltip>
|
e.stopPropagation();
|
||||||
</WithDisplayPropertiesHOC>
|
e.preventDefault();
|
||||||
|
if (subIssueCount) redirectToIssueDetail();
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
"flex h-5 flex-shrink-0 items-center justify-center gap-2 overflow-hidden rounded border-[0.5px] border-custom-border-300 px-2.5 py-1",
|
||||||
|
{
|
||||||
|
"hover:bg-custom-background-80 cursor-pointer": subIssueCount,
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Layers className="h-3 w-3 flex-shrink-0" strokeWidth={2} />
|
||||||
|
<div className="text-xs">{subIssueCount}</div>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</WithDisplayPropertiesHOC>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* attachments */}
|
{/* attachments */}
|
||||||
<WithDisplayPropertiesHOC
|
<WithDisplayPropertiesHOC
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import { Row } from "@plane/ui";
|
||||||
import { cn } from "@/helpers/common.helper";
|
import { cn } from "@/helpers/common.helper";
|
||||||
// hooks
|
// hooks
|
||||||
import { useAppRouter } from "@/hooks/use-app-router";
|
import { useAppRouter } from "@/hooks/use-app-router";
|
||||||
|
import { IssueStats } from "@/plane-web/components/issues/issue-layouts/issue-stats";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
issue: TIssue;
|
issue: TIssue;
|
||||||
|
|
@ -18,30 +19,30 @@ export const SpreadsheetSubIssueColumn: React.FC<Props> = observer((props: Props
|
||||||
// router
|
// router
|
||||||
const router = useAppRouter();
|
const router = useAppRouter();
|
||||||
// hooks
|
// hooks
|
||||||
const { workspaceSlug, epicId } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
// derived values
|
// derived values
|
||||||
|
const isEpic = issue?.is_epic;
|
||||||
const subIssueCount = issue?.sub_issues_count ?? 0;
|
const subIssueCount = issue?.sub_issues_count ?? 0;
|
||||||
|
|
||||||
const redirectToIssueDetail = () => {
|
const redirectToIssueDetail = () => {
|
||||||
router.push(
|
router.push(
|
||||||
`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}${epicId ? "epics" : "issues"}/${issue.id}#sub-issues`
|
`/${workspaceSlug?.toString()}/projects/${issue.project_id}/${issue.archived_at ? "archives/" : ""}${isEpic ? "epics" : "issues"}/${issue.id}#sub-issues`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const issueLabel = epicId ? "work item" : "sub-work item";
|
const label = `${subIssueCount} sub-work item${subIssueCount !== 1 ? "s" : ""}`;
|
||||||
const label = `${subIssueCount} ${issueLabel}${subIssueCount !== 1 ? "s" : ""}`;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
onClick={subIssueCount ? redirectToIssueDetail : () => {}}
|
onClick={subIssueCount ? redirectToIssueDetail : () => {}}
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 py-1 text-xs hover:bg-custom-background-80 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-100/10",
|
"flex h-11 w-full items-center border-b-[0.5px] border-custom-border-200 py-1 text-xs hover:bg-custom-background-90 group-[.selected-issue-row]:bg-custom-primary-100/5 group-[.selected-issue-row]:hover:bg-custom-primary-90",
|
||||||
{
|
{
|
||||||
"cursor-pointer": subIssueCount,
|
"cursor-pointer": subIssueCount,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{label}
|
{isEpic ? <IssueStats issueId={issue.id} /> : label}
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue