feat: code-first migration — replace raw IDs with codes and usernames

- TaskDetailPage: show task_code instead of raw #id in header
- TaskDetailPage: show author_username in comment metadata
- ProjectDetailPage: show member username instead of User #id
- ProposeDetailPage: show created_by_username instead of User #id
- TasksPage: fix status filter options to match actual statuses
  (pending/open/undergoing/completed/closed)
- TasksPage: fix status color map for correct status values
- Types: add username/full_name to ProjectMember, author_username
  to Comment, created_by_username to Propose
- Supports TODO §3.1 (code-first UI migration)
This commit is contained in:
zhi
2026-03-21 20:28:35 +00:00
parent dc97764e43
commit fb5658739b
5 changed files with 13 additions and 9 deletions

View File

@@ -92,7 +92,7 @@ export default function ProjectDetailPage() {
<div className="member-list">
{members.map((m) => (
<span key={m.id} className="badge" style={{ marginRight: 8 }}>
{`User #${m.user_id} (${m.role})`}
{`${m.username || m.full_name || `User #${m.user_id}`} (${m.role})`}
{canEditProject && (
<button
onClick={(e) => { e.stopPropagation(); removeMember(m.user_id, m.role) }}

View File

@@ -138,7 +138,7 @@ export default function ProposeDetailPage() {
<div className="detail-grid">
<div><strong>Propose Code:</strong> {propose.propose_code || '—'}</div>
<div><strong>Status:</strong> {propose.status}</div>
<div><strong>Created By:</strong> User #{propose.created_by_id || '—'}</div>
<div><strong>Created By:</strong> {propose.created_by_username || (propose.created_by_id ? `User #${propose.created_by_id}` : '—')}</div>
<div><strong>Created:</strong> {dayjs(propose.created_at).format('YYYY-MM-DD HH:mm')}</div>
<div><strong>Updated:</strong> {propose.updated_at ? dayjs(propose.updated_at).format('YYYY-MM-DD HH:mm') : '—'}</div>
<div><strong>Feature Task:</strong> {propose.feat_task_id || '—'}</div>

View File

@@ -120,7 +120,7 @@ export default function TaskDetailPage() {
<button className="btn-back" onClick={() => navigate('/tasks')}> Back</button>
<div className="task-header">
<h2>#{task.id} {task.title}</h2>
<h2>{task.task_code ? `[${task.task_code}]` : `#${task.id}`} {task.title}</h2>
<div className="task-meta">
<span className={`badge status-${task.status}`}>{task.status}</span>
<span className={`badge priority-${task.priority}`}>{task.priority}</span>
@@ -261,7 +261,7 @@ export default function TaskDetailPage() {
<h3>Comments ({comments.length})</h3>
{comments.map((c) => (
<div className="comment" key={c.id}>
<div className="comment-meta">User #{c.author_id} · {dayjs(c.created_at).format('MM-DD HH:mm')}</div>
<div className="comment-meta">{c.author_username || `User #${c.author_id}`} · {dayjs(c.created_at).format('MM-DD HH:mm')}</div>
<p>{c.content}</p>
</div>
))}

View File

@@ -27,8 +27,8 @@ export default function TasksPage() {
useEffect(() => { fetchTasks() }, [page, statusFilter, priorityFilter])
const statusColors: Record<string, string> = {
open: '#3b82f6', in_progress: '#f59e0b', resolved: '#10b981',
closed: '#6b7280', blocked: '#ef4444',
pending: '#9ca3af', open: '#3b82f6', undergoing: '#f59e0b',
completed: '#10b981', closed: '#6b7280',
}
return (
@@ -50,11 +50,11 @@ export default function TasksPage() {
<div className="filters">
<select value={statusFilter} onChange={(e) => { setStatusFilter(e.target.value); setPage(1) }}>
<option value="">All statuses</option>
<option value="pending">Pending</option>
<option value="open">Open</option>
<option value="in_progress">In Progress</option>
<option value="resolved">Resolved</option>
<option value="undergoing">Undergoing</option>
<option value="completed">Completed</option>
<option value="closed">Closed</option>
<option value="blocked">Blocked</option>
</select>
</div>

View File

@@ -27,6 +27,8 @@ export interface Project {
export interface ProjectMember {
id: number
user_id: number
username: string | null
full_name: string | null
project_id: number
role: string
}
@@ -59,6 +61,7 @@ export interface Comment {
content: string
task_id: number
author_id: number
author_username: string | null
created_at: string
updated_at: string | null
}
@@ -132,6 +135,7 @@ export interface Propose {
status: 'open' | 'accepted' | 'rejected'
project_id: number
created_by_id: number | null
created_by_username: string | null
feat_task_id: string | null
created_at: string
updated_at: string | null