@tuturuuu/ui 0.9.0 → 0.11.0
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/CHANGELOG.md +45 -0
- package/package.json +6 -5
- package/src/components/ui/custom/__tests__/settings-dialog-search.test.ts +78 -0
- package/src/components/ui/custom/__tests__/settings-dialog-shell-compile-graph.test.ts +76 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +27 -1
- package/src/components/ui/custom/nav-link.test.tsx +165 -0
- package/src/components/ui/custom/nav-link.tsx +69 -11
- package/src/components/ui/custom/navigation.tsx +1 -0
- package/src/components/ui/custom/settings/task-settings.tsx +104 -0
- package/src/components/ui/custom/settings-dialog-search-loader.d.ts +5 -0
- package/src/components/ui/custom/settings-dialog-search-loader.js +3 -0
- package/src/components/ui/custom/settings-dialog-search.ts +75 -0
- package/src/components/ui/custom/settings-dialog-shell.tsx +63 -27
- package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
- package/src/components/ui/custom/workspace-select.tsx +17 -16
- package/src/components/ui/finance/invoices/components/subscription-attendance-summary.tsx +19 -5
- package/src/components/ui/finance/invoices/components/subscription-prepaid-controls.test.tsx +41 -0
- package/src/components/ui/finance/invoices/components/subscription-prepaid-controls.tsx +93 -0
- package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +82 -70
- package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-content.ts +50 -25
- package/src/components/ui/finance/invoices/hooks.ts +11 -2
- package/src/components/ui/finance/invoices/internal-api.ts +5 -0
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +92 -31
- package/src/components/ui/finance/invoices/utils.test.ts +82 -0
- package/src/components/ui/finance/invoices/utils.ts +240 -7
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +16 -0
- package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -0
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +4 -328
- package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +351 -0
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +50 -37
- package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +7 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-types.ts +3 -3
- package/src/components/ui/tu-do/boards/boardId/kanban/data/use-bulk-resources.ts +59 -5
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/drag-preview.tsx +20 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +263 -21
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +133 -14
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +112 -54
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +8 -2
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +29 -14
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +24 -1
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +7 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +7 -1
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +20 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +10 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +80 -8
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +15 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +35 -3
- package/src/components/ui/tu-do/boards/form.tsx +1 -1
- package/src/components/ui/tu-do/hooks/__tests__/useTaskLabelManagement.test.tsx +48 -0
- package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +144 -0
- package/src/components/ui/tu-do/hooks/useTaskDialog.ts +7 -0
- package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +115 -106
- package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +115 -122
- package/src/components/ui/tu-do/progress/task-progress-import-panel.tsx +60 -0
- package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +156 -0
- package/src/components/ui/tu-do/progress/task-progress-page.tsx +348 -0
- package/src/components/ui/tu-do/progress/task-progress-panels.tsx +301 -0
- package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +26 -0
- package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +81 -10
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +123 -1
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +38 -0
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +128 -7
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +222 -9
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +58 -0
- package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
- package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +3 -0
- package/src/components/ui/tu-do/shared/assignee-select.tsx +77 -26
- package/src/components/ui/tu-do/shared/board-client.tsx +16 -4
- package/src/components/ui/tu-do/shared/board-header.tsx +8 -1
- package/src/components/ui/tu-do/shared/board-switcher.tsx +70 -38
- package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
- package/src/components/ui/tu-do/shared/board-views.tsx +49 -69
- package/src/components/ui/tu-do/shared/list-view.tsx +21 -3
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +58 -4
- package/src/components/ui/tu-do/shared/task-cache-patches.ts +394 -0
- package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +21 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +5 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +25 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +7 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/description-versions.test.ts +97 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/description-versions.ts +210 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-data.ts +79 -10
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +76 -77
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +63 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +78 -69
- package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +28 -8
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +14 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +6 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +8 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +56 -8
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-change-dialog.test.tsx +63 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-change-dialog.tsx +218 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-restore-banner.tsx +83 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-version-restore-dialog.test.tsx +120 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-version-restore-dialog.tsx +180 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +8 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +150 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +61 -35
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +44 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils.test.ts +100 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils.ts +109 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +390 -34
- package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +11 -0
- package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +2 -0
- package/src/components/ui/tu-do/templates/task-template-api.ts +142 -0
- package/src/components/ui/tu-do/templates/task-template-card.tsx +118 -0
- package/src/components/ui/tu-do/templates/task-template-client.tsx +258 -0
- package/src/components/ui/tu-do/templates/task-template-dialogs.test.tsx +167 -0
- package/src/components/ui/tu-do/templates/task-template-dialogs.tsx +376 -0
- package/src/components/ui/tu-do/templates/task-templates-hub.test.tsx +114 -0
- package/src/components/ui/tu-do/templates/task-templates-hub.tsx +50 -0
- package/src/components/ui/tu-do/templates/task-templates-page.tsx +6 -11
- package/src/hooks/__tests__/useBoardPresence.test.tsx +191 -0
- package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
- package/src/hooks/useBoardPresence.ts +364 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +34 -90
- package/src/lib/workspace-actions.ts +2 -6
|
@@ -11,10 +11,12 @@ import {
|
|
|
11
11
|
uploadWorkspaceTaskFile,
|
|
12
12
|
} from '@tuturuuu/internal-api';
|
|
13
13
|
import type { WorkspaceTaskSuggestionTask } from '@tuturuuu/internal-api/tasks';
|
|
14
|
+
import { getWorkspaceTaskHistory } from '@tuturuuu/internal-api/tasks';
|
|
14
15
|
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
15
16
|
import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
|
|
16
17
|
import { Button } from '@tuturuuu/ui/button';
|
|
17
18
|
import { Dialog, DialogContent, DialogTitle } from '@tuturuuu/ui/dialog';
|
|
19
|
+
import { SUPABASE_PROVIDER_SYNC_ORIGIN } from '@tuturuuu/ui/hooks/supabase-provider';
|
|
18
20
|
import { useYjsCollaboration } from '@tuturuuu/ui/hooks/use-yjs-collaboration';
|
|
19
21
|
import { toast } from '@tuturuuu/ui/sonner';
|
|
20
22
|
import { Tooltip, TooltipContent, TooltipTrigger } from '@tuturuuu/ui/tooltip';
|
|
@@ -30,8 +32,9 @@ import {
|
|
|
30
32
|
} from '@tuturuuu/utils/yjs-helper';
|
|
31
33
|
import dayjs from 'dayjs';
|
|
32
34
|
import { usePathname } from 'next/navigation';
|
|
33
|
-
import { useTranslations } from 'next-intl';
|
|
35
|
+
import { useLocale, useTranslations } from 'next-intl';
|
|
34
36
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
37
|
+
import * as Y from 'yjs';
|
|
35
38
|
import { BoardEstimationConfigDialog } from '../boards/boardId/task-dialogs/BoardEstimationConfigDialog';
|
|
36
39
|
import { TaskNewLabelDialog } from '../boards/boardId/task-dialogs/TaskNewLabelDialog';
|
|
37
40
|
import { TaskNewProjectDialog } from '../boards/boardId/task-dialogs/TaskNewProjectDialog';
|
|
@@ -64,7 +67,14 @@ import {
|
|
|
64
67
|
import { TaskNameInput } from './task-edit-dialog/components/task-name-input';
|
|
65
68
|
import { TaskSuggestionMenus } from './task-edit-dialog/components/task-suggestion-menus';
|
|
66
69
|
import { NAME_UPDATE_DEBOUNCE_MS } from './task-edit-dialog/constants';
|
|
67
|
-
import {
|
|
70
|
+
import {
|
|
71
|
+
buildRecoverableTaskDescriptionVersions,
|
|
72
|
+
type RecoverableTaskDescriptionVersion,
|
|
73
|
+
} from './task-edit-dialog/description-versions';
|
|
74
|
+
import {
|
|
75
|
+
fetchWorkspaceTaskDescription,
|
|
76
|
+
updateWorkspaceTaskDescription,
|
|
77
|
+
} from './task-edit-dialog/hooks/task-api';
|
|
68
78
|
import { useEditorCommands } from './task-edit-dialog/hooks/use-editor-commands';
|
|
69
79
|
import { useSuggestionMenus } from './task-edit-dialog/hooks/use-suggestion-menus';
|
|
70
80
|
import { useTaskChangeDetection } from './task-edit-dialog/hooks/use-task-change-detection';
|
|
@@ -85,6 +95,8 @@ import { useTaskYjsSync } from './task-edit-dialog/hooks/use-task-yjs-sync';
|
|
|
85
95
|
import { PersonalOverridesSection } from './task-edit-dialog/personal-overrides-section';
|
|
86
96
|
import { TaskActivitySection } from './task-edit-dialog/task-activity-section';
|
|
87
97
|
import { TaskDeleteDialog } from './task-edit-dialog/task-delete-dialog';
|
|
98
|
+
import { TaskDescriptionRestoreBanner } from './task-edit-dialog/task-description-restore-banner';
|
|
99
|
+
import { TaskDescriptionVersionRestoreDialog } from './task-edit-dialog/task-description-version-restore-dialog';
|
|
88
100
|
import { TaskInstancesSection } from './task-edit-dialog/task-instances-section';
|
|
89
101
|
import { TaskPropertiesSection } from './task-edit-dialog/task-properties-section';
|
|
90
102
|
import { TaskRelationshipsProperties } from './task-edit-dialog/task-relationships-properties';
|
|
@@ -104,16 +116,22 @@ import {
|
|
|
104
116
|
} from './task-edit-dialog/user-display';
|
|
105
117
|
import {
|
|
106
118
|
broadcastTaskDescriptionUpsert,
|
|
119
|
+
canPersistTaskDescriptionSnapshot,
|
|
107
120
|
clearDraft,
|
|
121
|
+
createTaskDescriptionPersistenceGuardState,
|
|
108
122
|
getDescriptionContent,
|
|
109
123
|
getDraftStorageKey,
|
|
110
124
|
getTaskDescriptionPercentLeft,
|
|
111
125
|
getTaskDescriptionPreviewText,
|
|
112
126
|
getTaskDescriptionStorageLength,
|
|
127
|
+
normalizeTaskDescriptionSnapshot,
|
|
128
|
+
recordTaskDescriptionEditorSnapshot,
|
|
113
129
|
saveAndVerifyYjsDescriptionToDatabase,
|
|
114
130
|
saveYjsDescriptionToDatabase,
|
|
115
|
-
|
|
131
|
+
serializeTaskDescriptionPersistenceSnapshot,
|
|
116
132
|
shouldPreserveNativeContextMenu,
|
|
133
|
+
type TaskDescriptionPersistenceGuardState,
|
|
134
|
+
updateTaskDescriptionCaches,
|
|
117
135
|
} from './task-edit-dialog/utils';
|
|
118
136
|
import {
|
|
119
137
|
resolveInlineTaskTargetList,
|
|
@@ -123,6 +141,13 @@ import { TaskShareDialog } from './task-share-dialog';
|
|
|
123
141
|
import type { TaskFilters } from './types';
|
|
124
142
|
import { UnsavedChangesWarningDialog } from './unsaved-changes-warning-dialog';
|
|
125
143
|
|
|
144
|
+
type AssigneeMemberSource = 'workspace' | 'board' | 'workspace-and-board';
|
|
145
|
+
|
|
146
|
+
const EMPTY_TASK_DESCRIPTION_DOC: JSONContent = {
|
|
147
|
+
type: 'doc',
|
|
148
|
+
content: [{ type: 'paragraph' }],
|
|
149
|
+
};
|
|
150
|
+
|
|
126
151
|
export {
|
|
127
152
|
type DialogHeaderInfo,
|
|
128
153
|
getTaskDialogHeaderInfo,
|
|
@@ -149,6 +174,8 @@ export interface TaskEditDialogProps {
|
|
|
149
174
|
taskLoadError?: boolean;
|
|
150
175
|
taskHydrationVersion?: number;
|
|
151
176
|
isPersonalWorkspace?: boolean;
|
|
177
|
+
canUseBoardAssignees?: boolean;
|
|
178
|
+
assigneeMemberSource?: AssigneeMemberSource;
|
|
152
179
|
parentTaskId?: string;
|
|
153
180
|
parentTaskName?: string;
|
|
154
181
|
pendingRelationship?: PendingRelationship;
|
|
@@ -189,6 +216,8 @@ export function TaskEditDialog({
|
|
|
189
216
|
taskLoadError = false,
|
|
190
217
|
taskHydrationVersion = 0,
|
|
191
218
|
isPersonalWorkspace = false,
|
|
219
|
+
canUseBoardAssignees,
|
|
220
|
+
assigneeMemberSource,
|
|
192
221
|
parentTaskId,
|
|
193
222
|
parentTaskName,
|
|
194
223
|
pendingRelationship,
|
|
@@ -210,10 +239,12 @@ export function TaskEditDialog({
|
|
|
210
239
|
const isCreateMode = mode === 'create';
|
|
211
240
|
const effectiveTaskWsId = !isCreateMode ? (taskWsId ?? wsId) : wsId;
|
|
212
241
|
const pathname = usePathname();
|
|
242
|
+
const locale = useLocale();
|
|
213
243
|
const queryClient = useQueryClient();
|
|
214
244
|
const t = useTranslations('common');
|
|
215
245
|
const rootT = useTranslations();
|
|
216
246
|
const dialogT = useTranslations('ws-task-boards.dialog');
|
|
247
|
+
const historyT = useTranslations('tasks.history');
|
|
217
248
|
const { registerCloseRequestHandler } = useTaskDialogContext();
|
|
218
249
|
|
|
219
250
|
// Access workspace tier for tier-aware collaboration settings (null outside the provider)
|
|
@@ -284,7 +315,18 @@ export function TaskEditDialog({
|
|
|
284
315
|
const handleConvertToTaskRef = useRef<(() => Promise<void>) | null>(null);
|
|
285
316
|
const descriptionRef = useRef<JSONContent | null>(formState.description);
|
|
286
317
|
const persistedDescriptionRef = useRef<string | null>(null);
|
|
318
|
+
const descriptionPersistenceGuardRef =
|
|
319
|
+
useRef<TaskDescriptionPersistenceGuardState>(
|
|
320
|
+
createTaskDescriptionPersistenceGuardState({
|
|
321
|
+
persistedDescription: null,
|
|
322
|
+
})
|
|
323
|
+
);
|
|
287
324
|
const taskRealtimeBroadcastRef = useRef<BoardBroadcastFn | null>(null);
|
|
325
|
+
const [hasHydratedYjsState, setHasHydratedYjsState] = useState(false);
|
|
326
|
+
const [showDescriptionVersionsDialog, setShowDescriptionVersionsDialog] =
|
|
327
|
+
useState(false);
|
|
328
|
+
const [restoringDescriptionVersionId, setRestoringDescriptionVersionId] =
|
|
329
|
+
useState<string | null>(null);
|
|
288
330
|
|
|
289
331
|
useEffect(() => {
|
|
290
332
|
descriptionRef.current = formState.description;
|
|
@@ -361,6 +403,54 @@ export function TaskEditDialog({
|
|
|
361
403
|
refetchOnReconnect: false,
|
|
362
404
|
});
|
|
363
405
|
|
|
406
|
+
const persistedTaskDescriptionSnapshot = useMemo(
|
|
407
|
+
() =>
|
|
408
|
+
serializeTaskDescriptionPersistenceSnapshot(
|
|
409
|
+
getDescriptionContent(task?.description)
|
|
410
|
+
) ?? null,
|
|
411
|
+
[task?.description]
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
useEffect(() => {
|
|
415
|
+
if (!isOpen || isCreateMode) {
|
|
416
|
+
persistedDescriptionRef.current = null;
|
|
417
|
+
descriptionPersistenceGuardRef.current =
|
|
418
|
+
createTaskDescriptionPersistenceGuardState({
|
|
419
|
+
persistedDescription: null,
|
|
420
|
+
});
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
persistedDescriptionRef.current = persistedTaskDescriptionSnapshot;
|
|
425
|
+
descriptionPersistenceGuardRef.current =
|
|
426
|
+
createTaskDescriptionPersistenceGuardState({
|
|
427
|
+
persistedDescription: persistedTaskDescriptionSnapshot,
|
|
428
|
+
trustPersistedDescription: !effectiveRealtimeEnabled,
|
|
429
|
+
});
|
|
430
|
+
}, [
|
|
431
|
+
effectiveRealtimeEnabled,
|
|
432
|
+
isOpen,
|
|
433
|
+
isCreateMode,
|
|
434
|
+
persistedTaskDescriptionSnapshot,
|
|
435
|
+
]);
|
|
436
|
+
|
|
437
|
+
const canConfirmEmptyDescriptionSnapshot =
|
|
438
|
+
!effectiveRealtimeEnabled || hasHydratedYjsState;
|
|
439
|
+
|
|
440
|
+
const recordDescriptionSnapshot = useCallback(
|
|
441
|
+
(nextDescription: JSONContent | null) => {
|
|
442
|
+
descriptionPersistenceGuardRef.current =
|
|
443
|
+
recordTaskDescriptionEditorSnapshot(
|
|
444
|
+
descriptionPersistenceGuardRef.current,
|
|
445
|
+
nextDescription,
|
|
446
|
+
{
|
|
447
|
+
canConfirmEmptySnapshot: canConfirmEmptyDescriptionSnapshot,
|
|
448
|
+
}
|
|
449
|
+
);
|
|
450
|
+
},
|
|
451
|
+
[canConfirmEmptyDescriptionSnapshot]
|
|
452
|
+
);
|
|
453
|
+
|
|
364
454
|
const loadTaskDescriptionState = useCallback(async () => {
|
|
365
455
|
if (!task?.id) return null;
|
|
366
456
|
const response = await fetchWorkspaceTaskDescription(
|
|
@@ -387,7 +477,9 @@ export function TaskEditDialog({
|
|
|
387
477
|
return false;
|
|
388
478
|
}
|
|
389
479
|
|
|
390
|
-
|
|
480
|
+
content = normalizeTaskDescriptionSnapshot(content);
|
|
481
|
+
const serializedDescription =
|
|
482
|
+
serializeTaskDescriptionPersistenceSnapshot(content);
|
|
391
483
|
|
|
392
484
|
if (
|
|
393
485
|
serializedDescription &&
|
|
@@ -396,6 +488,15 @@ export function TaskEditDialog({
|
|
|
396
488
|
return false;
|
|
397
489
|
}
|
|
398
490
|
|
|
491
|
+
if (
|
|
492
|
+
!canPersistTaskDescriptionSnapshot({
|
|
493
|
+
currentSerializedDescription: serializedDescription,
|
|
494
|
+
guardState: descriptionPersistenceGuardRef.current,
|
|
495
|
+
})
|
|
496
|
+
) {
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
499
|
+
|
|
399
500
|
const didPersist = await saveYjsDescriptionToDatabase({
|
|
400
501
|
wsId: effectiveTaskWsId,
|
|
401
502
|
taskId: task.id,
|
|
@@ -409,6 +510,11 @@ export function TaskEditDialog({
|
|
|
409
510
|
if (!didPersist) return false;
|
|
410
511
|
|
|
411
512
|
persistedDescriptionRef.current = serializedDescription;
|
|
513
|
+
descriptionPersistenceGuardRef.current =
|
|
514
|
+
createTaskDescriptionPersistenceGuardState({
|
|
515
|
+
persistedDescription: serializedDescription,
|
|
516
|
+
trustPersistedDescription: true,
|
|
517
|
+
});
|
|
412
518
|
|
|
413
519
|
const broadcast =
|
|
414
520
|
getActiveBroadcast() ?? taskRealtimeBroadcastRef.current;
|
|
@@ -438,8 +544,6 @@ export function TaskEditDialog({
|
|
|
438
544
|
saveDocumentState: saveTaskDescriptionState,
|
|
439
545
|
});
|
|
440
546
|
|
|
441
|
-
const [hasHydratedYjsState, setHasHydratedYjsState] = useState(false);
|
|
442
|
-
|
|
443
547
|
useEffect(() => {
|
|
444
548
|
if (!isOpen || isCreateMode || !effectiveRealtimeEnabled || !task?.id) {
|
|
445
549
|
setHasHydratedYjsState(false);
|
|
@@ -469,6 +573,49 @@ export function TaskEditDialog({
|
|
|
469
573
|
synced,
|
|
470
574
|
]);
|
|
471
575
|
|
|
576
|
+
const { data: descriptionHistoryData } = useQuery({
|
|
577
|
+
queryKey: ['task-history', effectiveTaskWsId, task?.id, 'description'],
|
|
578
|
+
queryFn: async () => {
|
|
579
|
+
if (!task?.id) return null;
|
|
580
|
+
|
|
581
|
+
return getWorkspaceTaskHistory(effectiveTaskWsId, task.id, {
|
|
582
|
+
limit: 100,
|
|
583
|
+
changeType: 'field_updated',
|
|
584
|
+
fieldName: 'description',
|
|
585
|
+
});
|
|
586
|
+
},
|
|
587
|
+
enabled:
|
|
588
|
+
isOpen &&
|
|
589
|
+
!isCreateMode &&
|
|
590
|
+
!taskControlsDisabled &&
|
|
591
|
+
Boolean(effectiveTaskWsId) &&
|
|
592
|
+
Boolean(task?.id),
|
|
593
|
+
staleTime: 30 * 1000,
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
const recoverableDescriptionVersions = useMemo(
|
|
597
|
+
() =>
|
|
598
|
+
buildRecoverableTaskDescriptionVersions(
|
|
599
|
+
descriptionHistoryData?.history ?? []
|
|
600
|
+
),
|
|
601
|
+
[descriptionHistoryData?.history]
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const currentSerializedDescription = useMemo(
|
|
605
|
+
() =>
|
|
606
|
+
serializeTaskDescriptionPersistenceSnapshot(formState.description) ??
|
|
607
|
+
null,
|
|
608
|
+
[formState.description]
|
|
609
|
+
);
|
|
610
|
+
|
|
611
|
+
const latestRestorableDescriptionVersion = useMemo(() => {
|
|
612
|
+
const latestVersion = recoverableDescriptionVersions[0] ?? null;
|
|
613
|
+
if (!latestVersion) return null;
|
|
614
|
+
return latestVersion.description === currentSerializedDescription
|
|
615
|
+
? null
|
|
616
|
+
: latestVersion;
|
|
617
|
+
}, [currentSerializedDescription, recoverableDescriptionVersions]);
|
|
618
|
+
|
|
472
619
|
// Update user when props change
|
|
473
620
|
useEffect(() => {
|
|
474
621
|
if (propsCurrentUser) {
|
|
@@ -494,6 +641,8 @@ export function TaskEditDialog({
|
|
|
494
641
|
isOpen,
|
|
495
642
|
propAvailableLists,
|
|
496
643
|
taskSearchQuery,
|
|
644
|
+
canUseBoardAssignees: canUseBoardAssignees ?? !isPersonalWorkspace,
|
|
645
|
+
assigneeMemberSource,
|
|
497
646
|
sharedContext,
|
|
498
647
|
});
|
|
499
648
|
const currentList = availableLists?.find(
|
|
@@ -776,34 +925,45 @@ export function TaskEditDialog({
|
|
|
776
925
|
});
|
|
777
926
|
|
|
778
927
|
// Change detection
|
|
779
|
-
const { hasUnsavedChanges, canSave
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
});
|
|
928
|
+
const { hasUnsavedChanges, canSave } = useTaskChangeDetection({
|
|
929
|
+
task,
|
|
930
|
+
name: formState.name,
|
|
931
|
+
description: formState.description,
|
|
932
|
+
priority: formState.priority,
|
|
933
|
+
startDate: formState.startDate,
|
|
934
|
+
endDate: formState.endDate,
|
|
935
|
+
selectedListId: formState.selectedListId,
|
|
936
|
+
estimationPoints: formState.estimationPoints,
|
|
937
|
+
selectedLabels: formState.selectedLabels,
|
|
938
|
+
selectedAssignees: formState.selectedAssignees,
|
|
939
|
+
isCreateMode,
|
|
940
|
+
isLoading: isLoading || taskControlsDisabled,
|
|
941
|
+
collaborationMode: effectiveCollaborationMode,
|
|
942
|
+
draftId,
|
|
943
|
+
});
|
|
796
944
|
|
|
797
945
|
useEffect(() => {
|
|
798
|
-
if (!isOpen || isCreateMode)
|
|
799
|
-
|
|
946
|
+
if (!isOpen || isCreateMode) return;
|
|
947
|
+
if (effectiveRealtimeEnabled && !hasHydratedYjsState) return;
|
|
948
|
+
if (
|
|
949
|
+
serializeTaskDescriptionPersistenceSnapshot(formState.description) ===
|
|
950
|
+
null
|
|
951
|
+
) {
|
|
800
952
|
return;
|
|
801
953
|
}
|
|
802
954
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
955
|
+
descriptionPersistenceGuardRef.current =
|
|
956
|
+
recordTaskDescriptionEditorSnapshot(
|
|
957
|
+
descriptionPersistenceGuardRef.current,
|
|
958
|
+
formState.description
|
|
959
|
+
);
|
|
960
|
+
}, [
|
|
961
|
+
effectiveRealtimeEnabled,
|
|
962
|
+
formState.description,
|
|
963
|
+
hasHydratedYjsState,
|
|
964
|
+
isOpen,
|
|
965
|
+
isCreateMode,
|
|
966
|
+
]);
|
|
807
967
|
|
|
808
968
|
// Realtime sync for task fields and relations. Description sync remains Yjs-only.
|
|
809
969
|
const { broadcast: taskRealtimeBroadcast } = useTaskRealtimeSync({
|
|
@@ -1020,6 +1180,135 @@ export function TaskEditDialog({
|
|
|
1020
1180
|
);
|
|
1021
1181
|
}, []);
|
|
1022
1182
|
|
|
1183
|
+
const applyRestoredDescriptionToOpenEditor = useCallback(
|
|
1184
|
+
({
|
|
1185
|
+
content,
|
|
1186
|
+
yjsState,
|
|
1187
|
+
}: {
|
|
1188
|
+
content: JSONContent | null;
|
|
1189
|
+
yjsState: number[] | null;
|
|
1190
|
+
}) => {
|
|
1191
|
+
const nextContent = content ?? EMPTY_TASK_DESCRIPTION_DOC;
|
|
1192
|
+
|
|
1193
|
+
const nextYjsState =
|
|
1194
|
+
yjsState && yjsState.length > 0
|
|
1195
|
+
? Uint8Array.from(yjsState)
|
|
1196
|
+
: doc && editorInstance?.schema
|
|
1197
|
+
? convertJsonContentToYjsState(nextContent, editorInstance.schema)
|
|
1198
|
+
: null;
|
|
1199
|
+
|
|
1200
|
+
if (doc && nextYjsState) {
|
|
1201
|
+
const syncOrigin = provider ?? SUPABASE_PROVIDER_SYNC_ORIGIN;
|
|
1202
|
+
doc.transact(() => {
|
|
1203
|
+
const fragment = doc.getXmlFragment('prosemirror');
|
|
1204
|
+
if (fragment.length > 0) {
|
|
1205
|
+
fragment.delete(0, fragment.length);
|
|
1206
|
+
}
|
|
1207
|
+
}, syncOrigin);
|
|
1208
|
+
Y.applyUpdate(doc, nextYjsState, syncOrigin);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
editorInstance?.commands.setContent(nextContent, {
|
|
1213
|
+
emitUpdate: false,
|
|
1214
|
+
});
|
|
1215
|
+
},
|
|
1216
|
+
[doc, editorInstance, provider]
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
const handleRestoreDescriptionVersion = useCallback(
|
|
1220
|
+
async (version: RecoverableTaskDescriptionVersion) => {
|
|
1221
|
+
if (!task?.id) return;
|
|
1222
|
+
|
|
1223
|
+
setRestoringDescriptionVersionId(version.id);
|
|
1224
|
+
|
|
1225
|
+
try {
|
|
1226
|
+
const response = await updateWorkspaceTaskDescription(
|
|
1227
|
+
effectiveTaskWsId,
|
|
1228
|
+
task.id,
|
|
1229
|
+
{ description: version.description }
|
|
1230
|
+
);
|
|
1231
|
+
const restoredDescription = response.description ?? version.description;
|
|
1232
|
+
const restoredContent = normalizeTaskDescriptionSnapshot(
|
|
1233
|
+
getDescriptionContent(restoredDescription)
|
|
1234
|
+
);
|
|
1235
|
+
const serializedRestoredDescription =
|
|
1236
|
+
serializeTaskDescriptionPersistenceSnapshot(restoredContent) ?? null;
|
|
1237
|
+
|
|
1238
|
+
applyRestoredDescriptionToOpenEditor({
|
|
1239
|
+
content: restoredContent,
|
|
1240
|
+
yjsState: response.description_yjs_state,
|
|
1241
|
+
});
|
|
1242
|
+
formState.setDescription(restoredContent);
|
|
1243
|
+
setDescriptionStorageLength(
|
|
1244
|
+
getTaskDescriptionStorageLength(restoredContent)
|
|
1245
|
+
);
|
|
1246
|
+
persistedDescriptionRef.current = serializedRestoredDescription;
|
|
1247
|
+
descriptionPersistenceGuardRef.current =
|
|
1248
|
+
createTaskDescriptionPersistenceGuardState({
|
|
1249
|
+
persistedDescription: serializedRestoredDescription,
|
|
1250
|
+
trustPersistedDescription: true,
|
|
1251
|
+
});
|
|
1252
|
+
setHasHydratedYjsState(true);
|
|
1253
|
+
|
|
1254
|
+
updateTaskDescriptionCaches({
|
|
1255
|
+
taskId: task.id,
|
|
1256
|
+
descriptionString: serializedRestoredDescription,
|
|
1257
|
+
boardId,
|
|
1258
|
+
queryClient,
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
await Promise.all([
|
|
1262
|
+
queryClient.invalidateQueries({
|
|
1263
|
+
queryKey: ['task-history', effectiveTaskWsId, task.id],
|
|
1264
|
+
}),
|
|
1265
|
+
queryClient.invalidateQueries({
|
|
1266
|
+
queryKey: ['task', task.id],
|
|
1267
|
+
}),
|
|
1268
|
+
queryClient.invalidateQueries({
|
|
1269
|
+
queryKey: ['task-snapshot', effectiveTaskWsId, task.id],
|
|
1270
|
+
}),
|
|
1271
|
+
]);
|
|
1272
|
+
|
|
1273
|
+
const broadcast =
|
|
1274
|
+
getActiveBroadcast() ?? taskRealtimeBroadcastRef.current;
|
|
1275
|
+
broadcastTaskDescriptionUpsert({
|
|
1276
|
+
taskId: task.id,
|
|
1277
|
+
descriptionString: serializedRestoredDescription,
|
|
1278
|
+
broadcast: broadcast ?? undefined,
|
|
1279
|
+
});
|
|
1280
|
+
|
|
1281
|
+
onUpdate();
|
|
1282
|
+
setShowDescriptionVersionsDialog(false);
|
|
1283
|
+
toast.success(
|
|
1284
|
+
historyT('description_restore_success', {
|
|
1285
|
+
defaultValue: 'Description restored',
|
|
1286
|
+
})
|
|
1287
|
+
);
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
toast.error(
|
|
1290
|
+
error instanceof Error
|
|
1291
|
+
? error.message
|
|
1292
|
+
: historyT('description_restore_failed', {
|
|
1293
|
+
defaultValue: 'Failed to restore description',
|
|
1294
|
+
})
|
|
1295
|
+
);
|
|
1296
|
+
} finally {
|
|
1297
|
+
setRestoringDescriptionVersionId(null);
|
|
1298
|
+
}
|
|
1299
|
+
},
|
|
1300
|
+
[
|
|
1301
|
+
applyRestoredDescriptionToOpenEditor,
|
|
1302
|
+
boardId,
|
|
1303
|
+
effectiveTaskWsId,
|
|
1304
|
+
formState.setDescription,
|
|
1305
|
+
historyT,
|
|
1306
|
+
onUpdate,
|
|
1307
|
+
queryClient,
|
|
1308
|
+
task?.id,
|
|
1309
|
+
]
|
|
1310
|
+
);
|
|
1311
|
+
|
|
1023
1312
|
const isDescriptionOverLimit =
|
|
1024
1313
|
descriptionStorageLength > MAX_TASK_DESCRIPTION_LENGTH;
|
|
1025
1314
|
const descriptionPercentLeft = getTaskDescriptionPercentLeft(
|
|
@@ -1385,7 +1674,8 @@ export function TaskEditDialog({
|
|
|
1385
1674
|
return createWorkspaceTaskSuggestions(effectiveTaskWsId, {
|
|
1386
1675
|
boardId,
|
|
1387
1676
|
prompt,
|
|
1388
|
-
description:
|
|
1677
|
+
description:
|
|
1678
|
+
serializeTaskDescriptionPersistenceSnapshot(currentDescription),
|
|
1389
1679
|
currentListId: formState.selectedListId || undefined,
|
|
1390
1680
|
clientTimezone:
|
|
1391
1681
|
Intl.DateTimeFormat().resolvedOptions().timeZone || undefined,
|
|
@@ -1682,9 +1972,11 @@ export function TaskEditDialog({
|
|
|
1682
1972
|
|
|
1683
1973
|
closeBlockedByOverflowRef.current = false;
|
|
1684
1974
|
|
|
1685
|
-
const currentContent =
|
|
1975
|
+
const currentContent = normalizeTaskDescriptionSnapshot(
|
|
1976
|
+
flushEditorPendingRef.current()
|
|
1977
|
+
);
|
|
1686
1978
|
const currentSerializedDescription =
|
|
1687
|
-
|
|
1979
|
+
serializeTaskDescriptionPersistenceSnapshot(currentContent) ?? null;
|
|
1688
1980
|
|
|
1689
1981
|
if (
|
|
1690
1982
|
currentSerializedDescription &&
|
|
@@ -1701,6 +1993,15 @@ export function TaskEditDialog({
|
|
|
1701
1993
|
return true;
|
|
1702
1994
|
}
|
|
1703
1995
|
|
|
1996
|
+
if (
|
|
1997
|
+
!canPersistTaskDescriptionSnapshot({
|
|
1998
|
+
currentSerializedDescription,
|
|
1999
|
+
guardState: descriptionPersistenceGuardRef.current,
|
|
2000
|
+
})
|
|
2001
|
+
) {
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
|
|
1704
2005
|
const yjsState =
|
|
1705
2006
|
currentContent && editorInstance?.schema
|
|
1706
2007
|
? Array.from(
|
|
@@ -1720,6 +2021,11 @@ export function TaskEditDialog({
|
|
|
1720
2021
|
|
|
1721
2022
|
if (didPersist) {
|
|
1722
2023
|
persistedDescriptionRef.current = currentSerializedDescription;
|
|
2024
|
+
descriptionPersistenceGuardRef.current =
|
|
2025
|
+
createTaskDescriptionPersistenceGuardState({
|
|
2026
|
+
persistedDescription: currentSerializedDescription,
|
|
2027
|
+
trustPersistedDescription: true,
|
|
2028
|
+
});
|
|
1723
2029
|
}
|
|
1724
2030
|
|
|
1725
2031
|
return didPersist;
|
|
@@ -1737,9 +2043,11 @@ export function TaskEditDialog({
|
|
|
1737
2043
|
return false;
|
|
1738
2044
|
}
|
|
1739
2045
|
|
|
1740
|
-
const currentContent =
|
|
2046
|
+
const currentContent = normalizeTaskDescriptionSnapshot(
|
|
2047
|
+
flushEditorPendingRef.current()
|
|
2048
|
+
);
|
|
1741
2049
|
const currentSerializedDescription =
|
|
1742
|
-
|
|
2050
|
+
serializeTaskDescriptionPersistenceSnapshot(currentContent) ?? null;
|
|
1743
2051
|
|
|
1744
2052
|
if (
|
|
1745
2053
|
currentSerializedDescription &&
|
|
@@ -2166,6 +2474,7 @@ export function TaskEditDialog({
|
|
|
2166
2474
|
selectedAssignees={formState.selectedAssignees}
|
|
2167
2475
|
isLoading={isLoading}
|
|
2168
2476
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
2477
|
+
canUseBoardAssignees={canUseBoardAssignees ?? !isPersonalWorkspace}
|
|
2169
2478
|
totalDuration={formState.totalDuration}
|
|
2170
2479
|
isSplittable={formState.isSplittable}
|
|
2171
2480
|
minSplitDurationMinutes={formState.minSplitDurationMinutes}
|
|
@@ -2512,6 +2821,30 @@ export function TaskEditDialog({
|
|
|
2512
2821
|
/>
|
|
2513
2822
|
)}
|
|
2514
2823
|
|
|
2824
|
+
{!taskControlsDisabled && !isCreateMode && (
|
|
2825
|
+
<TaskDescriptionRestoreBanner
|
|
2826
|
+
isRestoring={Boolean(restoringDescriptionVersionId)}
|
|
2827
|
+
latestVersion={latestRestorableDescriptionVersion}
|
|
2828
|
+
onRestoreLatest={() => {
|
|
2829
|
+
if (latestRestorableDescriptionVersion) {
|
|
2830
|
+
void handleRestoreDescriptionVersion(
|
|
2831
|
+
latestRestorableDescriptionVersion
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
}}
|
|
2835
|
+
onViewVersions={() =>
|
|
2836
|
+
setShowDescriptionVersionsDialog(true)
|
|
2837
|
+
}
|
|
2838
|
+
t={
|
|
2839
|
+
historyT as (
|
|
2840
|
+
key: string,
|
|
2841
|
+
options?: { count?: number; defaultValue?: string }
|
|
2842
|
+
) => string
|
|
2843
|
+
}
|
|
2844
|
+
versionCount={recoverableDescriptionVersions.length}
|
|
2845
|
+
/>
|
|
2846
|
+
)}
|
|
2847
|
+
|
|
2515
2848
|
<TaskDescriptionEditor
|
|
2516
2849
|
description={formState.description}
|
|
2517
2850
|
setDescription={formState.setDescription}
|
|
@@ -2545,6 +2878,7 @@ export function TaskEditDialog({
|
|
|
2545
2878
|
onImageUpload={imageUploadHandler}
|
|
2546
2879
|
onEditorReady={handleEditorReady}
|
|
2547
2880
|
onConvertToTask={handleConvertToTask}
|
|
2881
|
+
onDescriptionSnapshotChange={recordDescriptionSnapshot}
|
|
2548
2882
|
onDescriptionStorageLengthChange={
|
|
2549
2883
|
handleDescriptionStorageLengthChange
|
|
2550
2884
|
}
|
|
@@ -2624,7 +2958,13 @@ export function TaskEditDialog({
|
|
|
2624
2958
|
id: p.id,
|
|
2625
2959
|
})),
|
|
2626
2960
|
}}
|
|
2961
|
+
onRestoreDescriptionVersion={
|
|
2962
|
+
handleRestoreDescriptionVersion
|
|
2963
|
+
}
|
|
2627
2964
|
revertDisabled={true}
|
|
2965
|
+
restoringDescriptionVersionId={
|
|
2966
|
+
restoringDescriptionVersionId
|
|
2967
|
+
}
|
|
2628
2968
|
/>
|
|
2629
2969
|
)}
|
|
2630
2970
|
</div>
|
|
@@ -2646,6 +2986,22 @@ export function TaskEditDialog({
|
|
|
2646
2986
|
</DialogContent>
|
|
2647
2987
|
</Dialog>
|
|
2648
2988
|
|
|
2989
|
+
<TaskDescriptionVersionRestoreDialog
|
|
2990
|
+
currentDescription={currentSerializedDescription}
|
|
2991
|
+
isOpen={showDescriptionVersionsDialog}
|
|
2992
|
+
locale={locale}
|
|
2993
|
+
onClose={() => setShowDescriptionVersionsDialog(false)}
|
|
2994
|
+
onRestoreVersion={handleRestoreDescriptionVersion}
|
|
2995
|
+
restoringVersionId={restoringDescriptionVersionId}
|
|
2996
|
+
t={
|
|
2997
|
+
historyT as (
|
|
2998
|
+
key: string,
|
|
2999
|
+
options?: { count?: number; defaultValue?: string }
|
|
3000
|
+
) => string
|
|
3001
|
+
}
|
|
3002
|
+
versions={recoverableDescriptionVersions}
|
|
3003
|
+
/>
|
|
3004
|
+
|
|
2649
3005
|
<TaskDeleteDialog
|
|
2650
3006
|
open={showDeleteConfirm}
|
|
2651
3007
|
onOpenChange={setShowDeleteConfirm}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
TaskSchedulingMenu,
|
|
42
42
|
} from '../boards/boardId/menus';
|
|
43
43
|
import { useTaskDialog } from '../hooks/useTaskDialog';
|
|
44
|
+
import type { TaskAssigneeMemberSource } from '../providers/task-dialog-provider';
|
|
44
45
|
import { useTasksHref } from '../tasks-route-context';
|
|
45
46
|
|
|
46
47
|
interface TaskRowActionsMenuProps {
|
|
@@ -49,6 +50,8 @@ interface TaskRowActionsMenuProps {
|
|
|
49
50
|
workspaceId: string;
|
|
50
51
|
lists: TaskList[];
|
|
51
52
|
isPersonalWorkspace?: boolean;
|
|
53
|
+
canUseBoardAssignees?: boolean;
|
|
54
|
+
assigneeMemberSource?: TaskAssigneeMemberSource;
|
|
52
55
|
onUpdate: () => void;
|
|
53
56
|
open?: boolean;
|
|
54
57
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -78,6 +81,8 @@ export function TaskRowActionsMenu({
|
|
|
78
81
|
workspaceId,
|
|
79
82
|
lists,
|
|
80
83
|
isPersonalWorkspace = false,
|
|
84
|
+
canUseBoardAssignees,
|
|
85
|
+
assigneeMemberSource,
|
|
81
86
|
onUpdate,
|
|
82
87
|
open,
|
|
83
88
|
onOpenChange,
|
|
@@ -171,6 +176,12 @@ export function TaskRowActionsMenu({
|
|
|
171
176
|
taskWorkspacePersonal: task.source_workspace_id
|
|
172
177
|
? false
|
|
173
178
|
: isPersonalWorkspace,
|
|
179
|
+
canUseBoardAssignees:
|
|
180
|
+
canUseBoardAssignees ??
|
|
181
|
+
(task.source_workspace_id ? true : !isPersonalWorkspace),
|
|
182
|
+
assigneeMemberSource: task.source_workspace_id
|
|
183
|
+
? 'workspace'
|
|
184
|
+
: assigneeMemberSource,
|
|
174
185
|
}
|
|
175
186
|
);
|
|
176
187
|
setMenuOpen(false);
|
|
@@ -93,6 +93,7 @@ export function useProgressiveBoardLoader(
|
|
|
93
93
|
offset: page * PAGE_SIZE,
|
|
94
94
|
includeCount: true,
|
|
95
95
|
...options,
|
|
96
|
+
includeRelationshipSummary: false,
|
|
96
97
|
});
|
|
97
98
|
const tasks = payload.tasks ?? [];
|
|
98
99
|
const loadedThrough = page * PAGE_SIZE + tasks.length;
|
|
@@ -206,6 +207,7 @@ export function useProgressiveBoardLoader(
|
|
|
206
207
|
offset: page * PAGE_SIZE,
|
|
207
208
|
includeCount: true,
|
|
208
209
|
...listOptionsRef.current[listId],
|
|
210
|
+
includeRelationshipSummary: false,
|
|
209
211
|
})
|
|
210
212
|
)
|
|
211
213
|
);
|