multica/apps/web/features/issues/components/board-card.tsx
Naiyuan Qing 4052017c7a feat: settings redesign, rich text mentions, inbox listeners, and UI polish
- Refactor settings page into tabbed components (general, workspace, members, tokens, account)
- Move settings link from dropdown to sidebar nav
- Add @mention suggestions in rich text editor
- Expand inbox listeners with enhanced event handling
- Improve board column, issue detail, and create issue modal UX
- Update markdown rendering and code block styling
- Polish skills page layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:17:14 +08:00

81 lines
2.2 KiB
TypeScript

"use client";
import Link from "next/link";
import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import type { Issue } from "@/shared/types";
import { CalendarDays } from "lucide-react";
import { ActorAvatar } from "@/components/common/actor-avatar";
import { PriorityIcon } from "./priority-icon";
function formatDate(date: string): string {
return new Date(date).toLocaleDateString("en-US", {
month: "short",
day: "numeric",
});
}
export function BoardCardContent({ issue }: { issue: Issue }) {
return (
<div className="rounded-lg border bg-card p-3">
<div className="flex items-center gap-1.5 text-xs text-muted-foreground">
<PriorityIcon priority={issue.priority} />
<span>{issue.id.slice(0, 8)}</span>
</div>
<p className="mt-1.5 text-sm leading-snug line-clamp-2">{issue.title}</p>
<div className="mt-2.5 flex items-center justify-between">
<div className="flex items-center gap-2">
{issue.assignee_type && issue.assignee_id && (
<ActorAvatar
actorType={issue.assignee_type}
actorId={issue.assignee_id}
size={20}
/>
)}
</div>
{issue.due_date && (
<span className={`flex items-center gap-1 text-xs ${new Date(issue.due_date) < new Date() ? "text-destructive" : "text-muted-foreground"}`}>
<CalendarDays className="size-3" />
{formatDate(issue.due_date)}
</span>
)}
</div>
</div>
);
}
export function DraggableBoardCard({ issue }: { issue: Issue }) {
const {
attributes,
listeners,
setNodeRef,
transform,
transition,
isDragging,
} = useSortable({
id: issue.id,
data: { status: issue.status },
});
const style = {
transform: CSS.Transform.toString(transform),
transition,
};
return (
<div
ref={setNodeRef}
style={style}
{...attributes}
{...listeners}
className={isDragging ? "opacity-30" : ""}
>
<Link
href={`/issues/${issue.id}`}
className={`block transition-colors hover:opacity-80 ${isDragging ? "pointer-events-none" : ""}`}
>
<BoardCardContent issue={issue} />
</Link>
</div>
);
}