DV: pill for recording URLs, show more/less toggle for longtext fields

This commit is contained in:
Bhanu Prakash Sai Potteri 2026-05-23 16:27:43 +05:30
parent 82934cd191
commit c45b7c9d28

View File

@ -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, Plus, AlertTriangle, 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,
@ -876,6 +876,8 @@ function SectionCard({
}).length; }).length;
const isEmpty = populated === 0; const isEmpty = populated === 0;
const [open, setOpen] = useState(!isEmpty); const [open, setOpen] = useState(!isEmpty);
const [expanded, setExpanded] = useState<Record<string, boolean>>({});
const LONGTEXT_PREVIEW = 220;
// White header with a small red number tag — editorial, no navy weight. // White header with a small red number tag — editorial, no navy weight.
const numberTag = ( const numberTag = (
@ -932,6 +934,10 @@ function SectionCard({
const empty = raw == null || raw === "" || raw === "—"; const empty = raw == null || raw === "" || raw === "—";
const text = empty ? "—" : fmtCellText(raw, f.data_type, f.field_key); const text = empty ? "—" : fmtCellText(raw, f.data_type, f.field_key);
const isProse = !empty && text.length > 60; const isProse = !empty && text.length > 60;
const isRecording = !empty && /recording_url$/i.test(f.field_key);
const isUrl = !empty && !isRecording && typeof raw === "string" && /^https?:\/\//i.test(raw);
const isLongText = !empty && f.data_type === "longtext" && text.length > LONGTEXT_PREVIEW;
const isOpen = !!expanded[f.field_key];
return ( return (
<div <div
key={f.field_key} key={f.field_key}
@ -942,9 +948,47 @@ function SectionCard({
</dt> </dt>
<dd <dd
className="text-[14px] break-words leading-relaxed flex-1 min-w-0" className="text-[14px] break-words leading-relaxed flex-1 min-w-0"
style={{ color: empty ? "#c8c6c2" : INK, fontWeight: empty || isProse ? 400 : 600 }} style={{ color: empty ? "#c8c6c2" : INK, fontWeight: empty || isProse || isRecording || isUrl ? 400 : 600 }}
> >
{text} {isRecording ? (
<a
href={String(raw)}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-[12.5px] font-semibold transition-opacity hover:opacity-90"
style={{ background: ACCENT_SOFT, color: ACCENT_DARK }}
>
<PlayCircle size={14} strokeWidth={2.25} />
Play recording
</a>
) : isUrl ? (
<a
href={String(raw)}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-1 underline decoration-stone-300 underline-offset-4 hover:decoration-stone-700"
style={{ color: ACCENT_DARK }}
>
<span className="truncate max-w-[420px]">{text}</span>
<ExternalLink size={12} strokeWidth={2.25} className="shrink-0 opacity-70" />
</a>
) : isLongText ? (
<span>
<span className="whitespace-pre-wrap">
{isOpen ? text : text.slice(0, LONGTEXT_PREVIEW).trimEnd() + "…"}
</span>
<button
type="button"
onClick={() => setExpanded((m) => ({ ...m, [f.field_key]: !isOpen }))}
className="ml-2 text-[12px] font-semibold underline-offset-4 hover:underline"
style={{ color: ACCENT_DARK }}
>
{isOpen ? "Show less" : "Show more"}
</button>
</span>
) : (
text
)}
</dd> </dd>
</div> </div>
); );