Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
7e93934
Audit cycle KPI investigation
gustoliveira Mar 17, 2026
27eadbb
Plan cycle KPI implementation
gustoliveira Mar 17, 2026
9e0e78f
Add detailed KPI implementation plan
gustoliveira Mar 17, 2026
1d063a7
Remove mobile scope from KPI plan
gustoliveira Mar 17, 2026
da4524b
Document open-core import constraints
gustoliveira Mar 17, 2026
c538583
Add cycle list KPI action
gustoliveira Mar 17, 2026
eb47c96
Explain dependency installation
gustoliveira Mar 17, 2026
132c9e4
Investigate pnpm dev workspace build failure
gustoliveira Mar 17, 2026
13bf5f1
Confirm root dev race condition
gustoliveira Mar 17, 2026
0cf701c
Troubleshoot god-mode access
gustoliveira Mar 17, 2026
2fed24f
Add cycle KPI route shell
gustoliveira Mar 18, 2026
301f471
Wire cycle KPI burndown card
gustoliveira Mar 18, 2026
744c60c
Isolate KPI burndown chart
gustoliveira Mar 18, 2026
6cbb675
Fix KPI burndown value normalization
gustoliveira Mar 18, 2026
4708e17
Plan KPI burndown filters phase
gustoliveira Mar 18, 2026
b5954ab
Add KPI burndown tendency line
gustoliveira Mar 18, 2026
a3a1a35
Explain existing burndown line behavior
gustoliveira Mar 18, 2026
a96a4b8
Add KPI label filtering
gustoliveira Mar 18, 2026
cd8ebab
Explain docker compose choice for dev
gustoliveira Mar 18, 2026
01db19a
Describe hybrid docker compose changes
gustoliveira Mar 18, 2026
98aff6c
Refine backend-only compose guidance
gustoliveira Mar 18, 2026
f487d98
Advise on faster web development
gustoliveira Mar 18, 2026
4e03f07
Format KPI burndown tooltip values
gustoliveira Mar 18, 2026
ec15d16
Revert KPI tooltip truncation
gustoliveira Mar 18, 2026
cd66403
Explain cycle KPI cards
gustoliveira Mar 18, 2026
5bfa84c
Align KPI cards with burndown cutoff
gustoliveira Mar 19, 2026
f085a6f
Show business days left in KPI cards
gustoliveira Mar 19, 2026
d87c770
Refine KPI cycle-end wording
gustoliveira Mar 19, 2026
a475b9f
Remove KPI tendency series
gustoliveira Mar 19, 2026
3ff6a9a
Explain KPI label filter behavior
gustoliveira Mar 19, 2026
c805243
Explain KPI burndown scope-change behavior
gustoliveira Mar 19, 2026
8e5dd86
Explain cycle assignment timestamp availability
gustoliveira Mar 19, 2026
86a3981
Highlight KPI weekend days
gustoliveira Mar 19, 2026
f66838a
Fix KPI weekend day coloring
gustoliveira Mar 19, 2026
2bd3f72
Show every KPI burndown day
gustoliveira Mar 19, 2026
b407c7f
Keep every KPI day visible
gustoliveira Mar 19, 2026
e81dd38
Compact KPI daily labels
gustoliveira Mar 19, 2026
b7944ad
Restore KPI daily labels properly
gustoliveira Mar 19, 2026
0dd1593
Filter KPI burndown by user
gustoliveira Mar 19, 2026
249e54a
Align KPI user filter styling
gustoliveira Mar 19, 2026
96fe822
Force KPI chart tick visibility
gustoliveira Mar 19, 2026
ddb4d0f
Explain label filter unclickable issue
gustoliveira Mar 19, 2026
414ce8e
Fix duplicate label filter bug
gustoliveira Mar 19, 2026
425312f
Remove redundant label filter icon
gustoliveira Mar 19, 2026
419f657
Acknowledge completion of duplicate icon fix
gustoliveira Mar 19, 2026
37afa7c
Align KPI filter button heights
gustoliveira Mar 19, 2026
429d79d
Align KPI filter button heights via paddings
gustoliveira Mar 19, 2026
b4420a7
Revert KPI button height padding alignment
gustoliveira Mar 19, 2026
7c21673
Revert explicit KPI button height constraints
gustoliveira Mar 19, 2026
73bd021
Fix Clear filter button height in Cycle KPIs page
gustoliveira Mar 19, 2026
49ab8d9
Clear button before filter by members
gustoliveira Mar 19, 2026
ff41163
Add KPI points-by-label chart block
gustoliveira Mar 19, 2026
0dda42f
Record duplicate prompt audit entry
gustoliveira Mar 19, 2026
9166e12
Fix KPI label chart filter alignment
gustoliveira Mar 19, 2026
d85637d
Add KPI points-by-status chart block
gustoliveira Mar 19, 2026
4d1dd79
Document KPI label-point mismatch cause
gustoliveira Mar 19, 2026
f5f36b6
Explain KPI burndown vs status differences
gustoliveira Mar 19, 2026
a13918c
Time-cap KPI bar chart aggregations
gustoliveira Mar 19, 2026
325643e
Audit investigation and finalize time-cap request
gustoliveira Mar 19, 2026
a5431da
Reconcile status KPI totals with burndown
gustoliveira Mar 19, 2026
20bda90
Remove synthetic late status column
gustoliveira Mar 20, 2026
134752c
Add issue lists to KPI bar-chart tooltips
gustoliveira Mar 20, 2026
a10f008
Remove unnecessary subtitles
gustoliveira Mar 20, 2026
ffe699d
Investigate web Docker build
gustoliveira Mar 20, 2026
b96a9ac
Add unestimated-ticket stat for user filters
gustoliveira Mar 20, 2026
bc746e7
Make unestimated KPI card always visible
gustoliveira Mar 20, 2026
99f3a66
Investigate unestimated visibility in status chart
gustoliveira Mar 20, 2026
e679be2
Clarify all-unestimated status behavior
gustoliveira Mar 20, 2026
48b2691
Show unestimated presence in status chart
gustoliveira Mar 20, 2026
a57ec27
Add points-by-user stacked KPI block
gustoliveira Mar 20, 2026
4e4d822
Add scroll and spacing for user chart labels
gustoliveira Mar 20, 2026
9a4b188
Tilt user labels and reduce column gap
gustoliveira Mar 20, 2026
d9c305f
Show all user labels and externalize status legend
gustoliveira Mar 20, 2026
e8357cf
Guarantee all user labels on points-by-user axis
gustoliveira Mar 20, 2026
11b75af
Fix vertical scrolling
gustoliveira Mar 20, 2026
0fc77c2
Refine chart axis labels and height
gustoliveira Mar 23, 2026
b63bdcd
Investigate missing labels regression
gustoliveira Mar 23, 2026
51cd1b2
Revert chart axis refactor due to missing labels
gustoliveira Mar 23, 2026
1eef783
Investigate applying HTML labels to other charts
gustoliveira Mar 23, 2026
51e320c
Ensure rotation on KPI's charts
gustoliveira Mar 23, 2026
c8d4495
Rename KPI user chart y-axis label to Points
gustoliveira Mar 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
392 changes: 392 additions & 0 deletions PLAN.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { CycleKpiPageShell } from "@/components/cycles/kpi/page-shell";

export default function CycleKpiPage() {
return <CycleKpiPageShell />;
}
143 changes: 143 additions & 0 deletions apps/web/core/components/cycles/kpi/burndown-chart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import React from "react";
import type { ComponentType } from "react";
// plane imports
import { AreaChart } from "@plane/propel/charts/area-chart";
import type { TChartData, TCycleCompletionChartDistribution } from "@plane/types";
import { getDate, renderFormattedDateWithoutYear } from "@plane/utils";

type TKpiWeekendXAxisTickProps = {
x?: number;
y?: number;
payload?: {
value?: string;
};
};

const KpiWeekendXAxisTick = React.memo<TKpiWeekendXAxisTickProps & { isWeekend?: boolean }>(
({ x = 0, y = 0, payload, isWeekend = false }) => (
<g transform={`translate(${x},${y})`}>
<text y={0} dy={16} textAnchor="middle" className="text-sm" fill={isWeekend ? "#ef4444" : "#6b7280"}>
{payload?.value}
</text>
</g>
)
);
KpiWeekendXAxisTick.displayName = "KpiWeekendXAxisTick";

type Props = {
distribution: TCycleCompletionChartDistribution;
totalEstimatePoints: number;
className?: string;
};

export const KpiBurndownChart: React.FC<Props> = ({ distribution, totalEstimatePoints, className = "" }) => {
const distributionKeys = Object.keys(distribution ?? []);
const stepCount = Math.max(distributionKeys.length - 1, 1);
const weekendLabels = new Set(
distributionKeys
.filter((key) => {
const date = getDate(key);
return !!date && [0, 6].includes(date.getDay());
})
.map((key) => renderFormattedDateWithoutYear(key))
);

const rawValues = distributionKeys
.map((key) => distribution[key])
.filter((value): value is number => typeof value === "number");

const shouldTreatAsNegativeCompleted = rawValues.some((value) => value < 0);
const shouldTreatAsCompletedProgress =
!shouldTreatAsNegativeCompleted && rawValues.length > 1 && rawValues[0] <= rawValues[rawValues.length - 1];

const normalizeCurrentValue = (value: number | null) => {
if (typeof value !== "number") return value;

let nextCurrent = value;

if (shouldTreatAsNegativeCompleted) {
nextCurrent = totalEstimatePoints + value;
} else if (shouldTreatAsCompletedProgress) {
nextCurrent = totalEstimatePoints - value;
}

return Math.min(totalEstimatePoints, Math.max(0, nextCurrent));
};

const xAxisConfig = {
key: "name",
label: "Time",
interval: 0,
minTickGap: 0,
ticks: distributionKeys.map((key) => renderFormattedDateWithoutYear(key)),
} as unknown as {
key: string;
label?: string;
strokeColor?: string;
dy?: number;
minTickGap?: number;
ticks?: Array<string | number>;
};

const chartData = distributionKeys.map((key, index) => ({
name: renderFormattedDateWithoutYear(key),
current: normalizeCurrentValue(distribution[key]),
ideal: totalEstimatePoints * (1 - index / stepCount),
})) as unknown as TChartData<string, string>[];

const WeekendTickComponent = ((props: unknown) => (
<KpiWeekendXAxisTick
{...(props as TKpiWeekendXAxisTickProps)}
isWeekend={weekendLabels.has((props as TKpiWeekendXAxisTickProps).payload?.value ?? "")}
/>
)) as ComponentType<unknown>;

return (
<div className={`flex w-full items-center justify-center ${className}`}>
<AreaChart
data={chartData}
areas={[
{
key: "current",
label: "Current remaining points",
strokeColor: "#3F76FF",
fill: "#3F76FF33",
fillOpacity: 1,
showDot: true,
smoothCurves: true,
strokeOpacity: 1,
stackId: "bar-one",
},
{
key: "ideal",
label: "Ideal remaining points",
strokeColor: "#A9BBD0",
fill: "#A9BBD0",
fillOpacity: 0,
showDot: true,
smoothCurves: true,
strokeOpacity: 1,
stackId: "bar-two",
style: {
strokeDasharray: "6, 3",
strokeWidth: 1,
},
},
]}
xAxis={xAxisConfig}
yAxis={{ key: "current", label: "Remaining points", domain: [0, Math.max(totalEstimatePoints, 1)] }}
customTicks={{ x: WeekendTickComponent }}
margin={{ bottom: 30 }}
className="h-[370px] w-full"
legend={{
align: "center",
verticalAlign: "bottom",
layout: "horizontal",
wrapperStyles: {
marginTop: 20,
},
}}
/>
</div>
);
};
Loading