[WEB-1616] chore: improving the graphs ui and label in analytics (#4872)
* chore: improving the graphs ui and label in analytics * chore: automatic width in tabel
This commit is contained in:
parent
9b79a66a90
commit
67ad958998
3 changed files with 115 additions and 96 deletions
|
|
@ -8,7 +8,7 @@ import { Tooltip } from "@plane/ui";
|
|||
// ui
|
||||
import { BarGraph } from "@/components/ui";
|
||||
// helpers
|
||||
import { generateBarColor, generateDisplayName } from "@/helpers/analytics.helper";
|
||||
import { generateBarColor, generateDisplayName, renderChartDynamicLabel } from "@/helpers/analytics.helper";
|
||||
import { findStringWithMostCharacters } from "@/helpers/array.helper";
|
||||
// types
|
||||
import { CustomTooltip } from "./custom-tooltip";
|
||||
|
|
@ -66,7 +66,7 @@ export const AnalyticsGraph: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
height={fullScreen ? "400px" : "300px"}
|
||||
margin={{
|
||||
right: 20,
|
||||
bottom: params.x_axis === "assignees__id" ? 50 : longestXAxisLabel.length * 5 + 20,
|
||||
bottom: params.x_axis === "assignees__id" ? 50 : renderChartDynamicLabel(longestXAxisLabel)?.length * 5 + 20,
|
||||
}}
|
||||
axisBottom={{
|
||||
tickSize: 0,
|
||||
|
|
@ -111,18 +111,20 @@ export const AnalyticsGraph: React.FC<Props> = ({ analytics, barGraphData, param
|
|||
);
|
||||
}
|
||||
: (datum) => (
|
||||
<g transform={`translate(${datum.x},${datum.y + 10})`}>
|
||||
<text
|
||||
x={0}
|
||||
y={datum.y}
|
||||
textAnchor={`${barGraphData.data.length > 7 ? "end" : "middle"}`}
|
||||
fontSize={10}
|
||||
fill="rgb(var(--color-text-200))"
|
||||
className={`${barGraphData.data.length > 7 ? "-rotate-45" : ""}`}
|
||||
>
|
||||
{generateDisplayName(datum.value, analytics, params, "x_axis")}
|
||||
</text>
|
||||
</g>
|
||||
<Tooltip tooltipContent={generateDisplayName(datum.value, analytics, params, "x_axis")}>
|
||||
<g transform={`translate(${datum.x},${datum.y + 20})`}>
|
||||
<text
|
||||
x={0}
|
||||
y={datum.y}
|
||||
textAnchor={`${barGraphData.data.length > 7 ? "end" : "middle"}`}
|
||||
fontSize={10}
|
||||
fill="rgb(var(--color-text-200))"
|
||||
className={`${barGraphData.data.length > 7 ? "-rotate-45" : ""}`}
|
||||
>
|
||||
{renderChartDynamicLabel(generateDisplayName(datum.value, analytics, params, "x_axis"))?.label}
|
||||
</text>
|
||||
</g>
|
||||
</Tooltip>
|
||||
),
|
||||
}}
|
||||
theme={{
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
"use client";
|
||||
|
||||
import { BarDatum } from "@nivo/bar";
|
||||
// icons
|
||||
import { IAnalyticsParams, IAnalyticsResponse, TIssuePriorities } from "@plane/types";
|
||||
import { PriorityIcon } from "@plane/ui";
|
||||
import { PriorityIcon, Tooltip } from "@plane/ui";
|
||||
// helpers
|
||||
import { ANALYTICS_X_AXIS_VALUES, ANALYTICS_Y_AXIS_VALUES } from "@/constants/analytics";
|
||||
import { generateBarColor, generateDisplayName } from "@/helpers/analytics.helper";
|
||||
// types
|
||||
// constants
|
||||
import { generateBarColor, generateDisplayName, renderChartDynamicLabel } from "@/helpers/analytics.helper";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
|
||||
type Props = {
|
||||
analytics: IAnalyticsResponse;
|
||||
|
|
@ -21,80 +19,87 @@ type Props = {
|
|||
};
|
||||
|
||||
export const AnalyticsTable: React.FC<Props> = ({ analytics, barGraphData, params, yAxisKey }) => (
|
||||
<div className="flow-root">
|
||||
<div className="overflow-x-auto">
|
||||
<div className="inline-block min-w-full align-middle">
|
||||
<table className="min-w-full divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
|
||||
<thead className="bg-custom-background-80">
|
||||
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium">
|
||||
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
|
||||
</th>
|
||||
{params.segment ? (
|
||||
barGraphData.xAxisKeys.map((key) => (
|
||||
<th
|
||||
key={`segment-${key}`}
|
||||
scope="col"
|
||||
className={`px-2.5 py-3 text-left font-medium ${
|
||||
params.segment === "priority" || params.segment === "state__group" ? "capitalize" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{params.segment === "priority" ? (
|
||||
<PriorityIcon priority={key as TIssuePriorities} />
|
||||
) : (
|
||||
<span
|
||||
className="h-3 w-3 flex-shrink-0 rounded"
|
||||
style={{
|
||||
backgroundColor: generateBarColor(key, analytics, params, "segment"),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{generateDisplayName(key, analytics, params, "segment")}
|
||||
</div>
|
||||
</th>
|
||||
))
|
||||
) : (
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium sm:pr-0">
|
||||
{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === params.y_axis)?.label}
|
||||
</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-custom-border-200">
|
||||
{barGraphData.data.map((item, index) => (
|
||||
<tr key={`table-row-${index}`} className="divide-x divide-custom-border-200 text-xs text-custom-text-200">
|
||||
<td
|
||||
className={`flex items-center gap-2 whitespace-nowrap px-2.5 py-2 font-medium ${
|
||||
params.x_axis === "priority" || params.x_axis === "state__group" ? "capitalize" : ""
|
||||
}`}
|
||||
>
|
||||
{params.x_axis === "priority" ? (
|
||||
<PriorityIcon priority={item.name as TIssuePriorities} />
|
||||
<div className="w-full overflow-hidden overflow-x-auto">
|
||||
<table className="w-full overflow-hidden divide-y divide-custom-border-200 whitespace-nowrap border-y border-custom-border-200">
|
||||
<thead className="bg-custom-background-80">
|
||||
<tr className="divide-x divide-custom-border-200 text-sm text-custom-text-100">
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium">
|
||||
{ANALYTICS_X_AXIS_VALUES.find((v) => v.value === params.x_axis)?.label}
|
||||
</th>
|
||||
{params.segment ? (
|
||||
barGraphData.xAxisKeys.map((key) => (
|
||||
<th
|
||||
key={`segment-${key}`}
|
||||
scope="col"
|
||||
className={`px-2.5 py-3 text-left font-medium ${
|
||||
params.segment === "priority" || params.segment === "state__group" ? "capitalize" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{params.segment === "priority" ? (
|
||||
<PriorityIcon priority={key as TIssuePriorities} />
|
||||
) : (
|
||||
<span
|
||||
className="h-3 w-3 rounded"
|
||||
className="h-3 w-3 flex-shrink-0 rounded"
|
||||
style={{
|
||||
backgroundColor: generateBarColor(key, analytics, params, "segment"),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{renderChartDynamicLabel(generateDisplayName(key, analytics, params, "segment"))?.label}
|
||||
</div>
|
||||
</th>
|
||||
))
|
||||
) : (
|
||||
<th scope="col" className="px-2.5 py-3 text-left font-medium sm:pr-0">
|
||||
{ANALYTICS_Y_AXIS_VALUES.find((v) => v.value === params.y_axis)?.label}
|
||||
</th>
|
||||
)}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-custom-border-200">
|
||||
{barGraphData.data.map((item, index) => (
|
||||
<tr key={`table-row-${index}`} className="divide-x divide-custom-border-200 text-xs text-custom-text-200">
|
||||
<td className="px-2.5 py-2">
|
||||
<div className="relative flex items-center gap-2 w-full overflow-hidden">
|
||||
<div className="flex-shrink-0 h-3 w-3 rounded overflow-hidden">
|
||||
{params.x_axis === "priority" ? (
|
||||
<PriorityIcon priority={(item.name as string).toLowerCase() as TIssuePriorities} />
|
||||
) : (
|
||||
<div
|
||||
className="w-full h-full"
|
||||
style={{
|
||||
backgroundColor: generateBarColor(`${item.name}`, analytics, params, "x_axis"),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{generateDisplayName(`${item.name}`, analytics, params, "x_axis")}
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"font-medium",
|
||||
["priority", "state__group"].includes(params.x_axis) ? `capitalize` : ``
|
||||
)}
|
||||
>
|
||||
<Tooltip tooltipContent={generateDisplayName(`${item.name}`, analytics, params, "x_axis")}>
|
||||
<div className="overflow-hidden w-full whitespace-normal break-words truncate line-clamp-1">
|
||||
{generateDisplayName(`${item.name}`, analytics, params, "x_axis")}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
{params.segment ? (
|
||||
barGraphData.xAxisKeys.map((key, index) => (
|
||||
<td key={`segment-value-${index}`} className="whitespace-nowrap px-2.5 py-2 sm:pr-0">
|
||||
{item[key] ?? 0}
|
||||
</td>
|
||||
{params.segment ? (
|
||||
barGraphData.xAxisKeys.map((key, index) => (
|
||||
<td key={`segment-value-${index}`} className="whitespace-nowrap px-2.5 py-2 sm:pr-0">
|
||||
{item[key] ?? 0}
|
||||
</td>
|
||||
))
|
||||
) : (
|
||||
<td className="whitespace-nowrap px-2.5 py-2 sm:pr-0">{item[yAxisKey]}</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<td className="whitespace-nowrap px-2.5 py-2 sm:pr-0">{item[yAxisKey]}</td>
|
||||
)}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ export const convertResponseToBarGraphData = (
|
|||
name: DATE_KEYS.includes(params.x_axis)
|
||||
? renderMonthAndYear(key)
|
||||
: params.x_axis === "priority" || params.x_axis === "state__group"
|
||||
? capitalizeFirstLetter(key)
|
||||
: key,
|
||||
? capitalizeFirstLetter(key)
|
||||
: key,
|
||||
...segments,
|
||||
});
|
||||
} else {
|
||||
|
|
@ -49,8 +49,8 @@ export const convertResponseToBarGraphData = (
|
|||
name: DATE_KEYS.includes(params.x_axis)
|
||||
? renderMonthAndYear(item.dimension)
|
||||
: params.x_axis === "priority" || params.x_axis === "state__group"
|
||||
? capitalizeFirstLetter(item.dimension ?? "None")
|
||||
: item.dimension ?? "None",
|
||||
? capitalizeFirstLetter(item.dimension ?? "None")
|
||||
: item.dimension ?? "None",
|
||||
[yAxisKey]: item[yAxisKey] ?? 0,
|
||||
});
|
||||
}
|
||||
|
|
@ -84,12 +84,12 @@ export const generateBarColor = (
|
|||
priority === "urgent"
|
||||
? "#ef4444"
|
||||
: priority === "high"
|
||||
? "#f97316"
|
||||
: priority === "medium"
|
||||
? "#eab308"
|
||||
: priority === "low"
|
||||
? "#22c55e"
|
||||
: "#ced4da";
|
||||
? "#f97316"
|
||||
: priority === "medium"
|
||||
? "#eab308"
|
||||
: priority === "low"
|
||||
? "#22c55e"
|
||||
: "#ced4da";
|
||||
}
|
||||
|
||||
return color ?? generateRandomColor(value);
|
||||
|
|
@ -139,3 +139,15 @@ export const renderMonthAndYear = (date: string | number | null): string => {
|
|||
|
||||
return (MONTHS_LIST[monthNumber]?.shortTitle ?? "None") + ` ${year}` ?? "";
|
||||
};
|
||||
|
||||
export const MAX_CHART_LABEL_LENGTH = 15;
|
||||
export const renderChartDynamicLabel = (
|
||||
label: string,
|
||||
length: number = MAX_CHART_LABEL_LENGTH
|
||||
): { label: string; length: number } => {
|
||||
const currentLabel = label.substring(0, length);
|
||||
return {
|
||||
label: `${label.length > MAX_CHART_LABEL_LENGTH ? `${currentLabel.substring(0, MAX_CHART_LABEL_LENGTH - 3)}...` : currentLabel}`,
|
||||
length: currentLabel.length,
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue