PulseMap_Agent / web /src /components /sidebar /SelectedLocationCard.tsx
krishnadhulipalla's picture
pulsemap 1.2
71c1c9d
import type { SelectMeta } from "../../lib/types";
const isEmoji = (s: string) => !!s && /\p{Extended_Pictographic}/u.test(s);
const cleanTitle = (t?: string) => {
if (!t) return t;
return t
.replace(/^(?:[a-z0-9]+-){1,3}(?=[A-Z])/i, "") // slug stuck to title (no space)
.replace(/^(?:[a-z0-9]+-){1,3}\s+/i, ""); // slug + space
};
export default function SelectedLocationCard({
selectedLL,
selectedMeta,
onClear,
reactionsById,
}: {
selectedLL: [number, number] | null;
selectedMeta: SelectMeta | null;
onClear: () => void;
reactionsById: Record<string, any>;
}) {
const photoSrc =
selectedMeta?.raw?.photo_url ??
selectedMeta?.raw?.photoUrl ??
selectedMeta?.raw?.image_url ??
selectedMeta?.raw?.imageUrl ??
(selectedMeta as any)?.photo_url ??
(selectedMeta as any)?.photoUrl ??
null;
const rid = selectedMeta?.rid || selectedMeta?.raw?.rid;
const rx = rid ? reactionsById[rid] : null;
const verifyCount = rx?.verify_count ?? 0;
const clearCount = rx?.clear_count ?? 0;
const showEmoji =
selectedMeta?.emoji && isEmoji(String(selectedMeta.emoji))
? String(selectedMeta.emoji)
: null;
const displayTitle = cleanTitle(selectedMeta?.title) || "Selected";
return (
<div className="block">
<label className="label">Selected location</label>
<div className="locationCard">
{selectedLL ? (
<>
<div className="locName flex items-center gap-2">
{showEmoji ? (
<span style={{ fontSize: 18, lineHeight: "18px" }}>
{showEmoji}
</span>
) : null}
<span>{displayTitle}</span>
</div>
{selectedMeta?.subtitle && (
<div className="muted">{selectedMeta.subtitle}</div>
)}
<div className="locLL">
{selectedLL[0].toFixed(4)}, {selectedLL[1].toFixed(4)}
</div>
<div className="mt-2 text-sm space-y-1">
{(selectedMeta?.category || selectedMeta?.raw?.category) && (
<div>
<b>Category:</b>{" "}
{selectedMeta.category || selectedMeta.raw?.category}
</div>
)}
{(selectedMeta?.severity !== undefined ||
selectedMeta?.raw?.severity !== undefined) && (
<div>
<b>Severity/Mag:</b>{" "}
{String(
selectedMeta?.severity ?? selectedMeta?.raw?.severity
)}
</div>
)}
<div>
<b>Confidence:</b>{" "}
{(() => {
const k = selectedMeta?.kind;
const fromMeta =
selectedMeta?.confidence ?? selectedMeta?.raw?.confidence;
const official =
k && ["nws", "quake", "eonet", "fire"].includes(k);
const val = fromMeta ?? (official ? 1 : undefined);
return val !== undefined ? String(val) : "—";
})()}
</div>
{selectedMeta?.raw?.source ? (
<div>
<b>Source:</b> {selectedMeta.raw.source}
</div>
) : selectedMeta?.kind &&
["nws", "quake", "eonet", "fire"].includes(
selectedMeta.kind
) ? (
<div>
<b>Source:</b> {selectedMeta.kind.toUpperCase()}
</div>
) : null}
{selectedMeta?.sourceUrl && (
<div>
<a
href={selectedMeta.sourceUrl}
className="link"
target="_blank"
rel="noreferrer"
>
Source
</a>
</div>
)}
</div>
</>
) : (
<div className="locDetecting">Use search, 📍, or click the map.</div>
)}
{rid ? (
<div className="text-xs muted">
Verified: {verifyCount} · Cleared: {clearCount}
</div>
) : null}
{photoSrc && (
<div className="mt-2" style={{ maxHeight: 220, overflow: "auto" }}>
<img
src={photoSrc}
alt="Attached"
style={{
width: "100%",
height: "auto",
borderRadius: 8,
objectFit: "contain",
display: "block",
}}
/>
</div>
)}
<div className="hint">
Only one point is active. Drag 📍 to fine-tune; chat uses this point.
</div>
{selectedLL && (
<div className="mt-2">
<button className="btn btn-ghost" onClick={onClear}>
Clear selection
</button>
</div>
)}
</div>
</div>
);
}