DV: pill for recording URLs, show more/less toggle for longtext fields
This commit is contained in:
parent
82934cd191
commit
c45b7c9d28
@ -11,7 +11,7 @@ import {
|
||||
ChevronLeft, ChevronRight, Search, ArrowUp, ArrowDown, X, RefreshCw,
|
||||
CheckCircle2, LogOut, Sparkles, Car, ClipboardList, PhoneOutgoing,
|
||||
CalendarCheck2, MessageSquareHeart, Trophy, ArrowLeft, ChevronDown, ChevronUp,
|
||||
Plus, AlertTriangle,
|
||||
Plus, AlertTriangle, PlayCircle, ExternalLink,
|
||||
} from "lucide-react";
|
||||
import {
|
||||
BarChart as RBarChart, Bar, XAxis, YAxis, Tooltip, CartesianGrid,
|
||||
@ -876,6 +876,8 @@ function SectionCard({
|
||||
}).length;
|
||||
const isEmpty = populated === 0;
|
||||
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.
|
||||
const numberTag = (
|
||||
@ -932,6 +934,10 @@ function SectionCard({
|
||||
const empty = raw == null || raw === "" || raw === "—";
|
||||
const text = empty ? "—" : fmtCellText(raw, f.data_type, f.field_key);
|
||||
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 (
|
||||
<div
|
||||
key={f.field_key}
|
||||
@ -942,9 +948,47 @@ function SectionCard({
|
||||
</dt>
|
||||
<dd
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user