Skip to content

Commit c2a8ce0

Browse files
committed
feat: improve task creation with optimistic UI and realtime stability
- Add workspaceId field to Task type for proper task association - Prevent WebSocket connection in local development to avoid errors - Enhance optimistic task merging logic to preserve temporary tasks during creation - Fix task creation payload handling to properly merge server response with optimistic data - Simplify useCreateTask hook with better optimistic updates and error handling - Update useMyTasks and useReportTasks hooks for consistent parameter handling
1 parent 5749b4d commit c2a8ce0

4 files changed

Lines changed: 161 additions & 242 deletions

File tree

frontend/src/components/ProjectManagement.tsx

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1412,6 +1412,8 @@ export default function ProjectManagement({
14121412

14131413
// 🧠 NORMAL MERGE (REALTIME / OPTIMISTIC SAFE)
14141414
setTasks((localTasks) => {
1415+
// console.log("[ProjectManagement] merging tasks", { serverTasksCount: serverTasks.length, localTasksCount: localTasks.length });
1416+
14151417
// 1) Group local tasks by ID (real and tmp) and clientId (if available)
14161418
const localById = new Map(localTasks.map((t) => [nid(t.id), t]));
14171419
const localByClientId = new Map();
@@ -1480,6 +1482,10 @@ export default function ProjectManagement({
14801482
const localOnly = localTasks.filter((lt) => {
14811483
const id = nid(lt.id);
14821484
const cid = nid((lt as any).clientId);
1485+
1486+
// If creatingTask is true and this is a tmp task, definitely keep it!
1487+
if (creatingTask && id.startsWith("tmp_")) return true;
1488+
14831489
return !serverIds.has(id) && (!cid || !serverClientIds.has(cid));
14841490
});
14851491

@@ -1489,22 +1495,60 @@ export default function ProjectManagement({
14891495
const mergedMap = new Map(merged.map((t) => [nid(t.id), t]));
14901496
setSelectedTask((cur) => {
14911497
if (!cur) return null;
1492-
const found = mergedMap.get(nid(cur.id));
1498+
const curId = nid(cur.id);
1499+
const curCid = nid((cur as any).clientId);
1500+
1501+
const found = mergedMap.get(curId);
14931502
if (found) return found;
14941503

1495-
// keep optimistic task if it's currently selected
1496-
if (nid(cur.id).startsWith("tmp_")) return cur;
1504+
// check by clientId as fallback (if cur has a clientId)
1505+
if (curCid) {
1506+
const foundByCid = Array.from(mergedMap.values()).find(
1507+
(t) => nid((t as any).clientId) === curCid || nid(t.id) === curCid,
1508+
);
1509+
if (foundByCid) return foundByCid;
1510+
}
1511+
1512+
// If creatingTask is true, don't auto-close!
1513+
if (creatingTask) {
1514+
return cur;
1515+
}
1516+
1517+
// keep optimistic task if it's currently selected and not yet in merged list
1518+
if (curId.startsWith("tmp_")) {
1519+
return cur;
1520+
}
14971521

14981522
// also keep if it's in localTasks (prevents auto-close during creation)
1499-
const inLocal = localTasks.find((t) => nid(t.id) === nid(cur.id));
1523+
const inLocal = localTasks.find(
1524+
(t) =>
1525+
nid(t.id) === curId ||
1526+
(curCid && nid((t as any).clientId) === curCid) ||
1527+
(curCid && nid(t.id) === curCid),
1528+
);
15001529
if (inLocal) return inLocal;
15011530

1531+
// If the current task is NOT in the merged map but was recently created (has clientId)
1532+
// we might still be waiting for the server to return it in the main task list.
1533+
if (curCid && curCid.startsWith("tmp_")) {
1534+
return cur;
1535+
}
1536+
1537+
console.log(
1538+
"[ProjectManagement] auto-closing TaskModal: task not found in merged list",
1539+
{
1540+
curId,
1541+
curCid,
1542+
localTasksCount: localTasks.length,
1543+
mergedCount: merged.length,
1544+
},
1545+
);
15021546
return null;
15031547
});
15041548

15051549
return merged;
15061550
});
1507-
}, [tasksQuery.data, activeProjectId, dateRangeKey]);
1551+
}, [tasksQuery.data, activeProjectId, dateRangeKey, creatingTask]);
15081552

15091553
async function handleAddTask(title: string) {
15101554
if (!activeProjectId) {
@@ -1514,11 +1558,13 @@ export default function ProjectManagement({
15141558
if (creatingTask) return;
15151559
setCreatingTask(true);
15161560
playSound("/sounds/incoming.mp3", isPlaySound);
1561+
15171562
const optimistic: Task = {
15181563
id: `tmp_${Math.random().toString(36).slice(2, 9)}`,
15191564
title,
15201565
status: "todo" as Task["status"],
15211566
projectId: activeProjectId || null,
1567+
workspaceId: activeWorkspaceId || null, // ensure workspaceId is included
15221568
comments: [],
15231569
priority: "low" as Task["priority"],
15241570
taskAssignees: [],
@@ -1532,26 +1578,30 @@ export default function ProjectManagement({
15321578
setSelectedTask(optimistic);
15331579

15341580
try {
1535-
const { id: _tmp, ...payload } = { ...optimistic, title } as any;
1536-
const created = await createTaskMutation.mutateAsync({
1537-
...payload,
1538-
clientId: optimistic.id,
1539-
});
1581+
const payload = { ...optimistic, title, clientId: optimistic.id };
1582+
delete (payload as any).id; // Remove tmp_ ID so server generates real one
1583+
1584+
console.log("[handleAddTask] mutation started with payload:", payload);
1585+
const created = await createTaskMutation.mutateAsync(payload);
1586+
console.log("[handleAddTask] mutation success:", created);
1587+
1588+
// Merge optimistic and created to ensure no data loss (e.g. projectId)
1589+
const mergedTask = { ...optimistic, ...created, clientId: optimistic.id };
15401590

15411591
setTasks((s) => {
15421592
const replaced = s.map((t) =>
1543-
nid(t.id) === nid(optimistic.id)
1544-
? { ...created, clientId: optimistic.id }
1545-
: t,
1593+
nid(t.id) === nid(optimistic.id) ? mergedTask : t,
15461594
);
15471595
const map = new Map<string, Task>();
15481596
for (const t of replaced) map.set(nid(t.id), t);
15491597
return Array.from(map.values());
15501598
});
15511599

15521600
setSelectedTask((cur) =>
1553-
cur && nid(cur.id) === nid(optimistic.id)
1554-
? { ...created, clientId: optimistic.id }
1601+
cur &&
1602+
(nid(cur.id) === nid(optimistic.id) ||
1603+
nid((cur as any).clientId) === nid(optimistic.id))
1604+
? mergedTask
15551605
: cur,
15561606
);
15571607

0 commit comments

Comments
 (0)