Updating auth

This commit is contained in:
2026-04-04 08:28:43 -04:00
parent 35d07e8df1
commit f17e5061cd
78 changed files with 5266 additions and 1380 deletions

View File

@@ -0,0 +1,183 @@
import { useState } from "react";
import { api } from "../api";
import type { ListedMap } from "../types";
import "./MapListModal.css";
interface Props {
maps: ListedMap[];
selectedMapId: string | null;
onSelect: (id: string) => void;
onClose: () => void;
onMapsChange: (maps: ListedMap[]) => void;
}
/** Copy text to the clipboard; show a brief "Copied!" toast. */
function copyToClipboard(
text: string,
setCopied: (id: string | null) => void,
id: string,
) {
navigator.clipboard.writeText(text).then(() => {
setCopied(id);
setTimeout(() => setCopied(null), 1500);
});
}
function accessLabel(access: string): string {
switch (access) {
case "public_view":
return "Public (view)";
case "public_edit":
return "Public (edit)";
default:
return "Private";
}
}
export default function MapListModal({
maps,
selectedMapId,
onSelect,
onClose,
onMapsChange,
}: Props) {
const [copiedId, setCopiedId] = useState<string | null>(null);
const [togglingId, setTogglingId] = useState<string | null>(null);
async function handleFavoriteToggle(e: React.MouseEvent, map: ListedMap) {
e.stopPropagation();
setTogglingId(map.id);
try {
if (map.is_favorited) {
await api.unfavoriteMap(map.id);
} else {
await api.favoriteMap(map.id);
}
onMapsChange(
maps.map((m) =>
m.id === map.id ? { ...m, is_favorited: !m.is_favorited } : m,
),
);
} catch (err) {
console.error("Failed to toggle favorite", err);
} finally {
setTogglingId(null);
}
}
function handleCopyLink(e: React.MouseEvent, map: ListedMap) {
e.stopPropagation();
const link = `${window.location.origin}/map/${encodeURIComponent(map.id)}`;
copyToClipboard(link, setCopiedId, map.id);
}
function handleSelect(map: ListedMap) {
onSelect(map.id);
onClose();
}
return (
<div className="modal-backdrop" onClick={onClose}>
<div
className="modal map-list-modal"
onClick={(e) => e.stopPropagation()}
>
<div className="modal-header">
<h2>My Maps</h2>
<button className="modal-close" onClick={onClose} aria-label="Close">
</button>
</div>
{maps.length === 0 ? (
<p className="map-list-empty">
No maps yet. Click "+ New Map" to create one.
</p>
) : (
<div className="map-list-scroll">
{maps.map((map) => (
<div
key={map.id}
className={`map-list-row ${map.id === selectedMapId ? "active" : ""}`}
onClick={() => handleSelect(map)}
role="button"
tabIndex={0}
onKeyDown={(e) => e.key === "Enter" && handleSelect(map)}
>
<div className="map-list-main">
<span className="map-list-name">{map.name}</span>
<div className="map-list-meta">
<span className="map-list-owner">
by {map.owner_username}
</span>
<span
className={`map-access-badge access-${map.public_access}`}
>
{accessLabel(map.public_access)}
</span>
{map.user_role && (
<span className={`perm-role-badge role-${map.user_role}`}>
{map.user_role}
</span>
)}
{map.is_favorited && !map.user_role && (
<span className="map-fav-badge"> Favorited</span>
)}
</div>
</div>
<div className="map-list-actions">
{/* Favorite toggle */}
<button
className={`map-action-btn fav-btn ${map.is_favorited ? "fav-active" : ""}`}
onClick={(e) => handleFavoriteToggle(e, map)}
disabled={togglingId === map.id}
title={
map.is_favorited
? "Remove from favorites"
: "Add to favorites"
}
>
{map.is_favorited ? "★" : "☆"}
</button>
{/* Copy link */}
<button
className="map-action-btn copy-btn"
onClick={(e) => handleCopyLink(e, map)}
title="Copy link"
>
{copiedId === map.id ? (
<span className="copied-text"></span>
) : (
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<rect
x="9"
y="9"
width="13"
height="13"
rx="2"
ry="2"
/>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
)}
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
);
}