| "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`; |
| } |
|
|