@taskforcehq/taskforce 0.3.303 → 0.3.304
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +102 -5
- package/dist/TaskforceCore.js +514 -212
- package/dist/TaskforceCore.test.js +1172 -133
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/TaskSettings.js +58 -38
- package/dist/components/features/TaskSettings.test.js +139 -40
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +2 -1
- package/dist/components/task/TaskForm.js +22 -4
- package/dist/components/task/TaskForm.test.js +46 -0
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +31 -160
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +4 -2
- package/dist/components/views/PlanComparisonPage.test.js +4 -0
- package/dist/components/views/PlansPage.d.ts +5 -1
- package/dist/components/views/PlansPage.js +39 -12
- package/dist/components/views/PlansPage.test.js +59 -2
- package/dist/components/views/StandaloneLayout.js +113 -38
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/PlanEntitlementService.d.ts +3 -1
- package/dist/core/PlanEntitlementService.js +61 -11
- package/dist/core/SignupMonetizationSettings.test.js +37 -2
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/Taskforce.d.ts +15 -11
- package/dist/core/Taskforce.js +43 -2
- package/dist/core/WorkspacePlanMode.test.js +65 -0
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +61 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +122 -0
- package/dist/hooks/sync/transfers.js +431 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +5 -4
- package/dist/hooks/useSyncOrchestrator.js +314 -873
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +568 -3
- package/dist/hooks/useTaskMutations.js +45 -6
- package/dist/hooks/useTaskMutations.test.js +147 -0
- package/dist/hooks/useTaskforce.d.ts +8 -2
- package/dist/hooks/useTaskforce.js +285 -812
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +22 -10
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +174 -476
- package/dist/mcp/runtime.test.js +81 -8
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +46 -3
- package/dist/server/index.test.js +12 -1
- package/dist/server/routes/admin.js +13 -19
- package/dist/server/routes/auth.js +37 -2
- package/dist/server/routes/billing.js +18 -67
- package/dist/server/routes/billing.test.js +43 -0
- package/dist/server/routes/shared.d.ts +30 -0
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +110 -237
- package/dist/server/routes/tasks.js +49 -7
- package/dist/server/routes.js +8 -19
- package/dist/server/routes.test.js +243 -5
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.js +12 -10
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +377 -0
- package/dist/sync/syncService.d.ts +8 -1
- package/dist/sync/syncService.js +36 -2
- package/dist/sync/syncService.test.js +48 -0
- package/dist/sync/workspacePullFeed.d.ts +1 -0
- package/dist/sync/workspacePullFeed.js +66 -59
- package/dist/sync/workspacePullFeed.test.js +72 -4
- package/dist/sync/workspaceSyncModel.d.ts +22 -1
- package/dist/sync/workspaceSyncModel.js +65 -0
- package/dist/sync/workspaceSyncModel.test.js +119 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-C3GyzrWm.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-BjR6iBf9.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +1 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-DnFqdUME.js} +1 -1
- package/dist/ui/assets/index-DXUdtH1B.js +6 -0
- package/dist/ui/assets/index-Ii0B0rTO.css +1 -0
- package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-I7ZwG1z_.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +3 -1
- package/scripts/playwright-auth.sh +4 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
- package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
- package/dist/ui/assets/index-CLIMgR6g.js +0 -6
- package/dist/ui/assets/index-DneETxcu.css +0 -1
|
@@ -1,21 +1,46 @@
|
|
|
1
|
-
import { useState, useCallback, useRef } from 'react';
|
|
1
|
+
import { useState, useCallback, useEffect, useRef } from 'react';
|
|
2
2
|
import { normalizeTaskFromApi } from '../utils/taskNormalization.js';
|
|
3
3
|
export function useTaskMutations({ tasks, archivedTasks, editingTaskId, setTasks, setArchivedTasks, setDeletedTasks, setError, resetForm, setActiveTab, fetchTasks, fetchArchive, fetchDeletedTasks, mergeTaskFromServer, pushNotice, cloudAuthConfigured, runtimeMode, isAuthenticated, workspaceCloudSyncEnabled, buildWorkspaceSyncSignature, pushWorkspaceChangesToCloud, workspacePendingSignatureRef, workspaceDeletedTaskIdsRef, }) {
|
|
4
4
|
// Copy ID state + timer ref
|
|
5
5
|
const [copiedId, setCopiedId] = useState(null);
|
|
6
6
|
const copiedIdTimerRef = useRef(null);
|
|
7
|
+
const workspaceSyncTimerRef = useRef(null);
|
|
8
|
+
const workspaceSyncTargetAtRef = useRef(null);
|
|
9
|
+
useEffect(() => () => {
|
|
10
|
+
if (typeof window === 'undefined')
|
|
11
|
+
return;
|
|
12
|
+
if (workspaceSyncTimerRef.current !== null) {
|
|
13
|
+
window.clearTimeout(workspaceSyncTimerRef.current);
|
|
14
|
+
workspaceSyncTimerRef.current = null;
|
|
15
|
+
}
|
|
16
|
+
workspaceSyncTargetAtRef.current = null;
|
|
17
|
+
}, []);
|
|
7
18
|
const queueWorkspaceSyncFromAuthoritativeTaskState = useCallback((delayMs = 0) => {
|
|
8
19
|
if (!(cloudAuthConfigured && runtimeMode === 'local' && isAuthenticated && workspaceCloudSyncEnabled))
|
|
9
20
|
return;
|
|
10
21
|
if (typeof window === 'undefined')
|
|
11
22
|
return;
|
|
12
|
-
|
|
23
|
+
const normalizedDelayMs = Math.max(0, delayMs);
|
|
24
|
+
const requestedTargetAt = Date.now() + normalizedDelayMs;
|
|
25
|
+
const existingTargetAt = workspaceSyncTargetAtRef.current;
|
|
26
|
+
if (workspaceSyncTimerRef.current !== null) {
|
|
27
|
+
if (normalizedDelayMs <= 0)
|
|
28
|
+
return;
|
|
29
|
+
if (existingTargetAt !== null && existingTargetAt >= requestedTargetAt)
|
|
30
|
+
return;
|
|
31
|
+
window.clearTimeout(workspaceSyncTimerRef.current);
|
|
32
|
+
workspaceSyncTimerRef.current = null;
|
|
33
|
+
}
|
|
34
|
+
workspaceSyncTargetAtRef.current = requestedTargetAt;
|
|
35
|
+
workspaceSyncTimerRef.current = window.setTimeout(() => {
|
|
36
|
+
workspaceSyncTimerRef.current = null;
|
|
37
|
+
workspaceSyncTargetAtRef.current = null;
|
|
13
38
|
const signature = buildWorkspaceSyncSignature();
|
|
14
39
|
if (!signature)
|
|
15
40
|
return;
|
|
16
41
|
workspacePendingSignatureRef.current = signature;
|
|
17
42
|
void pushWorkspaceChangesToCloud(signature);
|
|
18
|
-
},
|
|
43
|
+
}, normalizedDelayMs);
|
|
19
44
|
}, [
|
|
20
45
|
cloudAuthConfigured,
|
|
21
46
|
runtimeMode,
|
|
@@ -227,8 +252,20 @@ export function useTaskMutations({ tasks, archivedTasks, editingTaskId, setTasks
|
|
|
227
252
|
}, [tasks, archivedTasks, pushNotice, mergeTaskFromServer, queueWorkspaceSyncFromAuthoritativeTaskState]);
|
|
228
253
|
const handleToggleComplete = async (task) => {
|
|
229
254
|
const newStatus = task.status === 'done' ? 'task' : 'done';
|
|
255
|
+
const completedAt = newStatus === 'done' ? new Date().toISOString() : undefined;
|
|
256
|
+
const optimisticUpdates = {
|
|
257
|
+
status: newStatus,
|
|
258
|
+
completedAt,
|
|
259
|
+
completed: newStatus === 'done',
|
|
260
|
+
inProgress: false,
|
|
261
|
+
readyForReview: false,
|
|
262
|
+
cancelled: false
|
|
263
|
+
};
|
|
264
|
+
const previousTasks = tasks;
|
|
265
|
+
const previousArchivedTasks = archivedTasks;
|
|
266
|
+
setTasks(prev => prev.map(t => t.id === task.id ? { ...t, ...optimisticUpdates } : t));
|
|
267
|
+
setArchivedTasks(prev => prev.map(t => t.id === task.id ? { ...t, ...optimisticUpdates } : t));
|
|
230
268
|
try {
|
|
231
|
-
const completedAt = newStatus === 'done' ? new Date().toISOString() : undefined;
|
|
232
269
|
const res = await fetch(`/api/taskforce/task/${task.id}`, {
|
|
233
270
|
method: 'PATCH',
|
|
234
271
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -239,7 +276,8 @@ export function useTaskMutations({ tasks, archivedTasks, editingTaskId, setTasks
|
|
|
239
276
|
const message = data.error || 'Failed to update task status.';
|
|
240
277
|
setError(message);
|
|
241
278
|
pushNotice(message, 'error');
|
|
242
|
-
|
|
279
|
+
setTasks(previousTasks);
|
|
280
|
+
setArchivedTasks(previousArchivedTasks);
|
|
243
281
|
return;
|
|
244
282
|
}
|
|
245
283
|
const updatedTask = await res.json().catch(() => null);
|
|
@@ -257,7 +295,8 @@ export function useTaskMutations({ tasks, archivedTasks, editingTaskId, setTasks
|
|
|
257
295
|
console.error("Failed to toggle complete", err);
|
|
258
296
|
setError('Failed to update task status.');
|
|
259
297
|
pushNotice('Failed to update task status.', 'error');
|
|
260
|
-
|
|
298
|
+
setTasks(previousTasks);
|
|
299
|
+
setArchivedTasks(previousArchivedTasks);
|
|
261
300
|
}
|
|
262
301
|
};
|
|
263
302
|
const handleToggleCancel = async (task) => {
|
|
@@ -6,9 +6,84 @@ describe('useTaskMutations', () => {
|
|
|
6
6
|
vi.stubGlobal('fetch', vi.fn());
|
|
7
7
|
});
|
|
8
8
|
afterEach(() => {
|
|
9
|
+
vi.useRealTimers();
|
|
9
10
|
vi.unstubAllGlobals();
|
|
10
11
|
vi.clearAllMocks();
|
|
11
12
|
});
|
|
13
|
+
const baseTask = {
|
|
14
|
+
id: 'task-1',
|
|
15
|
+
title: 'Test task',
|
|
16
|
+
description: '',
|
|
17
|
+
category: 'general',
|
|
18
|
+
type: 'task',
|
|
19
|
+
priority: 2,
|
|
20
|
+
complexity: 3,
|
|
21
|
+
status: 'task',
|
|
22
|
+
createdAt: '2026-04-03T10:00:00.000Z',
|
|
23
|
+
updatedAt: '2026-04-03T10:05:00.000Z'
|
|
24
|
+
};
|
|
25
|
+
const buildHook = (overrides = {}) => {
|
|
26
|
+
const setTasks = vi.fn();
|
|
27
|
+
const setArchivedTasks = vi.fn();
|
|
28
|
+
const setDeletedTasks = vi.fn();
|
|
29
|
+
const setError = vi.fn();
|
|
30
|
+
const resetForm = vi.fn();
|
|
31
|
+
const setActiveTab = vi.fn();
|
|
32
|
+
const fetchTasks = vi.fn();
|
|
33
|
+
const fetchArchive = vi.fn();
|
|
34
|
+
const fetchDeletedTasks = vi.fn();
|
|
35
|
+
const mergeTaskFromServer = vi.fn();
|
|
36
|
+
const pushNotice = vi.fn();
|
|
37
|
+
const buildWorkspaceSyncSignature = vi.fn(() => 'sig-1');
|
|
38
|
+
const pushWorkspaceChangesToCloud = vi.fn();
|
|
39
|
+
const workspacePendingSignatureRef = { current: null };
|
|
40
|
+
const workspaceDeletedTaskIdsRef = { current: new Set() };
|
|
41
|
+
const hook = renderHook(() => useTaskMutations({
|
|
42
|
+
tasks: [baseTask],
|
|
43
|
+
archivedTasks: [],
|
|
44
|
+
editingTaskId: null,
|
|
45
|
+
setTasks,
|
|
46
|
+
setArchivedTasks,
|
|
47
|
+
setDeletedTasks,
|
|
48
|
+
setError,
|
|
49
|
+
resetForm,
|
|
50
|
+
setActiveTab,
|
|
51
|
+
fetchTasks,
|
|
52
|
+
fetchArchive,
|
|
53
|
+
fetchDeletedTasks,
|
|
54
|
+
mergeTaskFromServer,
|
|
55
|
+
pushNotice,
|
|
56
|
+
cloudAuthConfigured: true,
|
|
57
|
+
runtimeMode: 'local',
|
|
58
|
+
isAuthenticated: true,
|
|
59
|
+
workspaceCloudSyncEnabled: true,
|
|
60
|
+
buildWorkspaceSyncSignature,
|
|
61
|
+
pushWorkspaceChangesToCloud,
|
|
62
|
+
workspacePendingSignatureRef,
|
|
63
|
+
workspaceDeletedTaskIdsRef,
|
|
64
|
+
...overrides
|
|
65
|
+
}));
|
|
66
|
+
return {
|
|
67
|
+
...hook,
|
|
68
|
+
deps: {
|
|
69
|
+
setTasks,
|
|
70
|
+
setArchivedTasks,
|
|
71
|
+
setDeletedTasks,
|
|
72
|
+
setError,
|
|
73
|
+
resetForm,
|
|
74
|
+
setActiveTab,
|
|
75
|
+
fetchTasks,
|
|
76
|
+
fetchArchive,
|
|
77
|
+
fetchDeletedTasks,
|
|
78
|
+
mergeTaskFromServer,
|
|
79
|
+
pushNotice,
|
|
80
|
+
buildWorkspaceSyncSignature,
|
|
81
|
+
pushWorkspaceChangesToCloud,
|
|
82
|
+
workspacePendingSignatureRef,
|
|
83
|
+
workspaceDeletedTaskIdsRef
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
};
|
|
12
87
|
it('clears pending delete ids when a deleted task is restored', async () => {
|
|
13
88
|
const fetchMock = vi.mocked(fetch);
|
|
14
89
|
fetchMock.mockResolvedValue({
|
|
@@ -77,4 +152,76 @@ describe('useTaskMutations', () => {
|
|
|
77
152
|
expect(pushWorkspaceChangesToCloud).toHaveBeenCalledWith('sig-1');
|
|
78
153
|
expect(setDeletedTasks).toHaveBeenCalled();
|
|
79
154
|
});
|
|
155
|
+
it('optimistically marks a task done before the server responds', async () => {
|
|
156
|
+
let resolveResponse;
|
|
157
|
+
const fetchMock = vi.mocked(fetch);
|
|
158
|
+
fetchMock.mockImplementation(() => new Promise((resolve) => {
|
|
159
|
+
resolveResponse = resolve;
|
|
160
|
+
}));
|
|
161
|
+
const { result, deps } = buildHook();
|
|
162
|
+
let togglePromise = null;
|
|
163
|
+
await act(async () => {
|
|
164
|
+
togglePromise = result.current.handleToggleComplete(baseTask);
|
|
165
|
+
});
|
|
166
|
+
expect(deps.setTasks).toHaveBeenCalledTimes(1);
|
|
167
|
+
const optimisticUpdater = deps.setTasks.mock.calls[0]?.[0];
|
|
168
|
+
const optimisticTasks = optimisticUpdater([baseTask]);
|
|
169
|
+
expect(optimisticTasks[0]?.status).toBe('done');
|
|
170
|
+
expect(optimisticTasks[0]?.completed).toBe(true);
|
|
171
|
+
resolveResponse({
|
|
172
|
+
ok: true,
|
|
173
|
+
status: 200,
|
|
174
|
+
json: async () => ({ ...baseTask, status: 'done', completedAt: '2026-04-14T12:00:00.000Z' })
|
|
175
|
+
});
|
|
176
|
+
await act(async () => {
|
|
177
|
+
await togglePromise;
|
|
178
|
+
});
|
|
179
|
+
expect(deps.mergeTaskFromServer).toHaveBeenCalledWith(expect.objectContaining({ id: 'task-1', status: 'done' }));
|
|
180
|
+
});
|
|
181
|
+
it('rolls back the optimistic done state when the server update fails', async () => {
|
|
182
|
+
const fetchMock = vi.mocked(fetch);
|
|
183
|
+
fetchMock.mockResolvedValue({
|
|
184
|
+
ok: false,
|
|
185
|
+
status: 500,
|
|
186
|
+
json: async () => ({ error: 'Server exploded' })
|
|
187
|
+
});
|
|
188
|
+
const { result, deps } = buildHook();
|
|
189
|
+
await act(async () => {
|
|
190
|
+
await result.current.handleToggleComplete(baseTask);
|
|
191
|
+
});
|
|
192
|
+
expect(deps.setTasks).toHaveBeenCalledTimes(2);
|
|
193
|
+
expect(deps.setTasks.mock.calls[1]?.[0]).toEqual([baseTask]);
|
|
194
|
+
expect(deps.setArchivedTasks.mock.calls[1]?.[0]).toEqual([]);
|
|
195
|
+
expect(deps.pushNotice).toHaveBeenCalledWith('Server exploded', 'error');
|
|
196
|
+
expect(deps.mergeTaskFromServer).not.toHaveBeenCalled();
|
|
197
|
+
});
|
|
198
|
+
it('coalesces pending workspace sync pushes and preserves attachment delay', async () => {
|
|
199
|
+
vi.useFakeTimers();
|
|
200
|
+
const fetchMock = vi.mocked(fetch);
|
|
201
|
+
fetchMock.mockResolvedValue({
|
|
202
|
+
ok: true,
|
|
203
|
+
status: 200,
|
|
204
|
+
json: async () => ({ ...baseTask, updatedAt: '2026-04-14T12:00:00.000Z' })
|
|
205
|
+
});
|
|
206
|
+
const { result, deps } = buildHook();
|
|
207
|
+
await act(async () => {
|
|
208
|
+
await result.current.handleUpdateTask(baseTask.id, { description: 'First update' });
|
|
209
|
+
});
|
|
210
|
+
await act(async () => {
|
|
211
|
+
await result.current.handleUpdateTask(baseTask.id, {
|
|
212
|
+
attachments: [{ id: 'asset-1', path: 'workspaces/workspace-local-active/assets/file.txt' }]
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
await act(async () => {
|
|
216
|
+
await vi.advanceTimersByTimeAsync(149);
|
|
217
|
+
});
|
|
218
|
+
expect(deps.pushWorkspaceChangesToCloud).not.toHaveBeenCalled();
|
|
219
|
+
await act(async () => {
|
|
220
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
221
|
+
});
|
|
222
|
+
expect(deps.buildWorkspaceSyncSignature).toHaveBeenCalledTimes(1);
|
|
223
|
+
expect(deps.workspacePendingSignatureRef.current).toBe('sig-1');
|
|
224
|
+
expect(deps.pushWorkspaceChangesToCloud).toHaveBeenCalledTimes(1);
|
|
225
|
+
expect(deps.pushWorkspaceChangesToCloud).toHaveBeenCalledWith('sig-1');
|
|
226
|
+
});
|
|
80
227
|
});
|
|
@@ -122,7 +122,7 @@ export declare function useTaskforce({ config, initialTaskId, onTaskCountChange,
|
|
|
122
122
|
authUserDisplayName: string;
|
|
123
123
|
authUserAvatarUrl: string;
|
|
124
124
|
authWorkspaceId: string;
|
|
125
|
-
userGlobalSyncStatus: "error" | "idle" | "
|
|
125
|
+
userGlobalSyncStatus: "error" | "idle" | "disconnected" | "syncing";
|
|
126
126
|
workspaceLastPullAt: string | null;
|
|
127
127
|
workspaceLastPushAt: string | null;
|
|
128
128
|
workspaceLastErrorAt: string | null;
|
|
@@ -193,6 +193,12 @@ export declare function useTaskforce({ config, initialTaskId, onTaskCountChange,
|
|
|
193
193
|
runtimeCapabilities: RuntimeCapabilities | null;
|
|
194
194
|
refreshSetupContext: () => Promise<void>;
|
|
195
195
|
retryBootstrapChecks: () => Promise<void>;
|
|
196
|
+
continueAfterCommercialOnboarding: () => Promise<{
|
|
197
|
+
success: boolean;
|
|
198
|
+
destination: "app" | "setup" | "login";
|
|
199
|
+
error?: string;
|
|
200
|
+
code?: string;
|
|
201
|
+
}>;
|
|
196
202
|
saveWorkspaceProfile: (input: {
|
|
197
203
|
name: string;
|
|
198
204
|
description?: string;
|
|
@@ -206,7 +212,7 @@ export declare function useTaskforce({ config, initialTaskId, onTaskCountChange,
|
|
|
206
212
|
fetchWorkspaces: () => Promise<{
|
|
207
213
|
success: boolean;
|
|
208
214
|
error?: string;
|
|
209
|
-
workspaces?: WorkspaceSummary[];
|
|
215
|
+
workspaces?: import("./workspace/useTaskforceWorkspaceBootstrap").WorkspaceSummary[];
|
|
210
216
|
}>;
|
|
211
217
|
createWorkspace: (name: string, description?: string) => Promise<{
|
|
212
218
|
success: boolean;
|