fix(drug-reference): responsive compare columns and dark-mode readability (#1163)
This commit is contained in:
parent
de6f453fa7
commit
fb99b3993b
|
|
@ -47,11 +47,11 @@ export default function DrugResultRow({ result, brandFirst = false }: Props) {
|
|||
return (
|
||||
<Link
|
||||
href={`/drug-reference/${result.id}`}
|
||||
className="flex items-center justify-between px-4 py-3 hover:bg-gray-50 transition-colors group"
|
||||
className="flex items-center justify-between px-4 py-3 hover:bg-surface-secondary transition-colors group"
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="font-semibold text-sm text-gray-900 group-hover:text-desert-green truncate">
|
||||
<span className="font-semibold text-sm text-text-primary group-hover:text-desert-green truncate">
|
||||
{headline}
|
||||
</span>
|
||||
|
||||
|
|
@ -67,14 +67,14 @@ export default function DrugResultRow({ result, brandFirst = false }: Props) {
|
|||
)}
|
||||
|
||||
{result.labelCount > 1 && (
|
||||
<span className="px-1.5 py-0.5 rounded text-xs bg-gray-100 text-gray-600 flex-shrink-0">
|
||||
<span className="px-1.5 py-0.5 rounded text-xs bg-surface-secondary text-text-secondary flex-shrink-0">
|
||||
{result.labelCount} labels
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{subParts.length > 0 && (
|
||||
<div className="flex flex-wrap gap-x-3 mt-0.5 text-xs text-gray-500">
|
||||
<div className="flex flex-wrap gap-x-3 mt-0.5 text-xs text-text-secondary">
|
||||
{subParts.map((p, i) => (
|
||||
<span key={i} className="truncate">
|
||||
{p}
|
||||
|
|
@ -84,7 +84,7 @@ export default function DrugResultRow({ result, brandFirst = false }: Props) {
|
|||
)}
|
||||
</div>
|
||||
|
||||
<span className="ml-3 text-gray-400 text-xs flex-shrink-0">›</span>
|
||||
<span className="ml-3 text-text-muted text-xs flex-shrink-0">›</span>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,14 +62,14 @@ function PhaseBlock({
|
|||
? 'text-green-700'
|
||||
: running
|
||||
? accentRunning
|
||||
: 'text-gray-500'
|
||||
: 'text-text-secondary'
|
||||
const barColor = failed
|
||||
? 'bg-red-500'
|
||||
: completed
|
||||
? 'bg-green-500'
|
||||
: running
|
||||
? accentRunning.replace('text-', 'bg-')
|
||||
: 'bg-gray-300'
|
||||
: 'bg-surface-elevated'
|
||||
|
||||
return (
|
||||
<div className="space-y-1">
|
||||
|
|
@ -83,14 +83,14 @@ function PhaseBlock({
|
|||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-gray-500">{explainer}</p>
|
||||
<p className="text-xs text-text-secondary">{explainer}</p>
|
||||
|
||||
{counterLeft && (
|
||||
<div className="text-xs text-gray-600 tabular-nums">{counterLeft}</div>
|
||||
<div className="text-xs text-text-secondary tabular-nums">{counterLeft}</div>
|
||||
)}
|
||||
|
||||
{(running || completed || failed) && (
|
||||
<div className="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||
<div className="w-full bg-surface-elevated rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
className={`h-2 rounded-full transition-all duration-500 ${barColor}`}
|
||||
style={{ width: `${failed ? 100 : pct}%` }}
|
||||
|
|
@ -206,7 +206,7 @@ export default function IngestStatus({ status, onRefresh, pollIntervalMs = 3000
|
|||
|
||||
const downloadTiming =
|
||||
status.phase === 'downloading' && elapsedMs !== null ? (
|
||||
<div className="flex flex-wrap items-center gap-x-3 text-xs text-gray-500 tabular-nums">
|
||||
<div className="flex flex-wrap items-center gap-x-3 text-xs text-text-secondary tabular-nums">
|
||||
<span>Elapsed {fmtDuration(elapsedMs)}</span>
|
||||
{dlBytes && <span>{dlBytes} this part</span>}
|
||||
</div>
|
||||
|
|
@ -214,11 +214,11 @@ export default function IngestStatus({ status, onRefresh, pollIntervalMs = 3000
|
|||
|
||||
const ingestTiming =
|
||||
status.phase === 'ingesting' && elapsedMs !== null ? (
|
||||
<div className="flex flex-wrap items-center gap-x-3 text-xs text-gray-500 tabular-nums">
|
||||
<div className="flex flex-wrap items-center gap-x-3 text-xs text-text-secondary tabular-nums">
|
||||
<span>Elapsed {fmtDuration(elapsedMs)}</span>
|
||||
{etaMs !== null && (
|
||||
<span>
|
||||
~{fmtDuration(etaMs)} left <span className="text-gray-400">(estimate)</span>
|
||||
~{fmtDuration(etaMs)} left <span className="text-text-muted">(estimate)</span>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -247,11 +247,11 @@ export default function IngestStatus({ status, onRefresh, pollIntervalMs = 3000
|
|||
counterLeft={
|
||||
ingest.state === 'running' || ingest.state === 'completed' ? (
|
||||
<span>
|
||||
<span className="text-sm font-semibold text-gray-800">
|
||||
<span className="text-sm font-semibold text-text-primary">
|
||||
{ingest.records.toLocaleString()}
|
||||
</span>
|
||||
{expected > 0 && (
|
||||
<span className="text-gray-400"> of ~{expected.toLocaleString()} labels</span>
|
||||
<span className="text-text-muted"> of ~{expected.toLocaleString()} labels</span>
|
||||
)}
|
||||
</span>
|
||||
) : undefined
|
||||
|
|
@ -262,7 +262,7 @@ export default function IngestStatus({ status, onRefresh, pollIntervalMs = 3000
|
|||
|
||||
{/* Reassurance while busy */}
|
||||
{busy && (
|
||||
<p className="text-xs text-gray-400">
|
||||
<p className="text-xs text-text-muted">
|
||||
Runs in the background — you can leave this page and it keeps going. Search turns on
|
||||
automatically when ingest finishes.
|
||||
</p>
|
||||
|
|
@ -270,7 +270,7 @@ export default function IngestStatus({ status, onRefresh, pollIntervalMs = 3000
|
|||
|
||||
{/* Ready footer */}
|
||||
{status.phase === 'ready' && status.lastUpdated && (
|
||||
<p className="text-xs text-gray-500">FDA data version {status.lastUpdated}.</p>
|
||||
<p className="text-xs text-text-secondary">FDA data version {status.lastUpdated}.</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default function IngredientGroup({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen((o) => !o)}
|
||||
className="flex w-full items-center gap-2 px-4 py-3 text-left hover:bg-gray-50 transition-colors"
|
||||
className="flex w-full items-center gap-2 px-4 py-3 text-left hover:bg-surface-secondary transition-colors"
|
||||
>
|
||||
<IconChevronDown
|
||||
size={16}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export default function LabelBlocks({
|
|||
}) {
|
||||
const blocks = parseLabelSection(text)
|
||||
if (blocks.length === 0) return null
|
||||
const bodyColor = tone === 'danger' ? 'text-red-800' : 'text-gray-800'
|
||||
const bodyColor = tone === 'danger' ? 'text-red-800' : 'text-text-primary'
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
{blocks.map((block, i) => (
|
||||
|
|
@ -39,7 +39,7 @@ function LabelBlockView({ block, bodyColor }: { block: LabelBlock; bodyColor: st
|
|||
|
||||
if (isLabelSectionHeader(block.text)) {
|
||||
return (
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-gray-500">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-text-secondary">
|
||||
{block.text!.replace(/^\s*\d{1,2}\s+/, '')}
|
||||
</p>
|
||||
)
|
||||
|
|
@ -48,7 +48,7 @@ function LabelBlockView({ block, bodyColor }: { block: LabelBlock; bodyColor: st
|
|||
return (
|
||||
<p className={`text-sm ${bodyColor} leading-relaxed`}>
|
||||
{block.label && (
|
||||
<span className="inline-block mr-1.5 px-1.5 py-0.5 rounded bg-gray-100 text-gray-600 text-xs font-semibold align-baseline">
|
||||
<span className="inline-block mr-1.5 px-1.5 py-0.5 rounded bg-surface-secondary text-text-secondary text-xs font-semibold align-baseline">
|
||||
{block.label}
|
||||
</span>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -547,7 +547,7 @@ export default function DrugReferenceIndex({
|
|||
onChange={handleQueryChange}
|
||||
autoFocus
|
||||
placeholder="Search a medicine by name — ibuprofen, Benadryl, loratadine…"
|
||||
className="w-full rounded-lg border border-desert-stone-lighter bg-white py-2.5 pl-10 pr-4 text-sm text-desert-green-darker transition focus:border-desert-green focus:outline-none focus:ring-2 focus:ring-desert-green/20"
|
||||
className="w-full rounded-lg border border-desert-stone-lighter bg-surface-primary py-2.5 pl-10 pr-4 text-sm text-desert-green-darker transition focus:border-desert-green focus:outline-none focus:ring-2 focus:ring-desert-green/20"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
@ -574,7 +574,7 @@ export default function DrugReferenceIndex({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setFiltersOpen((o) => !o)}
|
||||
className="ml-auto flex items-center gap-1 rounded-full border border-desert-stone-lighter bg-white px-3 py-1 text-xs text-desert-green-darker hover:border-desert-green"
|
||||
className="ml-auto flex items-center gap-1 rounded-full border border-desert-stone-lighter bg-surface-primary px-3 py-1 text-xs text-desert-green-darker hover:border-desert-green"
|
||||
>
|
||||
<IconAdjustmentsHorizontal size={14} />
|
||||
{route ? ROUTE_FRIENDLY[route] : 'Form'} · {sort === 'name' ? 'A–Z' : 'Best match'}
|
||||
|
|
@ -588,7 +588,7 @@ export default function DrugReferenceIndex({
|
|||
<select
|
||||
value={route ?? ''}
|
||||
onChange={(e) => handleRouteChange(e.target.value || null)}
|
||||
className="rounded-lg border border-desert-stone-lighter bg-white px-2 py-1 text-xs text-desert-green-darker focus:border-desert-green focus:outline-none"
|
||||
className="rounded-lg border border-desert-stone-lighter bg-surface-primary px-2 py-1 text-xs text-desert-green-darker focus:border-desert-green focus:outline-none"
|
||||
>
|
||||
<option value="">Any form</option>
|
||||
{ROUTE_OPTIONS.map((r) => (
|
||||
|
|
@ -603,7 +603,7 @@ export default function DrugReferenceIndex({
|
|||
<select
|
||||
value={sort}
|
||||
onChange={(e) => handleSortChange(e.target.value as 'relevance' | 'name')}
|
||||
className="rounded-lg border border-desert-stone-lighter bg-white px-2 py-1 text-xs text-desert-green-darker focus:border-desert-green focus:outline-none"
|
||||
className="rounded-lg border border-desert-stone-lighter bg-surface-primary px-2 py-1 text-xs text-desert-green-darker focus:border-desert-green focus:outline-none"
|
||||
>
|
||||
<option value="relevance">Best match</option>
|
||||
<option value="name">A–Z</option>
|
||||
|
|
@ -704,7 +704,7 @@ export default function DrugReferenceIndex({
|
|||
<button
|
||||
type="button"
|
||||
onClick={() => setBrowseOpen((o) => !o)}
|
||||
className="flex items-center gap-1 rounded-full border border-desert-stone-lighter bg-white px-3 py-1 text-xs text-desert-green-darker hover:border-desert-olive"
|
||||
className="flex items-center gap-1 rounded-full border border-desert-stone-lighter bg-surface-primary px-3 py-1 text-xs text-desert-green-darker hover:border-desert-olive"
|
||||
>
|
||||
<IconFirstAidKit size={14} />
|
||||
{anySituationSelected ? 'Add another situation' : 'Pick one or more situations'}
|
||||
|
|
@ -729,7 +729,7 @@ export default function DrugReferenceIndex({
|
|||
className={`rounded-full border px-3 py-1 text-sm transition-colors ${
|
||||
active
|
||||
? 'border-desert-olive bg-desert-olive text-white'
|
||||
: 'border-desert-stone-lighter bg-white text-desert-green-darker hover:border-desert-olive hover:bg-desert-olive/5'
|
||||
: 'border-desert-stone-lighter bg-surface-primary text-desert-green-darker hover:border-desert-olive hover:bg-desert-olive/5'
|
||||
}`}
|
||||
>
|
||||
{c.label}
|
||||
|
|
@ -977,7 +977,7 @@ function FilterPill({
|
|||
type="button"
|
||||
onClick={onClick}
|
||||
className={`rounded-full border px-3 py-1 text-sm transition-colors ${
|
||||
active ? activeClass : `border-desert-stone-lighter bg-white text-desert-green-darker ${hoverClass}`
|
||||
active ? activeClass : `border-desert-stone-lighter bg-surface-primary text-desert-green-darker ${hoverClass}`
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
|
||||
{isEmpty ? (
|
||||
// ── Empty state (no data ingested) ─────────────────────────────────
|
||||
<div className="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center">
|
||||
<div className="border-2 border-dashed border-border-default rounded-lg p-8 text-center">
|
||||
<p className="text-lg font-semibold mb-2">No FDA drug data yet</p>
|
||||
<p className="mb-6 opacity-70">
|
||||
Download the openFDA drug-label dataset to enable offline search and comparison.
|
||||
|
|
@ -173,9 +173,9 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
) : (
|
||||
<>
|
||||
{/* ── Drug picker ───────────────────────────────────────────────── */}
|
||||
<div className="mb-6 border border-gray-200 rounded-lg p-4 bg-gray-50">
|
||||
<div className="mb-6 border border-border-subtle rounded-lg p-4 bg-surface-secondary">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<h2 className="text-sm font-semibold text-gray-700">Select drugs to compare</h2>
|
||||
<h2 className="text-sm font-semibold text-text-primary">Select drugs to compare</h2>
|
||||
{atMax && (
|
||||
<span className="text-xs text-amber-700 bg-amber-100 border border-amber-200 rounded px-2 py-0.5">
|
||||
Maximum {MAX_COMPARE} drugs reached
|
||||
|
|
@ -208,7 +208,7 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
.map((id) => (
|
||||
<span
|
||||
key={id}
|
||||
className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-gray-300 text-gray-600 animate-pulse"
|
||||
className="inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-surface-elevated text-text-secondary animate-pulse"
|
||||
>
|
||||
#{id}
|
||||
<button
|
||||
|
|
@ -231,21 +231,21 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
onChange={handleQueryChange}
|
||||
placeholder="Search for a drug to add…"
|
||||
disabled={atMax}
|
||||
className="w-full border border-gray-300 rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-desert-green disabled:bg-gray-100 disabled:text-gray-400"
|
||||
className="w-full border border-border-default rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-desert-green disabled:bg-surface-secondary disabled:text-text-muted"
|
||||
/>
|
||||
|
||||
{/* Picker results */}
|
||||
{pickerLoading && (
|
||||
<div className="mt-2 text-xs text-gray-500">Searching…</div>
|
||||
<div className="mt-2 text-xs text-text-secondary">Searching…</div>
|
||||
)}
|
||||
{pickerError && (
|
||||
<div className="mt-2 text-xs text-red-600">{pickerError}</div>
|
||||
)}
|
||||
{pickerSearched && pickerResults.length === 0 && !pickerLoading && (
|
||||
<div className="mt-2 text-xs text-gray-500">No results for "{query}"</div>
|
||||
<div className="mt-2 text-xs text-text-secondary">No results for "{query}"</div>
|
||||
)}
|
||||
{pickerResults.length > 0 && !atMax && (
|
||||
<div className="mt-2 border border-gray-200 rounded-lg overflow-hidden divide-y divide-gray-200 max-h-64 overflow-y-auto bg-white">
|
||||
<div className="mt-2 border border-border-subtle rounded-lg overflow-hidden divide-y divide-border-subtle max-h-64 overflow-y-auto bg-surface-primary">
|
||||
{pickerResults.map((r) => (
|
||||
<PickerRow
|
||||
key={r.id}
|
||||
|
|
@ -260,19 +260,24 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
|
||||
{/* ── Comparison columns ────────────────────────────────────────── */}
|
||||
{selectedIds.length === 0 ? (
|
||||
<div className="text-center py-12 text-gray-400 border-2 border-dashed border-gray-200 rounded-lg">
|
||||
<div className="text-center py-12 text-text-muted border-2 border-dashed border-border-subtle rounded-lg">
|
||||
<p className="text-sm">Select drugs above to compare their labeled interaction warnings.</p>
|
||||
</div>
|
||||
) : loadingEntries ? (
|
||||
<div className="text-center py-12 text-gray-400">
|
||||
<div className="text-center py-12 text-text-muted">
|
||||
<p className="text-sm">Loading…</p>
|
||||
</div>
|
||||
) : (
|
||||
// Stacked single-column on phones; fixed-min-width columns that
|
||||
// scroll sideways (never crush) from sm: up, even at MAX_COMPARE.
|
||||
// Stacked single-column on phones. From sm: up the columns share
|
||||
// the available width instead of sitting at a fixed 15rem, so one
|
||||
// selection reads full-width and two split it in half. Label text
|
||||
// runs long (opioid interaction sections are hundreds of words),
|
||||
// and a narrow column next to empty space made it near-unreadable.
|
||||
// min-w-60 keeps the old width as a floor, so at MAX_COMPARE on a
|
||||
// narrow viewport they still scroll sideways rather than crush.
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:gap-3 sm:overflow-x-auto sm:pb-2">
|
||||
{entries.map((entry) => (
|
||||
<div key={entry.id} className="w-full sm:flex-none sm:w-60">
|
||||
<div key={entry.id} className="w-full sm:flex-1 sm:min-w-60">
|
||||
<InteractionColumn entry={entry} onRemove={removeId} />
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -282,7 +287,7 @@ export default function DrugReferenceInteractions({ ingestStatus, rowCount }: Pa
|
|||
)}
|
||||
|
||||
{/* ── Source citation (CC0, no-endorsement) ───────────────────────── */}
|
||||
<footer className="mt-8 pt-4 border-t border-gray-200 text-xs text-gray-500">
|
||||
<footer className="mt-8 pt-4 border-t border-border-subtle text-xs text-text-secondary">
|
||||
<strong>Source:</strong> U.S. Food & Drug Administration drug labeling, via{' '}
|
||||
<strong>openFDA</strong> — public domain (CC0 1.0). NOMAD is not affiliated with or
|
||||
endorsed by the FDA. Label data is provided as-is; do not rely on it for medical
|
||||
|
|
@ -305,14 +310,14 @@ interface PickerRowProps {
|
|||
|
||||
function PickerRow({ result, selected, onAdd }: PickerRowProps) {
|
||||
return (
|
||||
<div className="flex items-center justify-between px-3 py-2 hover:bg-gray-50">
|
||||
<div className="flex items-center justify-between px-3 py-2 hover:bg-surface-secondary">
|
||||
{/* Reuse the same visual pattern as DrugResultRow but without a Link */}
|
||||
<div className="min-w-0 flex-1 mr-3">
|
||||
<span className="text-sm font-medium text-gray-900 block truncate">
|
||||
<span className="text-sm font-medium text-text-primary block truncate">
|
||||
{result.brand_name ?? result.generic_name ?? 'Unknown'}
|
||||
</span>
|
||||
{result.brand_name && result.generic_name && (
|
||||
<span className="text-xs text-gray-500 italic">{result.generic_name}</span>
|
||||
<span className="text-xs text-text-secondary italic">{result.generic_name}</span>
|
||||
)}
|
||||
</div>
|
||||
{selected ? (
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ export default function DrugReferenceShow({ label, situations = [] }: PageProps)
|
|||
</div>
|
||||
|
||||
{label.brand_name && label.generic_name && (
|
||||
<p className="text-base text-gray-600 italic">{label.generic_name}</p>
|
||||
<p className="text-base text-text-secondary italic">{label.generic_name}</p>
|
||||
)}
|
||||
|
||||
<dl className="mt-3 grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-1 text-sm">
|
||||
|
|
@ -132,7 +132,7 @@ export default function DrugReferenceShow({ label, situations = [] }: PageProps)
|
|||
<Link
|
||||
key={s.slug}
|
||||
href={`/drug-reference?situation=${encodeURIComponent(s.slug)}`}
|
||||
className="rounded-full border border-desert-olive/40 bg-white px-3 py-1 text-sm text-desert-olive-dark transition-colors hover:border-desert-olive hover:bg-desert-olive hover:text-white"
|
||||
className="rounded-full border border-desert-olive/40 bg-surface-primary px-3 py-1 text-sm text-desert-olive-dark transition-colors hover:border-desert-olive hover:bg-desert-olive hover:text-white"
|
||||
>
|
||||
{s.label}
|
||||
</Link>
|
||||
|
|
@ -176,7 +176,7 @@ export default function DrugReferenceShow({ label, situations = [] }: PageProps)
|
|||
)}
|
||||
|
||||
{/* ── Footer citation ───────────────────────────────────────────────── */}
|
||||
<footer className="mt-8 pt-4 border-t border-gray-200 text-xs text-gray-500 space-y-1">
|
||||
<footer className="mt-8 pt-4 border-t border-border-subtle text-xs text-text-secondary space-y-1">
|
||||
<p>
|
||||
<strong>Source:</strong> U.S. Food & Drug Administration drug labeling, via{' '}
|
||||
<strong>openFDA</strong> — public domain (CC0 1.0). NOMAD is not affiliated with or
|
||||
|
|
@ -209,10 +209,10 @@ function LabelSection({
|
|||
}) {
|
||||
return (
|
||||
<section className="mb-6">
|
||||
<h2 className="text-base font-bold mb-2 border-b border-gray-200 pb-1">{title}</h2>
|
||||
<h2 className="text-base font-bold mb-2 border-b border-border-subtle pb-1">{title}</h2>
|
||||
<LabelBlocks text={body} />
|
||||
{footnote && (
|
||||
<p className="mt-2 text-xs text-gray-500 italic">{footnote}</p>
|
||||
<p className="mt-2 text-xs text-text-secondary italic">{footnote}</p>
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in New Issue