Files
places/src/sheets/save-to-collection-sheet.tsx
2026-05-20 15:40:17 +07:00

141 lines
4.6 KiB
TypeScript

"use client";
import { useState, useTransition } from "react";
import { useCollections } from "@/lib/app-context";
import type { Dispatch } from "@/lib/app-state";
import { Icons } from "@/components/icons";
import {
addPlaceToCollection,
removePlaceFromCollection,
} from "@/lib/db/actions";
export function SaveToCollectionSheet({
placeId,
onClose,
dispatch,
}: {
placeId: number;
onClose: () => void;
dispatch: Dispatch;
}) {
const collections = useCollections();
// Initial membership state derived from server data.
const [membership, setMembership] = useState<Record<string, boolean>>(() =>
Object.fromEntries(collections.map((c) => [c.id, c.place_ids.includes(placeId)])),
);
const [, startTransition] = useTransition();
const editable = collections.filter((c) => c.my_role !== "viewer");
const toggle = (cid: number) => {
const wasIn = !!membership[cid];
setMembership({ ...membership, [cid]: !wasIn });
startTransition(() => {
const op = wasIn
? removePlaceFromCollection(cid, placeId)
: addPlaceToCollection(cid, placeId);
op.catch(() => {
setMembership({ ...membership, [cid]: wasIn });
dispatch({ type: "TOAST", value: "Lưu thất bại" });
});
});
};
return (
<>
<div className="overlay" onClick={onClose} />
<div className="sheet" style={{ maxHeight: "70%" }}>
<div className="sheet-handle" />
<div
style={{
padding: "6px 16px 12px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
}}
>
<button
onClick={onClose}
style={{
background: "transparent",
border: 0,
color: "var(--muted-foreground)",
fontSize: 15,
fontWeight: 500,
padding: "8px 0",
}}
>
Xong
</button>
<div style={{ fontSize: 16, fontWeight: 600 }}>Lưu vào bộ sưu tập</div>
<div style={{ width: 48 }} />
</div>
<div style={{ overflowY: "auto", padding: "0 16px 16px" }}>
{editable.length === 0 ? (
<div
style={{
padding: "32px 16px",
textAlign: "center",
color: "var(--muted-foreground)",
fontSize: 14,
}}
>
Bạn chưa bộ sưu tập nào quyền chỉnh sửa. Tạo bộ sưu tập trước từ tab "Bộ sưu tập".
</div>
) : (
editable.map((c) => {
const inIt = !!membership[c.id];
const Icon = c.type === "trip" ? Icons.Plane : Icons.Folder;
return (
<button
key={c.id}
onClick={() => toggle(c.id)}
style={{
width: "100%",
display: "flex",
alignItems: "center",
gap: 14,
padding: "12px 4px",
background: "transparent",
border: 0,
borderBottom: "0.5px solid var(--border)",
textAlign: "left",
}}
>
<div
style={{
width: 36,
height: 36,
borderRadius: 10,
background: c.type === "trip" ? "var(--primary-soft)" : "var(--muted)",
color: c.type === "trip" ? "var(--primary)" : "var(--muted-foreground)",
display: "flex",
alignItems: "center",
justifyContent: "center",
flexShrink: 0,
}}
>
<Icon size={18} stroke={1.75} />
</div>
<div style={{ flex: 1, minWidth: 0 }}>
<div style={{ fontSize: 15, fontWeight: 500 }}>{c.name}</div>
<div style={{ fontSize: 12, color: "var(--muted-foreground)" }}>
{c.place_count} đa điểm
</div>
</div>
<div
className="checkbox"
data-checked={inIt}
style={{ pointerEvents: "none" }}
>
<Icons.Check size={14} stroke={3} />
</div>
</button>
);
})
)}
</div>
</div>
</>
);
}