fix: ui bugs
This commit is contained in:
parent
cfe267d540
commit
7ecdc917d6
@ -19,6 +19,7 @@ import {
|
|||||||
} from "recharts"
|
} from "recharts"
|
||||||
import { Loader2, Sparkles } from "lucide-react"
|
import { Loader2, Sparkles } from "lucide-react"
|
||||||
import { useTheme } from "@/components/ThemeProvider"
|
import { useTheme } from "@/components/ThemeProvider"
|
||||||
|
import { chartTooltipProps } from "@/lib/charts/rechartsTooltip"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
type ModelEvaluationPayload = {
|
type ModelEvaluationPayload = {
|
||||||
@ -120,8 +121,7 @@ export default function AiAnalyticsPage() {
|
|||||||
|
|
||||||
const chartMuted = isLight ? "#64748b" : "#94a3b8"
|
const chartMuted = isLight ? "#64748b" : "#94a3b8"
|
||||||
const gridStroke = isLight ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.08)"
|
const gridStroke = isLight ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.08)"
|
||||||
const tooltipBg = isLight ? "#ffffff" : "#0f172a"
|
const tt = useMemo(() => chartTooltipProps(isLight), [isLight])
|
||||||
const tooltipBorder = isLight ? "#e2e8f0" : "rgba(255,255,255,0.12)"
|
|
||||||
|
|
||||||
const barData = useMemo(
|
const barData = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@ -284,15 +284,7 @@ export default function AiAnalyticsPage() {
|
|||||||
tickLine={false}
|
tickLine={false}
|
||||||
tickFormatter={(v) => `${v}%`}
|
tickFormatter={(v) => `${v}%`}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip {...tt} formatter={(v) => [`${Number(v ?? 0)}%`, "F1"]} />
|
||||||
contentStyle={{
|
|
||||||
borderRadius: 12,
|
|
||||||
border: `1px solid ${tooltipBorder}`,
|
|
||||||
background: tooltipBg,
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
formatter={(v) => [`${Number(v ?? 0)}%`, "F1"]}
|
|
||||||
/>
|
|
||||||
<Line type="monotone" dataKey="f1" stroke="#6366f1" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
|
<Line type="monotone" dataKey="f1" stroke="#6366f1" strokeWidth={3} dot={{ r: 4 }} activeDot={{ r: 6 }} />
|
||||||
</LineChart>
|
</LineChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
@ -402,12 +394,7 @@ export default function AiAnalyticsPage() {
|
|||||||
tickLine={false}
|
tickLine={false}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
{...tt}
|
||||||
borderRadius: 12,
|
|
||||||
border: `1px solid ${tooltipBorder}`,
|
|
||||||
background: tooltipBg,
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
formatter={(value, _name, item) => {
|
formatter={(value, _name, item) => {
|
||||||
const g = (item as { payload?: { gmailAutoReplies?: number } })?.payload
|
const g = (item as { payload?: { gmailAutoReplies?: number } })?.payload
|
||||||
?.gmailAutoReplies
|
?.gmailAutoReplies
|
||||||
@ -460,13 +447,8 @@ export default function AiAnalyticsPage() {
|
|||||||
tickLine={false}
|
tickLine={false}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
{...tt}
|
||||||
cursor={{ fill: isLight ? "rgba(99,102,241,0.06)" : "rgba(255,255,255,0.05)" }}
|
cursor={{ fill: isLight ? "rgba(99,102,241,0.06)" : "rgba(255,255,255,0.05)" }}
|
||||||
contentStyle={{
|
|
||||||
borderRadius: 12,
|
|
||||||
border: `1px solid ${tooltipBorder}`,
|
|
||||||
background: tooltipBg,
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
<Bar dataKey="count" radius={[0, 6, 6, 0]} barSize={18}>
|
<Bar dataKey="count" radius={[0, 6, 6, 0]} barSize={18}>
|
||||||
{barData.map((entry, i) => (
|
{barData.map((entry, i) => (
|
||||||
@ -508,14 +490,9 @@ export default function AiAnalyticsPage() {
|
|||||||
tickLine={false}
|
tickLine={false}
|
||||||
tickFormatter={(v) => `${v}m`}
|
tickFormatter={(v) => `${v}m`}
|
||||||
/>
|
/>
|
||||||
<Legend wrapperStyle={{ fontSize: 11, fontWeight: 600 }} />
|
<Legend wrapperStyle={{ fontSize: 11, fontWeight: 600, color: chartMuted }} />
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
{...tt}
|
||||||
borderRadius: 12,
|
|
||||||
border: `1px solid ${tooltipBorder}`,
|
|
||||||
background: tooltipBg,
|
|
||||||
fontWeight: 600,
|
|
||||||
}}
|
|
||||||
formatter={(value, name) =>
|
formatter={(value, name) =>
|
||||||
name === "AI"
|
name === "AI"
|
||||||
? [`${Number(value ?? 0)} min (median)`, "AI"]
|
? [`${Number(value ?? 0)} min (median)`, "AI"]
|
||||||
|
|||||||
@ -34,6 +34,7 @@ import {
|
|||||||
Legend,
|
Legend,
|
||||||
} from "recharts"
|
} from "recharts"
|
||||||
import { useTheme } from "@/components/ThemeProvider"
|
import { useTheme } from "@/components/ThemeProvider"
|
||||||
|
import { chartTooltipProps } from "@/lib/charts/rechartsTooltip"
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
type TimeRange = "7days" | "30days" | "1year"
|
type TimeRange = "7days" | "30days" | "1year"
|
||||||
@ -212,6 +213,7 @@ export default function DashboardPage() {
|
|||||||
const currency = business?.currency ?? "USD"
|
const currency = business?.currency ?? "USD"
|
||||||
const gridStroke = isLight ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.08)"
|
const gridStroke = isLight ? "rgba(0,0,0,0.06)" : "rgba(255,255,255,0.08)"
|
||||||
const chartMuted = isLight ? "#64748b" : "#94a3b8"
|
const chartMuted = isLight ? "#64748b" : "#94a3b8"
|
||||||
|
const chartTt = chartTooltipProps(isLight)
|
||||||
const donutColors = ["#6366f1", "#a855f7", "#f43f5e", "#f97316"]
|
const donutColors = ["#6366f1", "#a855f7", "#f43f5e", "#f97316"]
|
||||||
|
|
||||||
const kpiAccent: Record<string, string> = {
|
const kpiAccent: Record<string, string> = {
|
||||||
@ -384,12 +386,7 @@ export default function DashboardPage() {
|
|||||||
tickFormatter={(v) => formatMoney(Number(v), currency)}
|
tickFormatter={(v) => formatMoney(Number(v), currency)}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{
|
{...chartTt}
|
||||||
borderRadius: 12,
|
|
||||||
border: isLight ? "1px solid #e2e8f0" : "1px solid rgba(255,255,255,0.12)",
|
|
||||||
background: isLight ? "#fff" : "#0f172a",
|
|
||||||
fontWeight: 700,
|
|
||||||
}}
|
|
||||||
formatter={(v) => [formatMoney(Number(v ?? 0), currency), "Profit (est.)"]}
|
formatter={(v) => [formatMoney(Number(v ?? 0), currency), "Profit (est.)"]}
|
||||||
/>
|
/>
|
||||||
<Bar dataKey="profit" fill="#22c55e" radius={[6, 6, 0, 0]} />
|
<Bar dataKey="profit" fill="#22c55e" radius={[6, 6, 0, 0]} />
|
||||||
@ -425,16 +422,13 @@ export default function DashboardPage() {
|
|||||||
<Cell key={i} fill={donutColors[i % donutColors.length]} />
|
<Cell key={i} fill={donutColors[i % donutColors.length]} />
|
||||||
))}
|
))}
|
||||||
</Pie>
|
</Pie>
|
||||||
<Legend verticalAlign="middle" align="right" layout="vertical" wrapperStyle={{ fontSize: 11, fontWeight: 600 }} />
|
<Legend
|
||||||
<Tooltip
|
verticalAlign="middle"
|
||||||
formatter={(v) => formatMoney(Number(v ?? 0), currency)}
|
align="right"
|
||||||
contentStyle={{
|
layout="vertical"
|
||||||
borderRadius: 12,
|
wrapperStyle={{ fontSize: 11, fontWeight: 600, color: chartMuted }}
|
||||||
border: isLight ? "1px solid #e2e8f0" : "1px solid rgba(255,255,255,0.12)",
|
|
||||||
background: isLight ? "#fff" : "#0f172a",
|
|
||||||
fontWeight: 700,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
<Tooltip {...chartTt} formatter={(v) => formatMoney(Number(v ?? 0), currency)} />
|
||||||
</PieChart>
|
</PieChart>
|
||||||
</ResponsiveContainer>
|
</ResponsiveContainer>
|
||||||
</div>
|
</div>
|
||||||
@ -479,8 +473,8 @@ export default function DashboardPage() {
|
|||||||
<div className={cn("px-4 md:px-6 py-4 border-b flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2", isLight ? "border-slate-100" : "border-white/10")}>
|
<div className={cn("px-4 md:px-6 py-4 border-b flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2", isLight ? "border-slate-100" : "border-white/10")}>
|
||||||
<h3 className={cn("text-lg font-bold", isLight ? "text-slate-900" : "text-white")}>Product-level margins (est.)</h3>
|
<h3 className={cn("text-lg font-bold", isLight ? "text-slate-900" : "text-white")}>Product-level margins (est.)</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="overflow-x-auto">
|
<div className="table-horizontal-scroll">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm min-w-[520px]">
|
||||||
<thead>
|
<thead>
|
||||||
<tr className={cn("text-left text-[10px] uppercase font-bold tracking-wider", isLight ? "bg-slate-50 text-slate-500" : "bg-white/[0.06] text-slate-400")}>
|
<tr className={cn("text-left text-[10px] uppercase font-bold tracking-wider", isLight ? "bg-slate-50 text-slate-500" : "bg-white/[0.06] text-slate-400")}>
|
||||||
<th className="px-4 py-3">Product</th>
|
<th className="px-4 py-3">Product</th>
|
||||||
@ -520,7 +514,9 @@ export default function DashboardPage() {
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Lightbulb className={cn("w-5 h-5 shrink-0 mt-0.5", isLight ? "text-amber-600" : "text-amber-300")} />
|
<Lightbulb className={cn("w-5 h-5 shrink-0 mt-0.5", isLight ? "text-amber-600" : "text-amber-300")} />
|
||||||
<p className={cn("text-sm font-medium leading-relaxed", isLight ? "text-amber-950" : "text-amber-50")}>{business.aiInsight}</p>
|
<p className={cn("text-sm font-medium leading-relaxed", isLight ? "text-amber-950" : "text-amber-50")}>
|
||||||
|
{business.aiInsight}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -590,13 +586,9 @@ export default function DashboardPage() {
|
|||||||
tickFormatter={(val) => `$${val > 999 ? (val / 1000).toFixed(1) + "k" : val}`}
|
tickFormatter={(val) => `$${val > 999 ? (val / 1000).toFixed(1) + "k" : val}`}
|
||||||
/>
|
/>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
{...chartTt}
|
||||||
contentStyle={{
|
contentStyle={{
|
||||||
borderRadius: "12px",
|
...chartTt.contentStyle,
|
||||||
border: isLight ? "1px solid #e2e8f0" : "1px solid rgba(255,255,255,0.1)",
|
|
||||||
background: isLight ? "#ffffff" : "#0f172a",
|
|
||||||
color: isLight ? "#1e293b" : "#f1f5f9",
|
|
||||||
fontWeight: 700,
|
|
||||||
boxShadow: "0 10px 15px -3px rgba(0, 0, 0, 0.1)",
|
|
||||||
backdropFilter: isLight ? "none" : "blur(12px)",
|
backdropFilter: isLight ? "none" : "blur(12px)",
|
||||||
}}
|
}}
|
||||||
formatter={(value) => {
|
formatter={(value) => {
|
||||||
|
|||||||
@ -199,9 +199,10 @@ html {
|
|||||||
-webkit-transform: translateZ(0);
|
-webkit-transform: translateZ(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar styling */
|
/* Scrollbar styling — width + height so horizontal tracks aren’t oversized fat bars */
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
::-webkit-scrollbar-track {
|
||||||
@ -218,6 +219,30 @@ html {
|
|||||||
background: rgba(16, 185, 129, 0.5);
|
background: rgba(16, 185, 129, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] ::-webkit-scrollbar-track {
|
||||||
|
background: var(--dashboard-bg, #f9fafb);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] ::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(15, 23, 42, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] ::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(79, 70, 229, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nested horizontal scroll (e.g. data tables): thin bar, avoids a heavy slab above footers */
|
||||||
|
.table-horizontal-scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
overflow-y: visible;
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: rgba(255, 255, 255, 0.18) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme="light"] .table-horizontal-scroll {
|
||||||
|
scrollbar-color: rgba(15, 23, 42, 0.22) transparent;
|
||||||
|
}
|
||||||
|
|
||||||
/* Smooth scrolling container optimization */
|
/* Smooth scrolling container optimization */
|
||||||
[data-scroll-container] {
|
[data-scroll-container] {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
|
|||||||
@ -255,8 +255,9 @@ export default function SupplierDashboardPage() {
|
|||||||
<XAxis dataKey="label" tick={{ fill: "rgb(148 163 184)", fontSize: 12 }} />
|
<XAxis dataKey="label" tick={{ fill: "rgb(148 163 184)", fontSize: 12 }} />
|
||||||
<YAxis tick={{ fill: "rgb(148 163 184)", fontSize: 12 }} />
|
<YAxis tick={{ fill: "rgb(148 163 184)", fontSize: 12 }} />
|
||||||
<Tooltip
|
<Tooltip
|
||||||
contentStyle={{ backgroundColor: "rgb(30 41 59)", border: "1px solid rgb(51 65 85)", borderRadius: "8px" }}
|
contentStyle={{ backgroundColor: "rgb(30 41 59)", border: "1px solid rgb(51 65 85)", borderRadius: "8px", color: "rgb(248 250 252)" }}
|
||||||
labelStyle={{ color: "rgb(248 250 252)" }}
|
labelStyle={{ color: "rgb(248 250 252)" }}
|
||||||
|
itemStyle={{ color: "rgb(248 250 252)" }}
|
||||||
formatter={(value) => {
|
formatter={(value) => {
|
||||||
const num =
|
const num =
|
||||||
typeof value === "number" ? value : Number(value)
|
typeof value === "number" ? value : Number(value)
|
||||||
|
|||||||
31
src/lib/charts/rechartsTooltip.ts
Normal file
31
src/lib/charts/rechartsTooltip.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import type { CSSProperties } from "react"
|
||||||
|
|
||||||
|
export type RechartsTooltipChrome = {
|
||||||
|
contentStyle: CSSProperties
|
||||||
|
labelStyle: CSSProperties
|
||||||
|
itemStyle: CSSProperties
|
||||||
|
wrapperStyle: CSSProperties
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Theme-aware defaults for Recharts <Tooltip />. Recharts often ignores `color` on
|
||||||
|
* `contentStyle` for label/value rows — labelStyle + itemStyle keep dark mode readable.
|
||||||
|
*/
|
||||||
|
export function chartTooltipProps(isLight: boolean): RechartsTooltipChrome {
|
||||||
|
const fg = isLight ? "#0f172a" : "#f8fafc"
|
||||||
|
return {
|
||||||
|
contentStyle: {
|
||||||
|
borderRadius: 12,
|
||||||
|
border: isLight ? "1px solid #e2e8f0" : "1px solid rgba(255,255,255,0.16)",
|
||||||
|
background: isLight ? "#ffffff" : "#334155",
|
||||||
|
color: fg,
|
||||||
|
fontWeight: 700,
|
||||||
|
boxShadow: isLight
|
||||||
|
? "0 10px 15px -3px rgba(15, 23, 42, 0.08)"
|
||||||
|
: "0 14px 28px rgba(0, 0, 0, 0.55)",
|
||||||
|
},
|
||||||
|
labelStyle: { color: fg, fontWeight: 700 },
|
||||||
|
itemStyle: { color: fg },
|
||||||
|
wrapperStyle: { outline: "none", zIndex: 50 },
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user