95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
"use client";
|
||
|
||
import { useEffect, useState } from "react";
|
||
import { useToast } from "@/lib/toast";
|
||
import type { Destination } from "@/lib/types";
|
||
|
||
const STORAGE_KEY = "nomadro-city-notes";
|
||
|
||
interface Props {
|
||
destinations: Destination[];
|
||
}
|
||
|
||
export default function CityNotes({ destinations }: Props) {
|
||
const { toast } = useToast();
|
||
const [slug, setSlug] = useState(destinations[0]?.slug || "bali");
|
||
const [notes, setNotes] = useState<Record<string, string>>({});
|
||
const [text, setText] = useState("");
|
||
const [ready, setReady] = useState(false);
|
||
const dest = destinations.find((d) => d.slug === slug);
|
||
|
||
useEffect(() => {
|
||
try {
|
||
const raw = localStorage.getItem(STORAGE_KEY);
|
||
if (raw) setNotes(JSON.parse(raw));
|
||
} catch { /* ignore */ }
|
||
setReady(true);
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (!ready) return;
|
||
setText(notes[slug] || "");
|
||
}, [slug, notes, ready]);
|
||
|
||
const save = () => {
|
||
const next = { ...notes, [slug]: text };
|
||
setNotes(next);
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||
toast(`${dest?.emoji || ""} 笔记已保存`);
|
||
};
|
||
|
||
const clear = () => {
|
||
const next = { ...notes };
|
||
delete next[slug];
|
||
setNotes(next);
|
||
setText("");
|
||
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||
toast("已清空该城笔记", "info");
|
||
};
|
||
|
||
const filled = Object.keys(notes).filter((k) => notes[k]?.trim()).length;
|
||
|
||
return (
|
||
<section className="section notes-section" id="notes">
|
||
<div className="container">
|
||
<div className="section-header reveal">
|
||
<span className="section-tag">📝 NOTES</span>
|
||
<h2>城市旅居笔记</h2>
|
||
<p>记录咖啡馆坐标、房东微信、避坑心得——只存在你的浏览器里</p>
|
||
</div>
|
||
|
||
<div className="notes-card reveal">
|
||
<div className="notes-sidebar">
|
||
{destinations.map((d) => (
|
||
<button
|
||
key={d.slug}
|
||
className={`notes-city${slug === d.slug ? " active" : ""}`}
|
||
onClick={() => setSlug(d.slug)}
|
||
>
|
||
<span>{d.emoji} {d.name}</span>
|
||
{notes[d.slug]?.trim() && <span className="notes-dot" />}
|
||
</button>
|
||
))}
|
||
<p className="notes-count">已记录 {filled} 座城市</p>
|
||
</div>
|
||
|
||
<div className="notes-editor">
|
||
<h3>{dest?.emoji} {dest?.name}</h3>
|
||
<textarea
|
||
className="notes-textarea"
|
||
rows={10}
|
||
placeholder={`写下关于 ${dest?.name || "这座城"} 的笔记…\n例如:好用的咖啡馆、长租价格、避坑提醒`}
|
||
value={text}
|
||
onChange={(e) => setText(e.target.value)}
|
||
/>
|
||
<div className="notes-actions">
|
||
<button className="btn btn-primary" onClick={save}>💾 保存</button>
|
||
<button className="btn btn-ghost" onClick={clear} disabled={!text.trim()}>清空</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
}
|