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,
|
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>
|
||||||
);
|
);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user