DV: drop phase dots from hero; remove chart focus outline on click
This commit is contained in:
parent
c45b7c9d28
commit
0621bd0a67
@ -11,7 +11,7 @@ import {
|
|||||||
ChevronLeft, ChevronRight, Search, ArrowUp, ArrowDown, X, RefreshCw,
|
ChevronLeft, ChevronRight, Search, ArrowUp, ArrowDown, X, RefreshCw,
|
||||||
CheckCircle2, LogOut, Sparkles, Car, ClipboardList, PhoneOutgoing,
|
CheckCircle2, LogOut, Sparkles, Car, ClipboardList, PhoneOutgoing,
|
||||||
CalendarCheck2, MessageSquareHeart, Trophy, ArrowLeft, ChevronDown, ChevronUp,
|
CalendarCheck2, MessageSquareHeart, Trophy, ArrowLeft, ChevronDown, ChevronUp,
|
||||||
Plus, AlertTriangle, PlayCircle, ExternalLink,
|
Plus, PlayCircle, ExternalLink,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import {
|
import {
|
||||||
BarChart as RBarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid,
|
BarChart as RBarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid,
|
||||||
@ -67,68 +67,6 @@ const STATE_TONE: Record<string, { bg: string; fg: string; dot: string }> = {
|
|||||||
};
|
};
|
||||||
function tone(name: string) { return STATE_TONE[name] || { bg: "#F5F5F4", fg: "#57534E", dot: "#A8A29E" }; }
|
function tone(name: string) { return STATE_TONE[name] || { bg: "#F5F5F4", fg: "#57534E", dot: "#A8A29E" }; }
|
||||||
|
|
||||||
const PHASES = ["Captured", "Scheduling", "Test drive", "Feedback"] as const;
|
|
||||||
function phaseIndexFor(state: string | null | undefined): number {
|
|
||||||
if (!state) return 0;
|
|
||||||
if (state === STATE_IDS.AWAITING_FEEDBACK_CALL || state === STATE_IDS.FEEDBACK_CALL_FAILED) return 3;
|
|
||||||
if (state === STATE_IDS.FEEDBACK_COLLECTED || state === STATE_IDS.END_STATE) return 3;
|
|
||||||
if (state === STATE_IDS.SCHEDULED) return 2;
|
|
||||||
if (state === STATE_IDS.AWAITING_SCHEDULER_CALL || state === STATE_IDS.SCHEDULING_CALL_FAILED) return 1;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
function failedPhaseIndex(state: string | null | undefined): number {
|
|
||||||
if (state === STATE_IDS.SCHEDULING_CALL_FAILED) return 1;
|
|
||||||
if (state === STATE_IDS.FEEDBACK_CALL_FAILED) return 3;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
function PhaseDots({ state }: { state: string | null | undefined }) {
|
|
||||||
const current = phaseIndexFor(state);
|
|
||||||
const failedAt = failedPhaseIndex(state);
|
|
||||||
return (
|
|
||||||
<div className="mt-4 flex items-center gap-2.5 flex-wrap">
|
|
||||||
{PHASES.map((label, i) => {
|
|
||||||
const reached = i <= current;
|
|
||||||
const isFailed = i === failedAt;
|
|
||||||
return (
|
|
||||||
<React.Fragment key={label}>
|
|
||||||
<div className="flex items-center gap-1.5">
|
|
||||||
<span
|
|
||||||
className="w-2.5 h-2.5 rounded-full shrink-0"
|
|
||||||
style={
|
|
||||||
isFailed
|
|
||||||
? { background: "#e31837", boxShadow: "0 0 0 2px rgba(227,24,55,0.35)" }
|
|
||||||
: reached
|
|
||||||
? { background: "#e31837" }
|
|
||||||
: { background: "transparent", border: "2px solid rgba(255,255,255,0.4)" }
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<span className={`flex items-center gap-1 text-[11px] font-semibold uppercase tracking-[0.08em] ${reached ? "text-white" : "text-white/50"}`}>
|
|
||||||
{label}
|
|
||||||
{isFailed && (
|
|
||||||
<span className="flex items-center gap-0.5 ml-0.5" style={{ color: "#ff8a9c" }} title="Stalled — call failed">
|
|
||||||
<AlertTriangle size={10} strokeWidth={2.5} />
|
|
||||||
<span className="text-[9.5px] tracking-[0.1em]">STALLED</span>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
{i < PHASES.length - 1 && (
|
|
||||||
<span
|
|
||||||
className="w-5 h-px"
|
|
||||||
style={
|
|
||||||
isFailed
|
|
||||||
? { background: "transparent", borderTop: "1px dashed rgba(227,24,55,0.55)" }
|
|
||||||
: { background: "rgba(255,255,255,0.2)" }
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</React.Fragment>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function iconFor(label: string) {
|
function iconFor(label: string) {
|
||||||
const k = label.toLowerCase();
|
const k = label.toLowerCase();
|
||||||
if (k.includes("lead")) return <ClipboardList size={15} />;
|
if (k.includes("lead")) return <ClipboardList size={15} />;
|
||||||
@ -374,7 +312,10 @@ function DashboardContent({
|
|||||||
) : data.length === 0 ? (
|
) : data.length === 0 ? (
|
||||||
<div className="h-[260px] flex items-center justify-center text-[13px] text-stone-400">No data yet</div>
|
<div className="h-[260px] flex items-center justify-center text-[13px] text-stone-400">No data yet</div>
|
||||||
) : (
|
) : (
|
||||||
<div style={{ width: "100%", height: 260 }}>
|
<div
|
||||||
|
style={{ width: "100%", height: 260 }}
|
||||||
|
className="[&_.recharts-sector]:outline-none [&_.recharts-rectangle]:outline-none [&_.recharts-surface]:outline-none"
|
||||||
|
>
|
||||||
<ResponsiveContainer>
|
<ResponsiveContainer>
|
||||||
{c.chartType === "pie" ? (
|
{c.chartType === "pie" ? (
|
||||||
<RPieChart>
|
<RPieChart>
|
||||||
@ -734,7 +675,6 @@ function DetailPage({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<PhaseDots state={instanceState} />
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right column — state badge stacked over action buttons. Fills the dead space. */}
|
{/* Right column — state badge stacked over action buttons. Fills the dead space. */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user