@tuturuuu/ui 0.8.0 → 0.9.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 +40 -0
- package/biome.json +1 -1
- package/package.json +73 -71
- package/src/components/ui/accordion.tsx +1 -1
- package/src/components/ui/breadcrumb.tsx +1 -1
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
- package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
- package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
- package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
- package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
- package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/carousel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
- package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
- package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
- package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/color-picker.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/components/ui/context-menu.tsx +5 -1
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +19 -0
- package/src/components/ui/custom/combobox.test.tsx +195 -0
- package/src/components/ui/custom/combobox.tsx +273 -156
- package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
- package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
- package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
- package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
- package/src/components/ui/custom/settings-dialog-shell.tsx +2 -1
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-select.tsx +8 -3
- package/src/components/ui/dialog.test.tsx +52 -0
- package/src/components/ui/dialog.tsx +6 -2
- package/src/components/ui/dropdown-menu.tsx +5 -1
- package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
- package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
- package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
- package/src/components/ui/finance/debts/debts-page.tsx +15 -2
- package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
- package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
- package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
- package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
- package/src/components/ui/finance/invoices/utils.ts +3 -1
- package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
- package/src/components/ui/finance/transactions/form-types.ts +1 -0
- package/src/components/ui/finance/transactions/form.tsx +2 -0
- package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
- package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
- package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
- package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
- package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
- package/src/components/ui/finance/wallets/form.test.tsx +51 -3
- package/src/components/ui/finance/wallets/form.tsx +15 -4
- package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
- package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
- package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
- package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
- package/src/components/ui/input-otp.tsx +1 -1
- package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
- package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
- package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
- package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
- package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
- package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
- package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
- package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
- package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
- package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
- package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
- package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
- package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
- package/src/components/ui/navigation-menu.tsx +1 -1
- package/src/components/ui/pagination.tsx +1 -1
- package/src/components/ui/radio-group.tsx +1 -1
- package/src/components/ui/select.tsx +5 -1
- package/src/components/ui/sheet.tsx +1 -1
- package/src/components/ui/sidebar.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +61 -0
- package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
- package/src/components/ui/storefront/cart-summary.tsx +93 -154
- package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
- package/src/components/ui/storefront/listing-card.tsx +1 -1
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +1 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
- package/src/components/ui/storefront/storefront-surface.tsx +101 -166
- package/src/components/ui/storefront/types.ts +4 -0
- package/src/components/ui/storefront/utils.ts +6 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/background-color-extension.ts +62 -0
- package/src/components/ui/text-editor/color-controls.tsx +284 -0
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +8 -2
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/toast.tsx +1 -1
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
- package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +112 -43
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +397 -2
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +103 -13
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +26 -4
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +5 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
- package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
- package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
- package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
- package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
- package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
- package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +203 -2
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
- package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +464 -975
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
- package/src/components/ui/tu-do/shared/board-views.tsx +587 -75
- package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
- package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
- package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +2 -1
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- package/src/declarations.d.ts +1 -0
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
- package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
- package/src/hooks/use-calendar-sync.tsx +247 -243
- package/src/hooks/use-calendar.tsx +323 -138
- package/src/hooks/use-task-actions.ts +24 -0
- package/src/hooks/use-user-workspace-config.ts +75 -0
- package/src/hooks/use-workspace-currency.ts +8 -3
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
|
@@ -4,6 +4,7 @@ import { type QueryClient, useMutation } from '@tanstack/react-query';
|
|
|
4
4
|
import { bulkWorkspaceTasks } from '@tuturuuu/internal-api/tasks';
|
|
5
5
|
import { toast } from '@tuturuuu/ui/sonner';
|
|
6
6
|
import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
|
|
7
|
+
import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
|
|
7
8
|
import type { BulkOperationI18n } from './bulk-operation-i18n';
|
|
8
9
|
import {
|
|
9
10
|
type BulkTaskWorkspaceGroup,
|
|
@@ -390,6 +391,7 @@ export function useBulkDeleteTasks(
|
|
|
390
391
|
for (const tid of succeededTaskIds) {
|
|
391
392
|
broadcast?.('task:delete', { taskId: tid });
|
|
392
393
|
}
|
|
394
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
393
395
|
|
|
394
396
|
clearSelection();
|
|
395
397
|
setBulkDeleteOpen(false);
|
|
@@ -6,6 +6,7 @@ import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
|
6
6
|
import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
|
|
7
7
|
import { toast } from '@tuturuuu/ui/sonner';
|
|
8
8
|
import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
|
|
9
|
+
import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
|
|
9
10
|
import type { BulkOperationI18n } from './bulk-operation-i18n';
|
|
10
11
|
import { getInternalApiOptions } from './bulk-operation-utils';
|
|
11
12
|
import {
|
|
@@ -184,6 +185,8 @@ export function useBulkMoveToBoard(
|
|
|
184
185
|
for (const tid of movedTaskIds) {
|
|
185
186
|
broadcast?.('task:delete', { taskId: tid });
|
|
186
187
|
}
|
|
188
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
189
|
+
void invalidateKanbanDeadlineTasks(queryClient, data.targetBoardId);
|
|
187
190
|
|
|
188
191
|
if (data.failures.length > 0) {
|
|
189
192
|
toast.warning(
|
|
@@ -428,6 +431,7 @@ export function useBulkMoveToList(
|
|
|
428
431
|
},
|
|
429
432
|
});
|
|
430
433
|
}
|
|
434
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
431
435
|
|
|
432
436
|
if (data.failures.length > 0) {
|
|
433
437
|
toast.warning(
|
|
@@ -631,6 +635,7 @@ export function useBulkMoveToStatus(
|
|
|
631
635
|
},
|
|
632
636
|
});
|
|
633
637
|
}
|
|
638
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
634
639
|
|
|
635
640
|
if (data.failures.length > 0) {
|
|
636
641
|
toast.warning(
|
|
@@ -4,6 +4,7 @@ import { type QueryClient, useMutation } from '@tanstack/react-query';
|
|
|
4
4
|
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
5
5
|
import { toast } from '@tuturuuu/ui/sonner';
|
|
6
6
|
import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
|
|
7
|
+
import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
|
|
7
8
|
import type { BulkOperationI18n } from './bulk-operation-i18n';
|
|
8
9
|
import {
|
|
9
10
|
bulkWorkspaceTasksByEffectiveWorkspace,
|
|
@@ -322,6 +323,7 @@ export function useBulkUpdateDueDate(
|
|
|
322
323
|
task: { id: tid, end_date: data.end_date },
|
|
323
324
|
});
|
|
324
325
|
}
|
|
326
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
325
327
|
|
|
326
328
|
if (data.failures.length > 0) {
|
|
327
329
|
toast.warning(
|
|
@@ -434,6 +436,7 @@ export function useBulkUpdateCustomDueDate(
|
|
|
434
436
|
task: { id: tid, end_date: data.end_date },
|
|
435
437
|
});
|
|
436
438
|
}
|
|
439
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
437
440
|
|
|
438
441
|
if (data.failures.length > 0) {
|
|
439
442
|
toast.warning(
|
|
@@ -1,22 +1,70 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
1
2
|
import { listWorkspaceTasks } from '@tuturuuu/internal-api';
|
|
3
|
+
import type { ListWorkspaceTasksOptions } from '@tuturuuu/internal-api/tasks';
|
|
2
4
|
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
3
5
|
|
|
4
6
|
const KANBAN_DEADLINE_TASK_PAGE_SIZE = 200;
|
|
7
|
+
export const KANBAN_DEADLINE_TASKS_QUERY_KEY = 'kanban-deadline-tasks';
|
|
8
|
+
|
|
9
|
+
export function getKanbanDeadlineTasksQueryKey(
|
|
10
|
+
workspaceId: string,
|
|
11
|
+
boardId: string | null | undefined,
|
|
12
|
+
taskQueryOptions?: ListWorkspaceTasksOptions
|
|
13
|
+
) {
|
|
14
|
+
return [
|
|
15
|
+
KANBAN_DEADLINE_TASKS_QUERY_KEY,
|
|
16
|
+
workspaceId,
|
|
17
|
+
boardId,
|
|
18
|
+
taskQueryOptions,
|
|
19
|
+
] as const;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function invalidateKanbanDeadlineTasks(
|
|
23
|
+
queryClient: QueryClient,
|
|
24
|
+
boardId?: string | null
|
|
25
|
+
) {
|
|
26
|
+
return queryClient.invalidateQueries({
|
|
27
|
+
predicate: (query) => {
|
|
28
|
+
const queryKey = query.queryKey;
|
|
29
|
+
if (!Array.isArray(queryKey)) return false;
|
|
30
|
+
if (queryKey[0] !== KANBAN_DEADLINE_TASKS_QUERY_KEY) return false;
|
|
31
|
+
return !boardId || queryKey[2] === boardId;
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
}
|
|
5
35
|
|
|
6
36
|
interface ListKanbanDeadlineTasksOptions {
|
|
7
37
|
boardId: string;
|
|
38
|
+
taskQueryOptions?: ListWorkspaceTasksOptions;
|
|
8
39
|
workspaceId: string;
|
|
9
40
|
}
|
|
10
41
|
|
|
11
42
|
export async function listKanbanDeadlineTasks({
|
|
12
43
|
boardId,
|
|
44
|
+
taskQueryOptions,
|
|
13
45
|
workspaceId,
|
|
14
46
|
}: ListKanbanDeadlineTasksOptions): Promise<Task[]> {
|
|
15
47
|
const tasks: Task[] = [];
|
|
16
48
|
let offset = 0;
|
|
49
|
+
const {
|
|
50
|
+
boardId: _boardId,
|
|
51
|
+
closed: _closed,
|
|
52
|
+
completed: _completed,
|
|
53
|
+
externalSortBy: _externalSortBy,
|
|
54
|
+
hasDueDate: _hasDueDate,
|
|
55
|
+
includeCount: _includeCount,
|
|
56
|
+
includeListCounts: _includeListCounts,
|
|
57
|
+
includeRelationshipSummary: _includeRelationshipSummary,
|
|
58
|
+
limit: _limit,
|
|
59
|
+
listId: _listId,
|
|
60
|
+
offset: _offset,
|
|
61
|
+
sortBy: _sortBy,
|
|
62
|
+
...filterOptions
|
|
63
|
+
} = taskQueryOptions ?? {};
|
|
17
64
|
|
|
18
65
|
while (true) {
|
|
19
66
|
const response = await listWorkspaceTasks(workspaceId, {
|
|
67
|
+
...filterOptions,
|
|
20
68
|
boardId,
|
|
21
69
|
closed: 'exclude',
|
|
22
70
|
completed: 'exclude',
|
|
@@ -25,9 +73,9 @@ export async function listKanbanDeadlineTasks({
|
|
|
25
73
|
includeCount: true,
|
|
26
74
|
includeRelationshipSummary: false,
|
|
27
75
|
limit: KANBAN_DEADLINE_TASK_PAGE_SIZE,
|
|
28
|
-
listStatuses: ['not_started', 'active'],
|
|
76
|
+
listStatuses: filterOptions.listStatuses ?? ['not_started', 'active'],
|
|
29
77
|
offset,
|
|
30
|
-
sourceScope: 'all_visible',
|
|
78
|
+
sourceScope: filterOptions.sourceScope ?? 'all_visible',
|
|
31
79
|
});
|
|
32
80
|
|
|
33
81
|
tasks.push(...response.tasks);
|
|
@@ -43,6 +43,23 @@ describe('getColumnReorderUpdates', () => {
|
|
|
43
43
|
expect(getColumnReorderUpdates(columns, 'todo', 'doing')).toBeNull();
|
|
44
44
|
});
|
|
45
45
|
|
|
46
|
+
it('excludes synthetic external staging lanes from persisted position repairs', () => {
|
|
47
|
+
const externalLane = {
|
|
48
|
+
...makeList('external', 'not_started', -1),
|
|
49
|
+
is_external_staging: true,
|
|
50
|
+
};
|
|
51
|
+
const columns = [
|
|
52
|
+
externalLane,
|
|
53
|
+
makeList('todo', 'not_started', 0),
|
|
54
|
+
makeList('backlog', 'not_started', 1),
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
expect(getColumnReorderUpdates(columns, 'backlog', 'todo')).toEqual([
|
|
58
|
+
{ listId: 'backlog', newPosition: 0 },
|
|
59
|
+
{ listId: 'todo', newPosition: 1 },
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
46
63
|
it('sorts columns in the same order they are rendered in Kanban', () => {
|
|
47
64
|
const columns = [
|
|
48
65
|
makeList('closed', 'closed', 0),
|
|
@@ -39,7 +39,10 @@ export function getColumnReorderUpdates(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
const statusColumns = [...columns]
|
|
42
|
-
.filter(
|
|
42
|
+
.filter(
|
|
43
|
+
(column) =>
|
|
44
|
+
column.status === activeColumn.status && !column.is_external_staging
|
|
45
|
+
)
|
|
43
46
|
.sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
|
|
44
47
|
|
|
45
48
|
const activeIndex = statusColumns.findIndex(
|
|
@@ -3,17 +3,27 @@ import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
|
3
3
|
import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
|
|
4
4
|
import { MAX_SAFE_INTEGER_SORT } from '../kanban-constants';
|
|
5
5
|
import type { DragCacheSnapshot, TaskSortKeyRepair } from './task-drag-types';
|
|
6
|
+
import { getEffectiveTaskSortKey } from './task-sort-key';
|
|
6
7
|
|
|
7
8
|
const SORT_KEY_BASE_UNIT = 1_000_000;
|
|
8
9
|
const SORT_KEY_DEFAULT = SORT_KEY_BASE_UNIT * 1000;
|
|
9
10
|
const SORT_KEY_MIN_GAP = 1000;
|
|
10
11
|
|
|
12
|
+
type SortKeyPlanTask = Pick<
|
|
13
|
+
Task,
|
|
14
|
+
| 'id'
|
|
15
|
+
| 'is_personal_external'
|
|
16
|
+
| 'is_personal_external_default'
|
|
17
|
+
| 'personal_sort_key'
|
|
18
|
+
| 'sort_key'
|
|
19
|
+
>;
|
|
20
|
+
|
|
11
21
|
function getTaskSortKeyInsertionContext({
|
|
12
22
|
activeTaskId,
|
|
13
23
|
orderedTasks,
|
|
14
24
|
}: {
|
|
15
25
|
activeTaskId: string;
|
|
16
|
-
orderedTasks:
|
|
26
|
+
orderedTasks: SortKeyPlanTask[];
|
|
17
27
|
}) {
|
|
18
28
|
const activeIndex = orderedTasks.findIndex(
|
|
19
29
|
(task) => task.id === activeTaskId
|
|
@@ -26,10 +36,15 @@ function getTaskSortKeyInsertionContext({
|
|
|
26
36
|
};
|
|
27
37
|
}
|
|
28
38
|
|
|
39
|
+
const nextTask = orderedTasks[activeIndex + 1];
|
|
40
|
+
const previousTask = orderedTasks[activeIndex - 1];
|
|
41
|
+
|
|
29
42
|
return {
|
|
30
43
|
activeIndex,
|
|
31
|
-
nextSortKey:
|
|
32
|
-
previousSortKey:
|
|
44
|
+
nextSortKey: nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
45
|
+
previousSortKey: previousTask
|
|
46
|
+
? getEffectiveTaskSortKey(previousTask)
|
|
47
|
+
: null,
|
|
33
48
|
};
|
|
34
49
|
}
|
|
35
50
|
|
|
@@ -94,7 +109,7 @@ function getPreviewSortKeyPlan({
|
|
|
94
109
|
targetListId,
|
|
95
110
|
}: {
|
|
96
111
|
activeTaskId: string;
|
|
97
|
-
orderedTasks:
|
|
112
|
+
orderedTasks: SortKeyPlanTask[];
|
|
98
113
|
targetListId: string;
|
|
99
114
|
}): {
|
|
100
115
|
previewSortKey: number;
|
|
@@ -119,10 +134,14 @@ function getPreviewSortKeyPlan({
|
|
|
119
134
|
});
|
|
120
135
|
const effectiveOrderedTasks = orderedTasks.map((task) => ({
|
|
121
136
|
...task,
|
|
122
|
-
|
|
137
|
+
effective_sort_key:
|
|
138
|
+
task.id === activeTaskId ? previewSortKey : getEffectiveTaskSortKey(task),
|
|
123
139
|
}));
|
|
124
140
|
const orderNeedsRepair = effectiveOrderedTasks.some((task, index) => {
|
|
125
|
-
if (
|
|
141
|
+
if (
|
|
142
|
+
typeof task.effective_sort_key !== 'number' ||
|
|
143
|
+
!Number.isFinite(task.effective_sort_key)
|
|
144
|
+
) {
|
|
126
145
|
return true;
|
|
127
146
|
}
|
|
128
147
|
|
|
@@ -130,13 +149,16 @@ function getPreviewSortKeyPlan({
|
|
|
130
149
|
if (!previousTask) return false;
|
|
131
150
|
|
|
132
151
|
if (
|
|
133
|
-
typeof previousTask.
|
|
134
|
-
!Number.isFinite(previousTask.
|
|
152
|
+
typeof previousTask.effective_sort_key !== 'number' ||
|
|
153
|
+
!Number.isFinite(previousTask.effective_sort_key)
|
|
135
154
|
) {
|
|
136
155
|
return true;
|
|
137
156
|
}
|
|
138
157
|
|
|
139
|
-
return
|
|
158
|
+
return (
|
|
159
|
+
task.effective_sort_key - previousTask.effective_sort_key <
|
|
160
|
+
SORT_KEY_MIN_GAP
|
|
161
|
+
);
|
|
140
162
|
});
|
|
141
163
|
|
|
142
164
|
if (
|
|
@@ -197,6 +219,9 @@ export function getTaskDropPreviewCacheTasks({
|
|
|
197
219
|
...task,
|
|
198
220
|
list_id: targetListId,
|
|
199
221
|
sort_key: previewSortKey,
|
|
222
|
+
personal_sort_key: task.is_personal_external
|
|
223
|
+
? previewSortKey
|
|
224
|
+
: task.personal_sort_key,
|
|
200
225
|
completed: targetIsCompleted,
|
|
201
226
|
completed_at: targetIsCompleted
|
|
202
227
|
? (task.completed_at ?? mutationTimestamp)
|
|
@@ -210,6 +235,10 @@ export function getTaskDropPreviewCacheTasks({
|
|
|
210
235
|
? ({
|
|
211
236
|
...task,
|
|
212
237
|
sort_key: repairedSortKeysByTaskId.get(task.id) ?? task.sort_key,
|
|
238
|
+
personal_sort_key: task.is_personal_external
|
|
239
|
+
? (repairedSortKeysByTaskId.get(task.id) ??
|
|
240
|
+
task.personal_sort_key)
|
|
241
|
+
: task.personal_sort_key,
|
|
213
242
|
_localMutationAt: localMutationAt,
|
|
214
243
|
} as Task & { _localMutationAt: number })
|
|
215
244
|
: task
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
2
2
|
import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
|
|
3
|
-
import { MAX_SAFE_INTEGER_SORT } from '../kanban-constants';
|
|
4
3
|
import type { DragPreviewPosition, TaskDropPosition } from './task-drag-types';
|
|
4
|
+
import { compareTasksByEffectiveSortKey } from './task-sort-key';
|
|
5
5
|
|
|
6
6
|
export function getNeighborTaskIds(tasks: Task[], taskId: string) {
|
|
7
7
|
const taskIndex = tasks.findIndex((task) => task.id === taskId);
|
|
@@ -142,13 +142,7 @@ export function sortTasksForList({
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
if (!disableSort) {
|
|
145
|
-
|
|
146
|
-
const sortB = b.sort_key ?? MAX_SAFE_INTEGER_SORT;
|
|
147
|
-
if (sortA !== sortB) return sortA - sortB;
|
|
148
|
-
if (!a.created_at || !b.created_at) return 0;
|
|
149
|
-
return (
|
|
150
|
-
new Date(a.created_at).getTime() - new Date(b.created_at).getTime()
|
|
151
|
-
);
|
|
145
|
+
return compareTasksByEffectiveSortKey(a, b);
|
|
152
146
|
}
|
|
153
147
|
|
|
154
148
|
return 0;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
2
|
+
import { MAX_SAFE_INTEGER_SORT } from '../kanban-constants';
|
|
3
|
+
|
|
4
|
+
type SortKeyTask = Pick<
|
|
5
|
+
Task,
|
|
6
|
+
| 'is_personal_external'
|
|
7
|
+
| 'is_personal_external_default'
|
|
8
|
+
| 'personal_sort_key'
|
|
9
|
+
| 'sort_key'
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
type SortableTask = SortKeyTask & Pick<Task, 'created_at'>;
|
|
13
|
+
|
|
14
|
+
export function getEffectiveTaskSortKey(task: SortKeyTask) {
|
|
15
|
+
if (typeof task.sort_key === 'number' && Number.isFinite(task.sort_key)) {
|
|
16
|
+
return task.sort_key;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (
|
|
20
|
+
task.is_personal_external === true &&
|
|
21
|
+
task.is_personal_external_default !== true &&
|
|
22
|
+
typeof task.personal_sort_key === 'number' &&
|
|
23
|
+
Number.isFinite(task.personal_sort_key)
|
|
24
|
+
) {
|
|
25
|
+
return task.personal_sort_key;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function compareTasksByEffectiveSortKey(
|
|
32
|
+
left: SortableTask,
|
|
33
|
+
right: SortableTask
|
|
34
|
+
) {
|
|
35
|
+
const leftSortKey = getEffectiveTaskSortKey(left) ?? MAX_SAFE_INTEGER_SORT;
|
|
36
|
+
const rightSortKey = getEffectiveTaskSortKey(right) ?? MAX_SAFE_INTEGER_SORT;
|
|
37
|
+
|
|
38
|
+
if (leftSortKey !== rightSortKey) {
|
|
39
|
+
return leftSortKey - rightSortKey;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!left.created_at || !right.created_at) return 0;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
new Date(left.created_at).getTime() - new Date(right.created_at).getTime()
|
|
46
|
+
);
|
|
47
|
+
}
|
|
@@ -22,6 +22,7 @@ import {
|
|
|
22
22
|
import { hasDraggableData } from '@tuturuuu/utils/task-helpers';
|
|
23
23
|
import { useCallback, useRef, useState } from 'react';
|
|
24
24
|
import { useBoardBroadcast } from '../../../../shared/board-broadcast-context';
|
|
25
|
+
import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
|
|
25
26
|
import { MAX_SAFE_INTEGER_SORT } from '../kanban-constants';
|
|
26
27
|
import { useAutoScroll } from './auto-scroll';
|
|
27
28
|
import { getColumnReorderUpdates } from './column-reorder';
|
|
@@ -62,6 +63,10 @@ import type {
|
|
|
62
63
|
TaskRect,
|
|
63
64
|
VerticalRect,
|
|
64
65
|
} from './task-drag-types';
|
|
66
|
+
import {
|
|
67
|
+
compareTasksByEffectiveSortKey,
|
|
68
|
+
getEffectiveTaskSortKey,
|
|
69
|
+
} from './task-sort-key';
|
|
65
70
|
|
|
66
71
|
export {
|
|
67
72
|
applyTaskDropPreviewToCache,
|
|
@@ -1162,14 +1167,14 @@ export function useKanbanDnd({
|
|
|
1162
1167
|
const nextTask = projectedDropOrder[1];
|
|
1163
1168
|
newSortKey = await calculateSortKeyWithRetry(
|
|
1164
1169
|
null,
|
|
1165
|
-
nextTask
|
|
1170
|
+
nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
1166
1171
|
targetListId,
|
|
1167
1172
|
projectedDropOrder
|
|
1168
1173
|
);
|
|
1169
1174
|
} else if (newIndex === projectedDropOrder.length - 1) {
|
|
1170
1175
|
const prevTask = projectedDropOrder[projectedDropOrder.length - 2];
|
|
1171
1176
|
newSortKey = await calculateSortKeyWithRetry(
|
|
1172
|
-
prevTask
|
|
1177
|
+
prevTask ? getEffectiveTaskSortKey(prevTask) : null,
|
|
1173
1178
|
null,
|
|
1174
1179
|
targetListId,
|
|
1175
1180
|
projectedDropOrder
|
|
@@ -1178,8 +1183,8 @@ export function useKanbanDnd({
|
|
|
1178
1183
|
const prevTask = projectedDropOrder[newIndex - 1];
|
|
1179
1184
|
const nextTask = projectedDropOrder[newIndex + 1];
|
|
1180
1185
|
newSortKey = await calculateSortKeyWithRetry(
|
|
1181
|
-
prevTask
|
|
1182
|
-
nextTask
|
|
1186
|
+
prevTask ? getEffectiveTaskSortKey(prevTask) : null,
|
|
1187
|
+
nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
1183
1188
|
targetListId,
|
|
1184
1189
|
projectedDropOrder
|
|
1185
1190
|
);
|
|
@@ -1195,7 +1200,8 @@ export function useKanbanDnd({
|
|
|
1195
1200
|
const needsUpdate =
|
|
1196
1201
|
dropChangesVisualOrder ||
|
|
1197
1202
|
(newSortKey !== null &&
|
|
1198
|
-
(activeTaskForDrop
|
|
1203
|
+
(getEffectiveTaskSortKey(activeTaskForDrop) ??
|
|
1204
|
+
MAX_SAFE_INTEGER_SORT) !== newSortKey);
|
|
1199
1205
|
|
|
1200
1206
|
let shouldPreservePendingAfterDragReset = false;
|
|
1201
1207
|
const persistPersonalPlacementMove = (
|
|
@@ -1211,6 +1217,19 @@ export function useKanbanDnd({
|
|
|
1211
1217
|
shouldPreservePendingAfterDragReset = true;
|
|
1212
1218
|
|
|
1213
1219
|
void movePersonalPlacementTask(task, targetListId, sortKey, order)
|
|
1220
|
+
.then((updatedTask) => {
|
|
1221
|
+
broadcast?.('task:upsert', {
|
|
1222
|
+
task: {
|
|
1223
|
+
id: updatedTask.id,
|
|
1224
|
+
list_id: updatedTask.list_id,
|
|
1225
|
+
sort_key: updatedTask.sort_key,
|
|
1226
|
+
personal_sort_key: updatedTask.personal_sort_key,
|
|
1227
|
+
completed_at: updatedTask.completed_at,
|
|
1228
|
+
closed_at: updatedTask.closed_at,
|
|
1229
|
+
},
|
|
1230
|
+
});
|
|
1231
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
1232
|
+
})
|
|
1214
1233
|
.catch((error) => {
|
|
1215
1234
|
console.error('Failed to update personal task placement:', error);
|
|
1216
1235
|
rollbackOptimisticDropPreview();
|
|
@@ -1231,16 +1250,9 @@ export function useKanbanDnd({
|
|
|
1231
1250
|
.map((taskId) => baseTasks.find((t) => t.id === taskId))
|
|
1232
1251
|
.filter((t): t is Task => t !== undefined);
|
|
1233
1252
|
|
|
1234
|
-
const sortedTasksToMove = selectedTaskObjects.sort(
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
if (sortA !== sortB) return sortA - sortB;
|
|
1238
|
-
if (!a.created_at || !b.created_at) return 0;
|
|
1239
|
-
return (
|
|
1240
|
-
new Date(a.created_at).getTime() -
|
|
1241
|
-
new Date(b.created_at).getTime()
|
|
1242
|
-
);
|
|
1243
|
-
});
|
|
1253
|
+
const sortedTasksToMove = selectedTaskObjects.sort(
|
|
1254
|
+
compareTasksByEffectiveSortKey
|
|
1255
|
+
);
|
|
1244
1256
|
|
|
1245
1257
|
if (
|
|
1246
1258
|
targetIsExternalStaging &&
|
|
@@ -1306,7 +1318,7 @@ export function useKanbanDnd({
|
|
|
1306
1318
|
const nextTask = simulatedTargetList[1];
|
|
1307
1319
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1308
1320
|
null,
|
|
1309
|
-
nextTask
|
|
1321
|
+
nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
1310
1322
|
targetListId,
|
|
1311
1323
|
targetListTasks
|
|
1312
1324
|
);
|
|
@@ -1316,7 +1328,7 @@ export function useKanbanDnd({
|
|
|
1316
1328
|
) {
|
|
1317
1329
|
const prevTask = simulatedTargetList[positionInSimulated - 1];
|
|
1318
1330
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1319
|
-
prevTask
|
|
1331
|
+
prevTask ? getEffectiveTaskSortKey(prevTask) : null,
|
|
1320
1332
|
null,
|
|
1321
1333
|
targetListId,
|
|
1322
1334
|
targetListTasks
|
|
@@ -1334,8 +1346,8 @@ export function useKanbanDnd({
|
|
|
1334
1346
|
|
|
1335
1347
|
if (!prevIsMoving && !nextIsMoving) {
|
|
1336
1348
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1337
|
-
prevTask
|
|
1338
|
-
nextTask
|
|
1349
|
+
prevTask ? getEffectiveTaskSortKey(prevTask) : null,
|
|
1350
|
+
nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
1339
1351
|
targetListId,
|
|
1340
1352
|
targetListTasks
|
|
1341
1353
|
);
|
|
@@ -1349,8 +1361,10 @@ export function useKanbanDnd({
|
|
|
1349
1361
|
}
|
|
1350
1362
|
}
|
|
1351
1363
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1352
|
-
stationaryPrev
|
|
1353
|
-
|
|
1364
|
+
stationaryPrev
|
|
1365
|
+
? getEffectiveTaskSortKey(stationaryPrev)
|
|
1366
|
+
: null,
|
|
1367
|
+
nextTask ? getEffectiveTaskSortKey(nextTask) : null,
|
|
1354
1368
|
targetListId,
|
|
1355
1369
|
targetListTasks
|
|
1356
1370
|
);
|
|
@@ -1368,8 +1382,10 @@ export function useKanbanDnd({
|
|
|
1368
1382
|
}
|
|
1369
1383
|
}
|
|
1370
1384
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1371
|
-
prevTask
|
|
1372
|
-
stationaryNext
|
|
1385
|
+
prevTask ? getEffectiveTaskSortKey(prevTask) : null,
|
|
1386
|
+
stationaryNext
|
|
1387
|
+
? getEffectiveTaskSortKey(stationaryNext)
|
|
1388
|
+
: null,
|
|
1373
1389
|
targetListId,
|
|
1374
1390
|
targetListTasks
|
|
1375
1391
|
);
|
|
@@ -1398,8 +1414,8 @@ export function useKanbanDnd({
|
|
|
1398
1414
|
}
|
|
1399
1415
|
|
|
1400
1416
|
batchSortKey = await calculateSortKeyWithRetry(
|
|
1401
|
-
boundaryPrev
|
|
1402
|
-
boundaryNext
|
|
1417
|
+
boundaryPrev ? getEffectiveTaskSortKey(boundaryPrev) : null,
|
|
1418
|
+
boundaryNext ? getEffectiveTaskSortKey(boundaryNext) : null,
|
|
1403
1419
|
targetListId,
|
|
1404
1420
|
targetListTasks
|
|
1405
1421
|
);
|
|
@@ -1430,6 +1446,7 @@ export function useKanbanDnd({
|
|
|
1430
1446
|
closed_at: updatedTask.closed_at,
|
|
1431
1447
|
},
|
|
1432
1448
|
});
|
|
1449
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
1433
1450
|
},
|
|
1434
1451
|
onSettled: () => {
|
|
1435
1452
|
clearPendingTaskIds(pendingTaskIds);
|
|
@@ -1475,14 +1492,45 @@ export function useKanbanDnd({
|
|
|
1475
1492
|
closed_at: updatedTask.closed_at,
|
|
1476
1493
|
},
|
|
1477
1494
|
});
|
|
1495
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
1478
1496
|
};
|
|
1479
1497
|
|
|
1480
1498
|
if (repairedTaskSortKeys.length > 0) {
|
|
1481
1499
|
void (async () => {
|
|
1482
1500
|
try {
|
|
1501
|
+
const repairTaskById = new Map<string, Task>();
|
|
1502
|
+
for (const task of [
|
|
1503
|
+
...baseTasks,
|
|
1504
|
+
...(optimisticDropPreview?.previousTasks ?? []),
|
|
1505
|
+
...(optimisticDropPreview?.previousFullTasks ?? []),
|
|
1506
|
+
]) {
|
|
1507
|
+
repairTaskById.set(task.id, task);
|
|
1508
|
+
}
|
|
1509
|
+
|
|
1483
1510
|
const results = await Promise.allSettled(
|
|
1484
|
-
repairedTaskSortKeys.map((repair) =>
|
|
1485
|
-
|
|
1511
|
+
repairedTaskSortKeys.map(async (repair) => {
|
|
1512
|
+
const repairTask = repairTaskById.get(repair.taskId);
|
|
1513
|
+
|
|
1514
|
+
if (repairTask && usesPersonalPlacement(repairTask)) {
|
|
1515
|
+
const updatedTask = await movePersonalPlacementTask(
|
|
1516
|
+
repairTask,
|
|
1517
|
+
repair.listId,
|
|
1518
|
+
repair.sortKey
|
|
1519
|
+
);
|
|
1520
|
+
broadcast?.('task:upsert', {
|
|
1521
|
+
task: {
|
|
1522
|
+
id: updatedTask.id,
|
|
1523
|
+
list_id: updatedTask.list_id,
|
|
1524
|
+
sort_key: updatedTask.sort_key,
|
|
1525
|
+
personal_sort_key: updatedTask.personal_sort_key,
|
|
1526
|
+
completed_at: updatedTask.completed_at,
|
|
1527
|
+
closed_at: updatedTask.closed_at,
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
return updatedTask;
|
|
1531
|
+
}
|
|
1532
|
+
|
|
1533
|
+
return reorderTaskMutation.mutateAsync(
|
|
1486
1534
|
{
|
|
1487
1535
|
taskId: repair.taskId,
|
|
1488
1536
|
newListId: repair.listId,
|
|
@@ -1495,14 +1543,17 @@ export function useKanbanDnd({
|
|
|
1495
1543
|
{
|
|
1496
1544
|
onSuccess: handleReorderSuccess,
|
|
1497
1545
|
}
|
|
1498
|
-
)
|
|
1499
|
-
)
|
|
1546
|
+
);
|
|
1547
|
+
})
|
|
1500
1548
|
);
|
|
1501
1549
|
const failedResults = results.filter(
|
|
1502
1550
|
(result) => result.status === 'rejected'
|
|
1503
1551
|
);
|
|
1504
1552
|
|
|
1505
|
-
if (failedResults.length === 0)
|
|
1553
|
+
if (failedResults.length === 0) {
|
|
1554
|
+
void invalidateKanbanDeadlineTasks(queryClient, boardId);
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1506
1557
|
|
|
1507
1558
|
console.error(
|
|
1508
1559
|
'Failed to persist repaired task sort keys:',
|