типизация фронтенд: убран дубликат MemberBrief, мёртвые slug params, typed WS events

This commit is contained in:
Markov 2026-02-25 14:40:14 +01:00
parent 6ea653dbb1
commit a948b2f6fb
3 changed files with 13 additions and 19 deletions

View File

@ -49,21 +49,21 @@ export default function KanbanBoard({ projectId, projectSlug }: Props) {
loadTasks(); loadTasks();
// Subscribe to WebSocket events for real-time updates // Subscribe to WebSocket events for real-time updates
const unsubscribeCreated = wsClient.on("task.created", (data: any) => { const unsubscribeCreated = wsClient.on("task.created", (data: Task) => {
if (data.project_id === projectId) { if (data.project_id === projectId) {
setTasks((prev) => [...prev, data as Task]); setTasks((prev) => prev.some((t) => t.id === data.id) ? prev : [...prev, data]);
} }
}); });
const unsubscribeUpdated = wsClient.on("task.updated", (data: any) => { const unsubscribeUpdated = wsClient.on("task.updated", (data: Task) => {
if (data.project_id === projectId) { if (data.project_id === projectId) {
setTasks((prev) => prev.map((t) => t.id === data.id ? { ...t, ...data } : t)); setTasks((prev) => prev.map((t) => t.id === data.id ? data : t));
} }
}); });
const unsubscribeAssigned = wsClient.on("task.assigned", (data: any) => { const unsubscribeAssigned = wsClient.on("task.assigned", (data: Task) => {
if (data.project_id === projectId) { if (data.project_id === projectId) {
setTasks((prev) => prev.map((t) => t.id === data.id ? { ...t, assignee_id: data.assignee_id, assignee: data.assignee, assigned_at: data.assigned_at } : t)); setTasks((prev) => prev.map((t) => t.id === data.id ? data : t));
} }
}); });

View File

@ -251,7 +251,7 @@ export default function TaskModal({ task, projectId: _projectId, projectSlug: _p
<div key={msg.id} className="text-sm"> <div key={msg.id} className="text-sm">
<div className="flex items-center gap-1 text-xs text-[var(--muted)] mb-0.5"> <div className="flex items-center gap-1 text-xs text-[var(--muted)] mb-0.5">
<span>{AUTHOR_ICON[msg.author_type] || "👤"}</span> <span>{AUTHOR_ICON[msg.author_type] || "👤"}</span>
<span className="font-medium">{msg.author?.slug || msg.author_id}</span> <span className="font-medium">{msg.author_type === "system" ? "System" : (msg.author?.name || msg.author?.slug || "Unknown")}</span>
<span>·</span> <span>·</span>
<span>{new Date(msg.created_at).toLocaleString("ru-RU", { hour: "2-digit", minute: "2-digit" })}</span> <span>{new Date(msg.created_at).toLocaleString("ru-RU", { hour: "2-digit", minute: "2-digit" })}</span>
</div> </div>

View File

@ -60,12 +60,6 @@ export interface AgentConfig {
model: string | null; model: string | null;
} }
export interface MemberBrief {
id: string;
slug: string;
name: string;
}
export interface Member { export interface Member {
id: string; id: string;
slug: string; slug: string;
@ -214,8 +208,8 @@ export async function deleteTask(taskId: string): Promise<void> {
await request(`/api/v1/tasks/${taskId}`, { method: "DELETE" }); await request(`/api/v1/tasks/${taskId}`, { method: "DELETE" });
} }
export async function takeTask(taskId: string, slug: string): Promise<Task> { export async function takeTask(taskId: string): Promise<Task> {
return request(`/api/v1/tasks/${taskId}/take?slug=${slug}`, { method: "POST" }); return request(`/api/v1/tasks/${taskId}/take`, { method: "POST" });
} }
export async function rejectTask(taskId: string, reason: string): Promise<{ok: boolean; reason: string; old_assignee: string}> { export async function rejectTask(taskId: string, reason: string): Promise<{ok: boolean; reason: string; old_assignee: string}> {
@ -232,12 +226,12 @@ export async function assignTask(taskId: string, assigneeId: string): Promise<Ta
}); });
} }
export async function watchTask(taskId: string, slug: string): Promise<{ok: boolean; watchers: string[]}> { export async function watchTask(taskId: string): Promise<{ok: boolean; watcher_ids: string[]}> {
return request(`/api/v1/tasks/${taskId}/watch?slug=${slug}`, { method: "POST" }); return request(`/api/v1/tasks/${taskId}/watch`, { method: "POST" });
} }
export async function unwatchTask(taskId: string, slug: string): Promise<{ok: boolean; watchers: string[]}> { export async function unwatchTask(taskId: string): Promise<{ok: boolean; watcher_ids: string[]}> {
return request(`/api/v1/tasks/${taskId}/watch?slug=${slug}`, { method: "DELETE" }); return request(`/api/v1/tasks/${taskId}/watch`, { method: "DELETE" });
} }
// --- Steps --- // --- Steps ---