Nexova / src /components /NoteCard.tsx
Nexova
refactor: server actions + SSR, fix backup/restore scripts, responsive
4a70373
"use client";
import { useState } from "react";
type Note = {
id: string;
title: string;
content: string;
color: string;
pinned: boolean;
createdAt: Date;
updatedAt: Date;
};
const COLORS: Record<string, string> = {
"#fef08a": "bg-yellow-200/10 border-yellow-500/30",
"#86efac": "bg-emerald-200/10 border-emerald-500/30",
"#93c5fd": "bg-blue-200/10 border-blue-500/30",
"#f9a8d4": "bg-pink-200/10 border-pink-500/30",
"#c4b5fd": "bg-violet-200/10 border-violet-500/30",
};
export default function NoteCard({
note,
onEdit,
onDelete,
onPin,
}: {
note: Note;
onEdit: () => void;
onDelete: () => void;
onPin: () => void;
}) {
const [showActions, setShowActions] = useState(false);
const cls = COLORS[note.color] ?? "bg-zinc-800/50 border-zinc-700";
const ago = timeAgo(note.updatedAt);
return (
<div
className={`border rounded-xl p-4 ${cls} transition hover:scale-[1.02] group`}
onClick={() => setShowActions((v) => !v)}
>
<div className="flex items-start justify-between mb-2">
<h3 className="font-semibold text-sm leading-tight flex-1 mr-2">
{note.pinned ? "πŸ“Œ " : ""}{note.title}
</h3>
<div className={`flex gap-1 transition ${showActions ? "opacity-100" : "opacity-0 group-hover:opacity-100"}`}>
<Btn onClick={(e) => { e.stopPropagation(); onPin(); }} title={note.pinned ? "Unpin" : "Pin"}>
{note.pinned ? "β—‡" : "β—†"}
</Btn>
<Btn onClick={(e) => { e.stopPropagation(); onEdit(); }} title="Edit">✎</Btn>
<Btn onClick={(e) => { e.stopPropagation(); onDelete(); }} title="Delete">βœ•</Btn>
</div>
</div>
{note.content && (
<p className="text-xs text-zinc-400 leading-relaxed line-clamp-4 mb-3">{note.content}</p>
)}
<div className="flex items-center justify-between">
<span
className="w-3 h-3 rounded-full inline-block"
style={{ backgroundColor: note.color }}
/>
<span className="text-[10px] text-zinc-500">{ago}</span>
</div>
</div>
);
}
function Btn({ onClick, title, children }: { onClick: (e: React.MouseEvent) => void; title: string; children: React.ReactNode }) {
return (
<button
onClick={onClick}
title={title}
className="w-7 h-7 flex items-center justify-center rounded-lg text-sm text-zinc-400 hover:text-white hover:bg-zinc-700 active:bg-zinc-600 transition"
>
{children}
</button>
);
}
function timeAgo(d: Date | string): string {
const t = new Date(d).getTime();
if (isNaN(t)) return "";
const diff = Date.now() - t;
const mins = Math.floor(diff / 60000);
if (mins < 1) return "just now";
if (mins < 60) return `${mins}m ago`;
const hrs = Math.floor(mins / 60);
if (hrs < 24) return `${hrs}h ago`;
return `${Math.floor(hrs / 24)}d ago`;
}