refactor: UUID-based member refs in API types and components
- Task interface: assignee_id/assignee, reviewer_id/reviewer, watcher_ids - Message interface: author_id/author вместо author_slug - API functions: assignTask использует assignee_id - KanbanBoard: обновлен под новые поля задач - TaskModal: assignee selection по ID вместо slug - ChatPanel: отображение author через relationship
This commit is contained in:
parent
8df4179dd7
commit
c7144328fa
@ -125,7 +125,7 @@ export default function ChatPanel({ chatId }: Props) {
|
||||
<div key={msg.id} className="text-sm" style={bgStyle}>
|
||||
<div className="flex items-center gap-1 text-xs text-[var(--muted)] mb-0.5">
|
||||
<span>{AUTHOR_ICON[msg.author_type] || "👤"}</span>
|
||||
<span className="font-medium">{msg.author_slug}</span>
|
||||
<span className="font-medium">{msg.author?.name || msg.author?.slug || "Unknown"}</span>
|
||||
<span>·</span>
|
||||
<span>{new Date(msg.created_at).toLocaleString("ru-RU", { hour: "2-digit", minute: "2-digit" })}</span>
|
||||
</div>
|
||||
|
||||
@ -63,7 +63,7 @@ export default function KanbanBoard({ projectId, projectSlug }: Props) {
|
||||
|
||||
const unsubscribeAssigned = wsClient.on("task.assigned", (data: any) => {
|
||||
if (data.project_id === projectId) {
|
||||
setTasks((prev) => prev.map((t) => t.id === data.id ? { ...t, assignee_slug: data.assignee_slug, assigned_at: data.assigned_at } : t));
|
||||
setTasks((prev) => prev.map((t) => t.id === data.id ? { ...t, assignee_id: data.assignee_id, assignee: data.assignee, assigned_at: data.assigned_at } : t));
|
||||
}
|
||||
});
|
||||
|
||||
@ -106,8 +106,8 @@ export default function KanbanBoard({ projectId, projectSlug }: Props) {
|
||||
style={{ background: PRIORITY_COLORS[task.priority] || "#737373" }}
|
||||
title={task.priority} />
|
||||
<span className="text-xs text-[var(--muted)]">{prefix}-{task.number}</span>
|
||||
{task.assignee_slug && (
|
||||
<span className="text-xs text-[var(--muted)] ml-auto">→ {task.assignee_slug}</span>
|
||||
{task.assignee && (
|
||||
<span className="text-xs text-[var(--muted)] ml-auto">→ {task.assignee.slug}</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-sm ml-3.5">{task.title}</div>
|
||||
|
||||
@ -42,7 +42,7 @@ export default function TaskModal({ task, projectId: _projectId, projectSlug: _p
|
||||
const [description, setDescription] = useState(task.description || "");
|
||||
const [status, setStatus] = useState(task.status);
|
||||
const [priority, setPriority] = useState(task.priority);
|
||||
const [assigneeSlug, setAssigneeSlug] = useState(task.assignee_slug || "");
|
||||
const [assigneeId, setAssigneeId] = useState(task.assignee_id || "");
|
||||
const [members, setMembers] = useState<Member[]>([]);
|
||||
const [steps, setSteps] = useState<Step[]>(task.steps || []);
|
||||
const [comments, setComments] = useState<Message[]>([]);
|
||||
@ -307,16 +307,16 @@ export default function TaskModal({ task, projectId: _projectId, projectSlug: _p
|
||||
<div>
|
||||
<div className="text-xs text-[var(--muted)] mb-1">Исполнитель</div>
|
||||
<select
|
||||
value={assigneeSlug}
|
||||
value={assigneeId}
|
||||
onChange={(e) => {
|
||||
setAssigneeSlug(e.target.value);
|
||||
save({ assignee_slug: e.target.value || undefined } as any);
|
||||
setAssigneeId(e.target.value);
|
||||
save({ assignee_id: e.target.value || undefined } as any);
|
||||
}}
|
||||
className="w-full bg-[var(--bg)] border border-[var(--border)] rounded px-2 py-1.5 text-sm outline-none focus:border-[var(--accent)]"
|
||||
>
|
||||
<option value="">Не назначен</option>
|
||||
{members.map((m) => (
|
||||
<option key={m.slug} value={m.slug}>
|
||||
<option key={m.id} value={m.id}>
|
||||
{m.type === "agent" ? "🤖 " : "👤 "}{m.name}
|
||||
</option>
|
||||
))}
|
||||
|
||||
@ -106,9 +106,11 @@ export interface Task {
|
||||
status: string;
|
||||
priority: string;
|
||||
labels: string[];
|
||||
assignee_slug: string | null;
|
||||
reviewer_slug: string | null;
|
||||
watchers: string[];
|
||||
assignee_id: string | null;
|
||||
assignee: Member | null;
|
||||
reviewer_id: string | null;
|
||||
reviewer: Member | null;
|
||||
watcher_ids: string[];
|
||||
depends_on: string[];
|
||||
position: number;
|
||||
time_spent: number;
|
||||
@ -128,7 +130,8 @@ export interface Message {
|
||||
task_id: string | null;
|
||||
parent_id: string | null;
|
||||
author_type: string;
|
||||
author_slug: string;
|
||||
author_id: string;
|
||||
author: Member | null;
|
||||
content: string;
|
||||
mentions: string[];
|
||||
voice_url: string | null;
|
||||
@ -203,10 +206,10 @@ export async function rejectTask(taskId: string, reason: string): Promise<{ok: b
|
||||
});
|
||||
}
|
||||
|
||||
export async function assignTask(taskId: string, assigneeSlug: string): Promise<Task> {
|
||||
export async function assignTask(taskId: string, assigneeId: string): Promise<Task> {
|
||||
return request(`/api/v1/tasks/${taskId}/assign`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ assignee_slug: assigneeSlug })
|
||||
body: JSON.stringify({ assignee_id: assigneeId })
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user