@tuturuuu/ui 0.8.0 → 0.10.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 +69 -0
- package/biome.json +1 -1
- package/package.json +74 -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-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__/settings-dialog-shell.test.tsx +3 -0
- package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +46 -1
- 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/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 +65 -28
- package/src/components/ui/custom/theme-toggle.tsx +1 -1
- package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
- package/src/components/ui/custom/workspace-select.tsx +25 -19
- 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 +286 -0
- package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -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 +15 -226
- 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 +121 -39
- 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-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/bulk/bulk-operation-types.ts +3 -3
- 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/data/use-bulk-resources.ts +59 -5
- 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/drag-preview.tsx +20 -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 +642 -5
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +224 -15
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +535 -53
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +101 -33
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +235 -113
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +50 -5
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +12 -2
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +10 -1
- 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-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 +271 -36
- 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 +22 -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/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/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/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/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 +141 -1
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +377 -36
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +374 -0
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +419 -5
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +38 -0
- package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -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 +15 -10
- package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
- package/src/components/ui/tu-do/shared/board-header.tsx +471 -975
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
- package/src/components/ui/tu-do/shared/board-switcher.tsx +244 -220
- package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
- package/src/components/ui/tu-do/shared/board-views.tsx +577 -85
- package/src/components/ui/tu-do/shared/list-view.tsx +246 -2
- 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-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-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/field-diff-viewer.tsx +3 -2
- 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/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-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/task-snapshot-dialog.tsx +8 -3
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +11 -1
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
- 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/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/__tests__/useBoardPresence.test.tsx +191 -0
- package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
- 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/useBoardPresence.ts +364 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +45 -90
- package/src/lib/workspace-actions.ts +2 -6
|
@@ -10,6 +10,7 @@ import { useTranslations } from 'next-intl';
|
|
|
10
10
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
|
11
11
|
import type { DragPreviewPosition } from './kanban/dnd/use-kanban-dnd';
|
|
12
12
|
import { MeasuredTaskCard } from './task';
|
|
13
|
+
import type { TaskCardAssigneeMemberSource } from './task-card/task-card';
|
|
13
14
|
|
|
14
15
|
const VIRTUALIZE_THRESHOLD = 60; // only virtualize for fairly large lists
|
|
15
16
|
const ESTIMATED_ITEM_HEIGHT = 96; // px including margin (space-y-2 gap)
|
|
@@ -33,6 +34,8 @@ interface VirtualizedTaskListProps {
|
|
|
33
34
|
isMultiSelectMode?: boolean;
|
|
34
35
|
selectedTasks?: Set<string>;
|
|
35
36
|
isPersonalWorkspace?: boolean;
|
|
37
|
+
canUseBoardAssignees?: boolean;
|
|
38
|
+
assigneeMemberSource?: TaskCardAssigneeMemberSource;
|
|
36
39
|
onTaskSelect?: (taskId: string, event: React.MouseEvent) => void;
|
|
37
40
|
onClearSelection?: () => void;
|
|
38
41
|
dragPreviewPosition?: DragPreviewPosition | null;
|
|
@@ -43,6 +46,7 @@ interface VirtualizedTaskListProps {
|
|
|
43
46
|
onLoadMore?: () => void;
|
|
44
47
|
hasMore?: boolean;
|
|
45
48
|
isLoadingMore?: boolean;
|
|
49
|
+
readOnly?: boolean;
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
interface TaskListContentProps {
|
|
@@ -55,6 +59,8 @@ interface TaskListContentProps {
|
|
|
55
59
|
isMultiSelectMode?: boolean;
|
|
56
60
|
selectedTasks?: Set<string>;
|
|
57
61
|
isPersonalWorkspace?: boolean;
|
|
62
|
+
canUseBoardAssignees?: boolean;
|
|
63
|
+
assigneeMemberSource?: TaskCardAssigneeMemberSource;
|
|
58
64
|
onTaskSelect?: (taskId: string, event: React.MouseEvent) => void;
|
|
59
65
|
onClearSelection?: () => void;
|
|
60
66
|
dragPreviewPosition?: DragPreviewPosition | null;
|
|
@@ -64,6 +70,7 @@ interface TaskListContentProps {
|
|
|
64
70
|
bulkUpdateCustomDueDate?: (date: Date | null) => Promise<void>;
|
|
65
71
|
startIndex?: number;
|
|
66
72
|
taskOrder?: Pick<Task, 'id'>[];
|
|
73
|
+
readOnly?: boolean;
|
|
67
74
|
}
|
|
68
75
|
|
|
69
76
|
export function getTaskDragPreviewSlotIndex({
|
|
@@ -123,6 +130,8 @@ function TaskListContent({
|
|
|
123
130
|
isMultiSelectMode,
|
|
124
131
|
selectedTasks,
|
|
125
132
|
isPersonalWorkspace,
|
|
133
|
+
canUseBoardAssignees,
|
|
134
|
+
assigneeMemberSource,
|
|
126
135
|
onTaskSelect,
|
|
127
136
|
onClearSelection,
|
|
128
137
|
dragPreviewPosition,
|
|
@@ -132,6 +141,7 @@ function TaskListContent({
|
|
|
132
141
|
bulkUpdateCustomDueDate,
|
|
133
142
|
startIndex = 0,
|
|
134
143
|
taskOrder = tasks,
|
|
144
|
+
readOnly = false,
|
|
135
145
|
}: TaskListContentProps) {
|
|
136
146
|
const slotIndex = getTaskDragPreviewSlotIndex({
|
|
137
147
|
columnId: column.id,
|
|
@@ -162,6 +172,8 @@ function TaskListContent({
|
|
|
162
172
|
)}
|
|
163
173
|
isMultiSelectMode={isMultiSelectMode}
|
|
164
174
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
175
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
176
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
165
177
|
onSelect={onTaskSelect}
|
|
166
178
|
onClearSelection={onClearSelection}
|
|
167
179
|
suppressSortableTransform={suppressSortableTransform}
|
|
@@ -170,6 +182,7 @@ function TaskListContent({
|
|
|
170
182
|
optimisticUpdateInProgress={optimisticUpdateInProgress}
|
|
171
183
|
selectedTasks={selectedTasks}
|
|
172
184
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
185
|
+
readOnly={readOnly}
|
|
173
186
|
/>
|
|
174
187
|
{slotIndex === globalIndex + 1 && (
|
|
175
188
|
<DragPreviewSlot
|
|
@@ -228,6 +241,8 @@ function VirtualizedTaskListInner({
|
|
|
228
241
|
isMultiSelectMode,
|
|
229
242
|
selectedTasks,
|
|
230
243
|
isPersonalWorkspace,
|
|
244
|
+
canUseBoardAssignees,
|
|
245
|
+
assigneeMemberSource,
|
|
231
246
|
onTaskSelect,
|
|
232
247
|
onClearSelection,
|
|
233
248
|
dragPreviewPosition,
|
|
@@ -238,6 +253,7 @@ function VirtualizedTaskListInner({
|
|
|
238
253
|
onLoadMore,
|
|
239
254
|
hasMore,
|
|
240
255
|
isLoadingMore,
|
|
256
|
+
readOnly = false,
|
|
241
257
|
}: VirtualizedTaskListProps) {
|
|
242
258
|
const t = useTranslations('common');
|
|
243
259
|
const tTasks = useTranslations('ws-tasks');
|
|
@@ -465,6 +481,8 @@ function VirtualizedTaskListInner({
|
|
|
465
481
|
isMultiSelectMode={isMultiSelectMode}
|
|
466
482
|
selectedTasks={selectedTasks}
|
|
467
483
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
484
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
485
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
468
486
|
onTaskSelect={onTaskSelect}
|
|
469
487
|
onClearSelection={onClearSelection}
|
|
470
488
|
dragPreviewPosition={dragPreviewPosition}
|
|
@@ -474,6 +492,7 @@ function VirtualizedTaskListInner({
|
|
|
474
492
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
475
493
|
startIndex={startIndex}
|
|
476
494
|
taskOrder={tasks}
|
|
495
|
+
readOnly={readOnly}
|
|
477
496
|
/>
|
|
478
497
|
</div>
|
|
479
498
|
</div>
|
|
@@ -494,6 +513,8 @@ function VirtualizedTaskListInner({
|
|
|
494
513
|
isMultiSelectMode={isMultiSelectMode}
|
|
495
514
|
selectedTasks={selectedTasks}
|
|
496
515
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
516
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
517
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
497
518
|
onTaskSelect={onTaskSelect}
|
|
498
519
|
onClearSelection={onClearSelection}
|
|
499
520
|
dragPreviewPosition={dragPreviewPosition}
|
|
@@ -502,6 +523,7 @@ function VirtualizedTaskListInner({
|
|
|
502
523
|
optimisticUpdateInProgress={optimisticUpdateInProgress}
|
|
503
524
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
504
525
|
taskOrder={tasks}
|
|
526
|
+
readOnly={readOnly}
|
|
505
527
|
/>
|
|
506
528
|
{loadMoreSentinel}
|
|
507
529
|
</SortableContext>
|
|
@@ -28,6 +28,9 @@ interface TimelineGridProps {
|
|
|
28
28
|
localTasks: Task[];
|
|
29
29
|
boardId?: string;
|
|
30
30
|
wsId?: string;
|
|
31
|
+
isPersonalWorkspace?: boolean;
|
|
32
|
+
canUseBoardAssignees?: boolean;
|
|
33
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
31
34
|
dayWidth: number;
|
|
32
35
|
sidebarWidth: number;
|
|
33
36
|
timelineWidth: number;
|
|
@@ -71,6 +74,9 @@ export function TimelineGrid({
|
|
|
71
74
|
localTasks,
|
|
72
75
|
boardId,
|
|
73
76
|
wsId,
|
|
77
|
+
isPersonalWorkspace,
|
|
78
|
+
canUseBoardAssignees,
|
|
79
|
+
assigneeMemberSource,
|
|
74
80
|
dayWidth,
|
|
75
81
|
sidebarWidth,
|
|
76
82
|
timelineWidth,
|
|
@@ -223,6 +229,9 @@ export function TimelineGrid({
|
|
|
223
229
|
)}
|
|
224
230
|
boardId={boardId}
|
|
225
231
|
wsId={wsId}
|
|
232
|
+
isPersonalWorkspace={isPersonalWorkspace}
|
|
233
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
234
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
226
235
|
dayWidth={dayWidth}
|
|
227
236
|
timelineWidth={timelineWidth}
|
|
228
237
|
sidebarWidth={sidebarWidth}
|
|
@@ -39,6 +39,9 @@ interface TimelineTaskRowProps {
|
|
|
39
39
|
lists: TaskList[];
|
|
40
40
|
boardId?: string;
|
|
41
41
|
wsId?: string;
|
|
42
|
+
isPersonalWorkspace?: boolean;
|
|
43
|
+
canUseBoardAssignees?: boolean;
|
|
44
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
42
45
|
dayWidth: number;
|
|
43
46
|
timelineWidth: number;
|
|
44
47
|
sidebarWidth: number;
|
|
@@ -75,6 +78,9 @@ export function TimelineTaskRow({
|
|
|
75
78
|
lists,
|
|
76
79
|
boardId,
|
|
77
80
|
wsId,
|
|
81
|
+
isPersonalWorkspace,
|
|
82
|
+
canUseBoardAssignees,
|
|
83
|
+
assigneeMemberSource,
|
|
78
84
|
dayWidth,
|
|
79
85
|
timelineWidth,
|
|
80
86
|
sidebarWidth,
|
|
@@ -235,6 +241,9 @@ export function TimelineTaskRow({
|
|
|
235
241
|
boardId={boardId}
|
|
236
242
|
workspaceId={wsId}
|
|
237
243
|
lists={lists}
|
|
244
|
+
isPersonalWorkspace={isPersonalWorkspace}
|
|
245
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
246
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
238
247
|
onUpdate={onActionsUpdate ?? (() => undefined)}
|
|
239
248
|
open={actionsMenu.open}
|
|
240
249
|
onOpenChange={(open) =>
|
|
@@ -34,6 +34,9 @@ interface TimelineToolbarProps {
|
|
|
34
34
|
lists: TaskList[];
|
|
35
35
|
boardId?: string;
|
|
36
36
|
wsId?: string;
|
|
37
|
+
isPersonalWorkspace?: boolean;
|
|
38
|
+
canUseBoardAssignees?: boolean;
|
|
39
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
37
40
|
primaryCreateListId: string | null;
|
|
38
41
|
dayWidth: number;
|
|
39
42
|
setDayWidth: (value: number) => void;
|
|
@@ -66,6 +69,9 @@ export function TimelineToolbar({
|
|
|
66
69
|
lists,
|
|
67
70
|
boardId,
|
|
68
71
|
wsId,
|
|
72
|
+
isPersonalWorkspace,
|
|
73
|
+
canUseBoardAssignees,
|
|
74
|
+
assigneeMemberSource,
|
|
69
75
|
primaryCreateListId,
|
|
70
76
|
dayWidth,
|
|
71
77
|
setDayWidth,
|
|
@@ -221,6 +227,9 @@ export function TimelineToolbar({
|
|
|
221
227
|
boardId={boardId}
|
|
222
228
|
workspaceId={wsId}
|
|
223
229
|
lists={lists}
|
|
230
|
+
isPersonalWorkspace={isPersonalWorkspace}
|
|
231
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
232
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
224
233
|
onUpdate={onActionsUpdate ?? (() => undefined)}
|
|
225
234
|
open={openTaskMenu?.taskId === task.id}
|
|
226
235
|
onOpenChange={(open) =>
|
|
@@ -65,6 +65,9 @@ export interface TimelineProps {
|
|
|
65
65
|
lists: TaskList[];
|
|
66
66
|
boardId?: string;
|
|
67
67
|
wsId?: string;
|
|
68
|
+
isPersonalWorkspace?: boolean;
|
|
69
|
+
canUseBoardAssignees?: boolean;
|
|
70
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
68
71
|
className?: string;
|
|
69
72
|
onTaskPartialUpdate?: (taskId: string, updates: Partial<Task>) => void;
|
|
70
73
|
}
|
|
@@ -110,6 +113,9 @@ export function TimelineBoard({
|
|
|
110
113
|
lists,
|
|
111
114
|
boardId,
|
|
112
115
|
wsId,
|
|
116
|
+
isPersonalWorkspace = false,
|
|
117
|
+
canUseBoardAssignees,
|
|
118
|
+
assigneeMemberSource,
|
|
113
119
|
className,
|
|
114
120
|
onTaskPartialUpdate,
|
|
115
121
|
}: TimelineProps) {
|
|
@@ -227,6 +233,9 @@ export function TimelineBoard({
|
|
|
227
233
|
(task: Task) => task.source_workspace_id ?? wsId ?? null,
|
|
228
234
|
[wsId]
|
|
229
235
|
);
|
|
236
|
+
const showAssignees = canUseBoardAssignees ?? !isPersonalWorkspace;
|
|
237
|
+
const effectiveAssigneeMemberSource =
|
|
238
|
+
assigneeMemberSource ?? (isPersonalWorkspace ? 'board' : 'workspace');
|
|
230
239
|
|
|
231
240
|
const openTimelineTask = useCallback(
|
|
232
241
|
(task: Task) => {
|
|
@@ -240,7 +249,13 @@ export function TimelineBoard({
|
|
|
240
249
|
boardId,
|
|
241
250
|
availableLists: lists,
|
|
242
251
|
effectiveWorkspaceId: wsId,
|
|
243
|
-
isPersonalWorkspace
|
|
252
|
+
isPersonalWorkspace,
|
|
253
|
+
canUseBoardAssignees: task.source_workspace_id
|
|
254
|
+
? true
|
|
255
|
+
: showAssignees,
|
|
256
|
+
assigneeMemberSource: task.source_workspace_id
|
|
257
|
+
? 'workspace'
|
|
258
|
+
: effectiveAssigneeMemberSource,
|
|
244
259
|
})
|
|
245
260
|
);
|
|
246
261
|
return;
|
|
@@ -248,10 +263,21 @@ export function TimelineBoard({
|
|
|
248
263
|
|
|
249
264
|
openTask(task, boardId, lists, false, {
|
|
250
265
|
taskWsId: wsId,
|
|
251
|
-
taskWorkspacePersonal:
|
|
266
|
+
taskWorkspacePersonal: isPersonalWorkspace,
|
|
267
|
+
canUseBoardAssignees: showAssignees,
|
|
268
|
+
assigneeMemberSource: effectiveAssigneeMemberSource,
|
|
252
269
|
});
|
|
253
270
|
},
|
|
254
|
-
[
|
|
271
|
+
[
|
|
272
|
+
boardId,
|
|
273
|
+
effectiveAssigneeMemberSource,
|
|
274
|
+
isPersonalWorkspace,
|
|
275
|
+
lists,
|
|
276
|
+
openTask,
|
|
277
|
+
openTaskById,
|
|
278
|
+
showAssignees,
|
|
279
|
+
wsId,
|
|
280
|
+
]
|
|
255
281
|
);
|
|
256
282
|
|
|
257
283
|
const clearDraft = useCallback((taskId: string) => {
|
|
@@ -674,6 +700,9 @@ export function TimelineBoard({
|
|
|
674
700
|
<TimelineToolbar
|
|
675
701
|
boardId={boardId}
|
|
676
702
|
wsId={wsId}
|
|
703
|
+
isPersonalWorkspace={isPersonalWorkspace}
|
|
704
|
+
canUseBoardAssignees={showAssignees}
|
|
705
|
+
assigneeMemberSource={effectiveAssigneeMemberSource}
|
|
677
706
|
dayWidth={dayWidth}
|
|
678
707
|
density={density}
|
|
679
708
|
formatLongDate={formatLongDate}
|
|
@@ -703,6 +732,9 @@ export function TimelineBoard({
|
|
|
703
732
|
barHeight={densityConfig.barHeight}
|
|
704
733
|
boardId={boardId}
|
|
705
734
|
wsId={wsId}
|
|
735
|
+
isPersonalWorkspace={isPersonalWorkspace}
|
|
736
|
+
canUseBoardAssignees={showAssignees}
|
|
737
|
+
assigneeMemberSource={effectiveAssigneeMemberSource}
|
|
706
738
|
dayWidth={dayWidth}
|
|
707
739
|
draggedUnscheduledTaskId={draggedUnscheduledTaskId}
|
|
708
740
|
dropPreview={dropPreview}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { ChevronDown, Info } from '@tuturuuu/icons';
|
|
4
|
+
import {
|
|
5
|
+
Collapsible,
|
|
6
|
+
CollapsibleContent,
|
|
7
|
+
CollapsibleTrigger,
|
|
8
|
+
} from '@tuturuuu/ui/collapsible';
|
|
9
|
+
import {
|
|
10
|
+
Tooltip,
|
|
11
|
+
TooltipContent,
|
|
12
|
+
TooltipProvider,
|
|
13
|
+
TooltipTrigger,
|
|
14
|
+
} from '@tuturuuu/ui/tooltip';
|
|
15
|
+
import { cn } from '@tuturuuu/utils/format';
|
|
16
|
+
import { useTranslations } from 'next-intl';
|
|
17
|
+
import type { ReactNode } from 'react';
|
|
18
|
+
|
|
19
|
+
function ShareInfoTooltip({
|
|
20
|
+
content,
|
|
21
|
+
label,
|
|
22
|
+
}: {
|
|
23
|
+
content: string;
|
|
24
|
+
label: string;
|
|
25
|
+
}) {
|
|
26
|
+
return (
|
|
27
|
+
<TooltipProvider delayDuration={0} skipDelayDuration={0}>
|
|
28
|
+
<Tooltip>
|
|
29
|
+
<TooltipTrigger asChild>
|
|
30
|
+
<button
|
|
31
|
+
type="button"
|
|
32
|
+
className="text-muted-foreground transition-colors hover:text-foreground"
|
|
33
|
+
aria-label={label}
|
|
34
|
+
>
|
|
35
|
+
<Info className="h-3.5 w-3.5" />
|
|
36
|
+
</button>
|
|
37
|
+
</TooltipTrigger>
|
|
38
|
+
<TooltipContent className="max-w-xs">{content}</TooltipContent>
|
|
39
|
+
</Tooltip>
|
|
40
|
+
</TooltipProvider>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface ShareSectionProps {
|
|
45
|
+
children: ReactNode;
|
|
46
|
+
icon: ReactNode;
|
|
47
|
+
onOpenChange: (open: boolean) => void;
|
|
48
|
+
open: boolean;
|
|
49
|
+
statusBadge: ReactNode;
|
|
50
|
+
title: string;
|
|
51
|
+
tooltip: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function ShareSection({
|
|
55
|
+
children,
|
|
56
|
+
icon,
|
|
57
|
+
onOpenChange,
|
|
58
|
+
open,
|
|
59
|
+
statusBadge,
|
|
60
|
+
title,
|
|
61
|
+
tooltip,
|
|
62
|
+
}: ShareSectionProps) {
|
|
63
|
+
const t = useTranslations();
|
|
64
|
+
|
|
65
|
+
return (
|
|
66
|
+
<Collapsible
|
|
67
|
+
open={open}
|
|
68
|
+
onOpenChange={onOpenChange}
|
|
69
|
+
className="rounded-md border"
|
|
70
|
+
>
|
|
71
|
+
<div className="flex min-h-11 items-center gap-2 px-3">
|
|
72
|
+
<CollapsibleTrigger asChild>
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
className="flex min-w-0 flex-1 items-center gap-2 text-left transition-colors hover:text-foreground"
|
|
76
|
+
>
|
|
77
|
+
{icon}
|
|
78
|
+
<span className="min-w-0 flex-1 truncate font-medium text-sm">
|
|
79
|
+
{title}
|
|
80
|
+
</span>
|
|
81
|
+
{statusBadge}
|
|
82
|
+
<ChevronDown
|
|
83
|
+
className={cn(
|
|
84
|
+
'h-4 w-4 shrink-0 text-muted-foreground transition-transform',
|
|
85
|
+
open && 'rotate-180'
|
|
86
|
+
)}
|
|
87
|
+
/>
|
|
88
|
+
</button>
|
|
89
|
+
</CollapsibleTrigger>
|
|
90
|
+
<ShareInfoTooltip
|
|
91
|
+
label={t('ws-task-boards.share.note')}
|
|
92
|
+
content={tooltip}
|
|
93
|
+
/>
|
|
94
|
+
</div>
|
|
95
|
+
<CollapsibleContent className="border-t p-3">
|
|
96
|
+
{children}
|
|
97
|
+
</CollapsibleContent>
|
|
98
|
+
</Collapsible>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useQuery } from '@tanstack/react-query';
|
|
4
4
|
import {
|
|
5
|
+
convertWorkspaceTaskDraft,
|
|
5
6
|
listWorkspaceTaskBoards,
|
|
6
7
|
listWorkspaceTaskLists,
|
|
7
8
|
} from '@tuturuuu/internal-api';
|
|
@@ -46,6 +47,10 @@ export function DraftConvertDialog({
|
|
|
46
47
|
const [selectedBoardId, setSelectedBoardId] = useState<string>('');
|
|
47
48
|
const [selectedListId, setSelectedListId] = useState<string>('');
|
|
48
49
|
const [isConverting, setIsConverting] = useState(false);
|
|
50
|
+
const internalApiOptions =
|
|
51
|
+
typeof window !== 'undefined'
|
|
52
|
+
? { baseUrl: window.location.origin }
|
|
53
|
+
: undefined;
|
|
49
54
|
|
|
50
55
|
// Sync selections when draft changes (useState initializer only runs on mount)
|
|
51
56
|
useEffect(() => {
|
|
@@ -107,20 +112,13 @@ export function DraftConvertDialog({
|
|
|
107
112
|
|
|
108
113
|
setIsConverting(true);
|
|
109
114
|
try {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
body: JSON.stringify({ listId: selectedListId }),
|
|
116
|
-
}
|
|
115
|
+
await convertWorkspaceTaskDraft(
|
|
116
|
+
wsId,
|
|
117
|
+
draft.id,
|
|
118
|
+
{ listId: selectedListId },
|
|
119
|
+
internalApiOptions
|
|
117
120
|
);
|
|
118
121
|
|
|
119
|
-
if (!res.ok) {
|
|
120
|
-
const data = await res.json();
|
|
121
|
-
throw new Error(data.error || 'Failed to convert draft');
|
|
122
|
-
}
|
|
123
|
-
|
|
124
122
|
toast.success(t('converted_success'));
|
|
125
123
|
onConverted();
|
|
126
124
|
onClose();
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
|
4
4
|
import { FileText } from '@tuturuuu/icons';
|
|
5
|
+
import {
|
|
6
|
+
deleteWorkspaceTaskDraft,
|
|
7
|
+
listWorkspaceTaskDrafts,
|
|
8
|
+
} from '@tuturuuu/internal-api/tasks';
|
|
5
9
|
import { toast } from '@tuturuuu/ui/sonner';
|
|
6
10
|
import { useTranslations } from 'next-intl';
|
|
7
11
|
import { useState } from 'react';
|
|
@@ -11,38 +15,51 @@ import { DraftConvertDialog } from './draft-convert-dialog';
|
|
|
11
15
|
|
|
12
16
|
interface DraftsPageProps {
|
|
13
17
|
wsId: string;
|
|
18
|
+
boardId?: string;
|
|
19
|
+
includeUnassignedForBoard?: boolean;
|
|
14
20
|
}
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
function getBrowserInternalApiOptions() {
|
|
23
|
+
return typeof window !== 'undefined'
|
|
24
|
+
? { baseUrl: window.location.origin }
|
|
25
|
+
: undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function DraftsPage({
|
|
29
|
+
wsId,
|
|
30
|
+
boardId,
|
|
31
|
+
includeUnassignedForBoard = false,
|
|
32
|
+
}: DraftsPageProps) {
|
|
17
33
|
const t = useTranslations('task-drafts');
|
|
18
34
|
const queryClient = useQueryClient();
|
|
19
35
|
const { editDraft } = useTaskDialogContext();
|
|
20
36
|
const [convertDraft, setConvertDraft] = useState<TaskDraft | null>(null);
|
|
37
|
+
const draftQueryKey = [
|
|
38
|
+
'task-drafts',
|
|
39
|
+
wsId,
|
|
40
|
+
boardId ?? 'all',
|
|
41
|
+
includeUnassignedForBoard,
|
|
42
|
+
] as const;
|
|
21
43
|
|
|
22
44
|
const { data: drafts = [], isLoading } = useQuery<TaskDraft[]>({
|
|
23
|
-
queryKey:
|
|
24
|
-
queryFn:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
45
|
+
queryKey: draftQueryKey,
|
|
46
|
+
queryFn: () =>
|
|
47
|
+
listWorkspaceTaskDrafts(
|
|
48
|
+
wsId,
|
|
49
|
+
{ boardId, includeUnassignedForBoard },
|
|
50
|
+
getBrowserInternalApiOptions()
|
|
51
|
+
) as Promise<TaskDraft[]>,
|
|
30
52
|
});
|
|
31
53
|
|
|
32
54
|
const deleteMutation = useMutation({
|
|
33
|
-
mutationFn: async (draftId: string) =>
|
|
34
|
-
|
|
35
|
-
`/api/v1/workspaces/${wsId}/task-drafts/${draftId}`,
|
|
36
|
-
{ method: 'DELETE' }
|
|
37
|
-
);
|
|
38
|
-
if (!res.ok) throw new Error('Failed to delete draft');
|
|
39
|
-
},
|
|
55
|
+
mutationFn: async (draftId: string) =>
|
|
56
|
+
deleteWorkspaceTaskDraft(wsId, draftId, getBrowserInternalApiOptions()),
|
|
40
57
|
onSuccess: () => {
|
|
41
58
|
queryClient.invalidateQueries({ queryKey: ['task-drafts', wsId] });
|
|
42
59
|
toast.success(t('deleted_success'));
|
|
43
60
|
},
|
|
44
61
|
onError: () => {
|
|
45
|
-
toast.error('
|
|
62
|
+
toast.error(t('delete_failed'));
|
|
46
63
|
},
|
|
47
64
|
});
|
|
48
65
|
|
|
@@ -190,6 +190,46 @@ describe('useTaskLabelManagement', () => {
|
|
|
190
190
|
);
|
|
191
191
|
});
|
|
192
192
|
|
|
193
|
+
it('updates full-board and task detail caches without invalidating visible board queries', async () => {
|
|
194
|
+
queryClient.setQueryData(['tasks', 'board-1'], [mockTask]);
|
|
195
|
+
queryClient.setQueryData(['tasks-full', 'board-1', 'all'], [mockTask]);
|
|
196
|
+
queryClient.setQueryData(['task', 'task-1'], mockTask);
|
|
197
|
+
const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');
|
|
198
|
+
|
|
199
|
+
const { result } = renderHook(
|
|
200
|
+
() =>
|
|
201
|
+
useTaskLabelManagement({
|
|
202
|
+
task: mockTask,
|
|
203
|
+
boardId: 'board-1',
|
|
204
|
+
workspaceLabels: mockWorkspaceLabels,
|
|
205
|
+
workspaceId: 'ws-1',
|
|
206
|
+
taskId: 'task-1',
|
|
207
|
+
}),
|
|
208
|
+
{ wrapper }
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
await act(async () => {
|
|
212
|
+
await result.current.toggleTaskLabel('label-3');
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
expect(
|
|
216
|
+
queryClient
|
|
217
|
+
.getQueryData<Task[]>(['tasks-full', 'board-1', 'all'])?.[0]
|
|
218
|
+
?.labels?.some((label) => label.id === 'label-3')
|
|
219
|
+
).toBe(true);
|
|
220
|
+
expect(
|
|
221
|
+
queryClient
|
|
222
|
+
.getQueryData<Task>(['task', 'task-1'])
|
|
223
|
+
?.labels?.some((label) => label.id === 'label-3')
|
|
224
|
+
).toBe(true);
|
|
225
|
+
expect(invalidateSpy).not.toHaveBeenCalledWith({
|
|
226
|
+
queryKey: ['tasks', 'board-1'],
|
|
227
|
+
});
|
|
228
|
+
expect(invalidateSpy).not.toHaveBeenCalledWith({
|
|
229
|
+
queryKey: ['tasks-full', 'board-1'],
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
193
233
|
it('should rollback on error and show toast', async () => {
|
|
194
234
|
mockRemoveWorkspaceTaskLabel.mockRejectedValueOnce(
|
|
195
235
|
new Error('Database error')
|
|
@@ -197,6 +237,8 @@ describe('useTaskLabelManagement', () => {
|
|
|
197
237
|
|
|
198
238
|
const originalTasks = [mockTask];
|
|
199
239
|
queryClient.setQueryData(['tasks', 'board-1'], originalTasks);
|
|
240
|
+
queryClient.setQueryData(['tasks-full', 'board-1', 'all'], originalTasks);
|
|
241
|
+
queryClient.setQueryData(['task', 'task-1'], mockTask);
|
|
200
242
|
|
|
201
243
|
const { result } = renderHook(
|
|
202
244
|
() =>
|
|
@@ -219,6 +261,12 @@ describe('useTaskLabelManagement', () => {
|
|
|
219
261
|
'board-1',
|
|
220
262
|
]);
|
|
221
263
|
expect(cachedTasks).toEqual(originalTasks);
|
|
264
|
+
expect(
|
|
265
|
+
queryClient.getQueryData<Task[]>(['tasks-full', 'board-1', 'all'])
|
|
266
|
+
).toEqual(originalTasks);
|
|
267
|
+
expect(queryClient.getQueryData<Task>(['task', 'task-1'])).toEqual(
|
|
268
|
+
mockTask
|
|
269
|
+
);
|
|
222
270
|
|
|
223
271
|
// Verify error toast was shown with the correct format
|
|
224
272
|
expect(mockToast.error).toHaveBeenCalledWith('Error', {
|