@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
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
Filter,
|
|
12
12
|
GripVertical,
|
|
13
13
|
Loader2,
|
|
14
|
+
Pin,
|
|
15
|
+
PinOff,
|
|
14
16
|
RotateCcw,
|
|
15
17
|
} from '@tuturuuu/icons';
|
|
16
18
|
import type { ExternalTaskSortBy } from '@tuturuuu/internal-api/tasks';
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
} from './kanban/kanban-column-collapse';
|
|
50
52
|
import { ListActions } from './list-actions';
|
|
51
53
|
import { statusIcons } from './status-section';
|
|
54
|
+
import type { TaskCardAssigneeMemberSource } from './task-card/task-card';
|
|
52
55
|
import type { TaskFilters } from './task-filter';
|
|
53
56
|
import { VirtualizedTaskList } from './task-list';
|
|
54
57
|
|
|
@@ -161,6 +164,8 @@ interface BoardColumnProps {
|
|
|
161
164
|
isMultiSelectMode?: boolean;
|
|
162
165
|
setIsMultiSelectMode?: (value: boolean) => void;
|
|
163
166
|
isPersonalWorkspace?: boolean;
|
|
167
|
+
canUseBoardAssignees?: boolean;
|
|
168
|
+
assigneeMemberSource?: TaskCardAssigneeMemberSource;
|
|
164
169
|
onTaskSelect?: (taskId: string, event: React.MouseEvent) => void;
|
|
165
170
|
onClearSelection?: () => void;
|
|
166
171
|
onAddTask?: (list: TaskList) => void;
|
|
@@ -174,6 +179,10 @@ interface BoardColumnProps {
|
|
|
174
179
|
wsId: string;
|
|
175
180
|
onExternalTasksCollapsedChange?: (collapsed: boolean) => void;
|
|
176
181
|
onTaskListCollapsedChange?: (listId: string, collapsed: boolean) => void;
|
|
182
|
+
specialPinned?: boolean;
|
|
183
|
+
specialStickyOffset?: string;
|
|
184
|
+
onSpecialPinnedChange?: (pinned: boolean) => void;
|
|
185
|
+
readOnly?: boolean;
|
|
177
186
|
}
|
|
178
187
|
|
|
179
188
|
export function BoardColumn({
|
|
@@ -189,6 +198,8 @@ export function BoardColumn({
|
|
|
189
198
|
isMultiSelectMode,
|
|
190
199
|
setIsMultiSelectMode,
|
|
191
200
|
isPersonalWorkspace,
|
|
201
|
+
canUseBoardAssignees,
|
|
202
|
+
assigneeMemberSource,
|
|
192
203
|
onAddTask,
|
|
193
204
|
dragPreviewPosition,
|
|
194
205
|
suppressTaskTransforms,
|
|
@@ -200,6 +211,10 @@ export function BoardColumn({
|
|
|
200
211
|
wsId,
|
|
201
212
|
onExternalTasksCollapsedChange,
|
|
202
213
|
onTaskListCollapsedChange,
|
|
214
|
+
specialPinned = false,
|
|
215
|
+
specialStickyOffset,
|
|
216
|
+
onSpecialPinnedChange,
|
|
217
|
+
readOnly = false,
|
|
203
218
|
}: BoardColumnProps) {
|
|
204
219
|
const t = useTranslations('common');
|
|
205
220
|
const tTasks = useTranslations('ws-tasks');
|
|
@@ -210,7 +225,9 @@ export function BoardColumn({
|
|
|
210
225
|
const isExternalCollapsed =
|
|
211
226
|
isExternalStaging && column.is_external_collapsed === true;
|
|
212
227
|
const listState = pagination[column.id];
|
|
213
|
-
const isInitialLoad =
|
|
228
|
+
const isInitialLoad = readOnly
|
|
229
|
+
? false
|
|
230
|
+
: !listState || listState.isInitialLoad;
|
|
214
231
|
const [externalIncludeDocuments, setExternalIncludeDocuments] =
|
|
215
232
|
useState(false);
|
|
216
233
|
const [externalIncludeDoneClosed, setExternalIncludeDoneClosed] =
|
|
@@ -376,7 +393,7 @@ export function BoardColumn({
|
|
|
376
393
|
isDragging,
|
|
377
394
|
} = useSortable({
|
|
378
395
|
id: column.id,
|
|
379
|
-
disabled: isExternalStaging,
|
|
396
|
+
disabled: readOnly || isExternalStaging,
|
|
380
397
|
data: {
|
|
381
398
|
type: 'Column',
|
|
382
399
|
column: {
|
|
@@ -400,6 +417,12 @@ export function BoardColumn({
|
|
|
400
417
|
transform: CSS.Transform.toString(transform),
|
|
401
418
|
transition,
|
|
402
419
|
};
|
|
420
|
+
const columnStyle: React.CSSProperties = specialStickyOffset
|
|
421
|
+
? {
|
|
422
|
+
...style,
|
|
423
|
+
left: `calc(var(--kanban-snap-left-padding) + ${specialStickyOffset})`,
|
|
424
|
+
}
|
|
425
|
+
: style;
|
|
403
426
|
|
|
404
427
|
const handleUpdate = () => {
|
|
405
428
|
onUpdate?.();
|
|
@@ -446,6 +469,9 @@ export function BoardColumn({
|
|
|
446
469
|
: visibleTasks.length;
|
|
447
470
|
const externalFilterCount =
|
|
448
471
|
(externalIncludeDocuments ? 1 : 0) + (externalIncludeDoneClosed ? 1 : 0);
|
|
472
|
+
const pinListLabel = specialPinned
|
|
473
|
+
? tTasks('unpin_task_list', { name: translateListName(column.name) })
|
|
474
|
+
: tTasks('pin_task_list', { name: translateListName(column.name) });
|
|
449
475
|
|
|
450
476
|
// Memoize drag handle for performance
|
|
451
477
|
const DragHandle = useMemo(
|
|
@@ -500,10 +526,14 @@ export function BoardColumn({
|
|
|
500
526
|
return (
|
|
501
527
|
<Card
|
|
502
528
|
ref={composedRef}
|
|
503
|
-
style={
|
|
529
|
+
style={columnStyle}
|
|
530
|
+
data-kanban-pinned-special={specialStickyOffset ? 'true' : undefined}
|
|
531
|
+
data-kanban-column-id={column.id}
|
|
532
|
+
data-kanban-real-column={isExternalStaging ? undefined : 'true'}
|
|
504
533
|
className={cn(
|
|
505
534
|
'group flex h-full w-14 shrink-0 snap-start flex-col items-center rounded-xl border border-dashed transition-all duration-200',
|
|
506
535
|
'touch-none select-none overflow-hidden hover:shadow-md',
|
|
536
|
+
specialStickyOffset && 'sticky z-30',
|
|
507
537
|
isExternalCollapsed
|
|
508
538
|
? 'border-dynamic-cyan/45 bg-dynamic-cyan/[0.035]'
|
|
509
539
|
: colorClass
|
|
@@ -549,10 +579,14 @@ export function BoardColumn({
|
|
|
549
579
|
return (
|
|
550
580
|
<Card
|
|
551
581
|
ref={composedRef}
|
|
552
|
-
style={
|
|
582
|
+
style={columnStyle}
|
|
583
|
+
data-kanban-pinned-special={specialStickyOffset ? 'true' : undefined}
|
|
584
|
+
data-kanban-column-id={column.id}
|
|
585
|
+
data-kanban-real-column={isExternalStaging ? undefined : 'true'}
|
|
553
586
|
className={cn(
|
|
554
587
|
'group flex h-full w-[var(--kanban-column-width)] shrink-0 snap-start flex-col rounded-xl transition-all duration-200 last:snap-end',
|
|
555
588
|
'touch-none select-none',
|
|
589
|
+
specialStickyOffset && 'sticky z-30',
|
|
556
590
|
colorClass,
|
|
557
591
|
isDragging &&
|
|
558
592
|
'rotate-1 scale-[1.02] opacity-90 shadow-xl ring-2 ring-primary/20',
|
|
@@ -566,7 +600,7 @@ export function BoardColumn({
|
|
|
566
600
|
)}
|
|
567
601
|
>
|
|
568
602
|
<div className="flex items-center gap-2 rounded-t-xl border-b p-3">
|
|
569
|
-
{!isExternalStaging && DragHandle}
|
|
603
|
+
{!readOnly && !isExternalStaging && DragHandle}
|
|
570
604
|
<div className="flex flex-1 items-center gap-2">
|
|
571
605
|
<span className="text-sm">{statusIcon}</span>
|
|
572
606
|
<h3
|
|
@@ -577,9 +611,9 @@ export function BoardColumn({
|
|
|
577
611
|
: 'cursor-pointer hover:underline'
|
|
578
612
|
)}
|
|
579
613
|
onClick={() => {
|
|
580
|
-
if (!isExternalStaging) setIsEditOpen(true);
|
|
614
|
+
if (!readOnly && !isExternalStaging) setIsEditOpen(true);
|
|
581
615
|
}}
|
|
582
|
-
title={isExternalStaging ? undefined : t('edit_list')}
|
|
616
|
+
title={readOnly || isExternalStaging ? undefined : t('edit_list')}
|
|
583
617
|
>
|
|
584
618
|
{translateListName(column.name)}
|
|
585
619
|
</h3>
|
|
@@ -705,6 +739,26 @@ export function BoardColumn({
|
|
|
705
739
|
</DropdownMenuRadioGroup>
|
|
706
740
|
</DropdownMenuContent>
|
|
707
741
|
</DropdownMenu>
|
|
742
|
+
{onSpecialPinnedChange ? (
|
|
743
|
+
<Button
|
|
744
|
+
type="button"
|
|
745
|
+
variant="ghost"
|
|
746
|
+
size="xs"
|
|
747
|
+
className={cn(
|
|
748
|
+
'h-7 w-7 p-0 text-dynamic-cyan hover:bg-dynamic-cyan/10',
|
|
749
|
+
specialPinned && 'bg-dynamic-cyan/10'
|
|
750
|
+
)}
|
|
751
|
+
title={pinListLabel}
|
|
752
|
+
aria-label={pinListLabel}
|
|
753
|
+
onClick={() => onSpecialPinnedChange(!specialPinned)}
|
|
754
|
+
>
|
|
755
|
+
{specialPinned ? (
|
|
756
|
+
<PinOff className="h-3.5 w-3.5" />
|
|
757
|
+
) : (
|
|
758
|
+
<Pin className="h-3.5 w-3.5" />
|
|
759
|
+
)}
|
|
760
|
+
</Button>
|
|
761
|
+
) : null}
|
|
708
762
|
<Button
|
|
709
763
|
type="button"
|
|
710
764
|
variant="ghost"
|
|
@@ -720,38 +774,63 @@ export function BoardColumn({
|
|
|
720
774
|
) : (
|
|
721
775
|
<>
|
|
722
776
|
{isClosedCollapsed || column.status === 'closed' ? (
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
777
|
+
<>
|
|
778
|
+
{onSpecialPinnedChange ? (
|
|
779
|
+
<Button
|
|
780
|
+
type="button"
|
|
781
|
+
variant="ghost"
|
|
782
|
+
size="xs"
|
|
783
|
+
className={cn(
|
|
784
|
+
'h-7 w-7 p-0 hover:bg-muted/40',
|
|
785
|
+
getListTextColorClass(column.color as SupportedColor),
|
|
786
|
+
specialPinned && 'bg-muted/40'
|
|
787
|
+
)}
|
|
788
|
+
title={pinListLabel}
|
|
789
|
+
aria-label={pinListLabel}
|
|
790
|
+
onClick={() => onSpecialPinnedChange(!specialPinned)}
|
|
791
|
+
>
|
|
792
|
+
{specialPinned ? (
|
|
793
|
+
<PinOff className="h-3.5 w-3.5" />
|
|
794
|
+
) : (
|
|
795
|
+
<Pin className="h-3.5 w-3.5" />
|
|
796
|
+
)}
|
|
797
|
+
</Button>
|
|
798
|
+
) : null}
|
|
799
|
+
<Button
|
|
800
|
+
type="button"
|
|
801
|
+
variant="ghost"
|
|
802
|
+
size="xs"
|
|
803
|
+
className={cn(
|
|
804
|
+
'h-7 w-7 p-0 hover:bg-muted/40',
|
|
805
|
+
getListTextColorClass(column.color as SupportedColor)
|
|
806
|
+
)}
|
|
807
|
+
title={tTasks('collapse_task_list', {
|
|
808
|
+
name: translateListName(column.name),
|
|
809
|
+
})}
|
|
810
|
+
aria-label={tTasks('collapse_task_list', {
|
|
811
|
+
name: translateListName(column.name),
|
|
812
|
+
})}
|
|
813
|
+
onClick={() => onTaskListCollapsedChange?.(column.id, true)}
|
|
814
|
+
>
|
|
815
|
+
<ChevronLeft className="h-3.5 w-3.5" />
|
|
816
|
+
</Button>
|
|
817
|
+
</>
|
|
741
818
|
) : null}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
819
|
+
{!readOnly && (
|
|
820
|
+
<ListActions
|
|
821
|
+
listId={column.id}
|
|
822
|
+
listName={column.name}
|
|
823
|
+
listStatus={column.status}
|
|
824
|
+
listColor={column.color as SupportedColor}
|
|
825
|
+
tasks={tasks}
|
|
826
|
+
boardId={boardId}
|
|
827
|
+
wsId={wsId}
|
|
828
|
+
onUpdate={handleUpdate}
|
|
829
|
+
onSelectAll={handleSelectAll}
|
|
830
|
+
isEditOpen={isEditOpen}
|
|
831
|
+
onEditOpenChange={setIsEditOpen}
|
|
832
|
+
/>
|
|
833
|
+
)}
|
|
755
834
|
</>
|
|
756
835
|
)}
|
|
757
836
|
</div>
|
|
@@ -773,6 +852,8 @@ export function BoardColumn({
|
|
|
773
852
|
isMultiSelectMode={isMultiSelectMode}
|
|
774
853
|
selectedTasks={selectedTasks}
|
|
775
854
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
855
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
856
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
776
857
|
onTaskSelect={onTaskSelect}
|
|
777
858
|
onClearSelection={onClearSelection}
|
|
778
859
|
dragPreviewPosition={dragPreviewPosition}
|
|
@@ -783,10 +864,11 @@ export function BoardColumn({
|
|
|
783
864
|
onLoadMore={handleLoadMore}
|
|
784
865
|
hasMore={listState?.hasMore ?? false}
|
|
785
866
|
isLoadingMore={listState?.isLoading ?? false}
|
|
867
|
+
readOnly={readOnly}
|
|
786
868
|
/>
|
|
787
869
|
)}
|
|
788
870
|
|
|
789
|
-
{!isExternalStaging && (
|
|
871
|
+
{!readOnly && !isExternalStaging && (
|
|
790
872
|
<div className="rounded-b-xl border-t p-3 backdrop-blur-sm">
|
|
791
873
|
<Button
|
|
792
874
|
variant="ghost"
|
|
@@ -44,6 +44,7 @@ import { useBoardBroadcast } from '../../shared/board-broadcast-context';
|
|
|
44
44
|
import { isTaskListNameExistsError } from '../../shared/task-board-errors';
|
|
45
45
|
import { normalizeBoardText } from './board-text-utils';
|
|
46
46
|
import { TaskCard } from './task';
|
|
47
|
+
import type { TaskCardAssigneeMemberSource } from './task-card/task-card';
|
|
47
48
|
|
|
48
49
|
interface Props {
|
|
49
50
|
list: TaskList;
|
|
@@ -54,6 +55,8 @@ interface Props {
|
|
|
54
55
|
isOverlay?: boolean;
|
|
55
56
|
hideTasksMode?: boolean;
|
|
56
57
|
isPersonalWorkspace?: boolean;
|
|
58
|
+
canUseBoardAssignees?: boolean;
|
|
59
|
+
assigneeMemberSource?: TaskCardAssigneeMemberSource;
|
|
57
60
|
onAddTask?: (list: TaskList) => void;
|
|
58
61
|
}
|
|
59
62
|
|
|
@@ -102,6 +105,8 @@ export function EnhancedTaskList({
|
|
|
102
105
|
isOverlay = false,
|
|
103
106
|
hideTasksMode = false,
|
|
104
107
|
isPersonalWorkspace = false,
|
|
108
|
+
canUseBoardAssignees,
|
|
109
|
+
assigneeMemberSource,
|
|
105
110
|
onAddTask,
|
|
106
111
|
}: Props) {
|
|
107
112
|
const t = useTranslations('common');
|
|
@@ -479,6 +484,8 @@ export function EnhancedTaskList({
|
|
|
479
484
|
taskList={list}
|
|
480
485
|
boardId={boardId}
|
|
481
486
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
487
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
488
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
482
489
|
onUpdate={onUpdate}
|
|
483
490
|
/>
|
|
484
491
|
))}
|
|
@@ -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(
|
|
@@ -12,9 +12,9 @@ export interface WorkspaceProject {
|
|
|
12
12
|
export interface WorkspaceMember {
|
|
13
13
|
id: string;
|
|
14
14
|
user_id?: string;
|
|
15
|
-
display_name
|
|
16
|
-
email
|
|
17
|
-
avatar_url
|
|
15
|
+
display_name?: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
avatar_url?: string | null;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface BulkOperationsConfig {
|
|
@@ -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);
|
|
@@ -5,14 +5,24 @@ import {
|
|
|
5
5
|
listWorkspaceLabels,
|
|
6
6
|
listWorkspaceTaskProjects,
|
|
7
7
|
} from '@tuturuuu/internal-api';
|
|
8
|
+
import { listWorkspaceTaskBoardViewableMembers } from '@tuturuuu/internal-api/tasks';
|
|
8
9
|
import type { Workspace } from '@tuturuuu/types';
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useWorkspaceMembers,
|
|
12
|
+
type WorkspaceMember,
|
|
13
|
+
} from '@tuturuuu/ui/hooks/use-workspace-members';
|
|
10
14
|
|
|
11
15
|
export function useBulkResources({
|
|
16
|
+
boardId,
|
|
17
|
+
canUseBoardAssignees,
|
|
18
|
+
assigneeMemberSource,
|
|
12
19
|
workspace,
|
|
13
20
|
isMultiSelectMode,
|
|
14
21
|
selectedCount,
|
|
15
22
|
}: {
|
|
23
|
+
boardId?: string | null;
|
|
24
|
+
canUseBoardAssignees?: boolean;
|
|
25
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
16
26
|
workspace: Workspace;
|
|
17
27
|
isMultiSelectMode: boolean;
|
|
18
28
|
selectedCount: number;
|
|
@@ -46,13 +56,57 @@ export function useBulkResources({
|
|
|
46
56
|
});
|
|
47
57
|
|
|
48
58
|
// Workspace members for bulk operations
|
|
49
|
-
const
|
|
59
|
+
const shouldLoadMembers =
|
|
60
|
+
canUseBoardAssignees !== false && isMultiSelectMode && selectedCount > 0;
|
|
61
|
+
const effectiveAssigneeMemberSource =
|
|
62
|
+
assigneeMemberSource ?? (workspace.personal ? 'board' : 'workspace');
|
|
63
|
+
const { data: workspaceMembersData = [] } = useWorkspaceMembers(
|
|
64
|
+
workspace.id,
|
|
65
|
+
{
|
|
66
|
+
enabled:
|
|
67
|
+
!!workspace.id &&
|
|
68
|
+
shouldLoadMembers &&
|
|
69
|
+
effectiveAssigneeMemberSource !== 'board',
|
|
70
|
+
}
|
|
71
|
+
);
|
|
72
|
+
const { data: boardViewableMembers = [] } = useQuery({
|
|
73
|
+
queryKey: ['task-board-viewable-members', workspace.id, boardId],
|
|
74
|
+
queryFn: async (): Promise<WorkspaceMember[]> => {
|
|
75
|
+
if (!workspace.id || !boardId) return [];
|
|
76
|
+
|
|
77
|
+
const payload = await listWorkspaceTaskBoardViewableMembers(
|
|
78
|
+
workspace.id,
|
|
79
|
+
boardId
|
|
80
|
+
);
|
|
81
|
+
const members = Array.isArray(payload?.members) ? payload.members : [];
|
|
82
|
+
|
|
83
|
+
return members.map((member) => ({
|
|
84
|
+
id: member.user_id,
|
|
85
|
+
user_id: member.user_id,
|
|
86
|
+
workspace_id: workspace.id,
|
|
87
|
+
display_name: member.display_name ?? member.email ?? member.user_id,
|
|
88
|
+
email: member.email ?? undefined,
|
|
89
|
+
avatar_url: member.avatar_url ?? undefined,
|
|
90
|
+
}));
|
|
91
|
+
},
|
|
50
92
|
enabled:
|
|
51
93
|
!!workspace.id &&
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
94
|
+
!!boardId &&
|
|
95
|
+
shouldLoadMembers &&
|
|
96
|
+
effectiveAssigneeMemberSource !== 'workspace',
|
|
97
|
+
staleTime: 5 * 60 * 1000,
|
|
55
98
|
});
|
|
99
|
+
const workspaceMembers: WorkspaceMember[] = [
|
|
100
|
+
...workspaceMembersData,
|
|
101
|
+
...boardViewableMembers.filter(
|
|
102
|
+
(boardMember) =>
|
|
103
|
+
!workspaceMembersData.some(
|
|
104
|
+
(workspaceMember) =>
|
|
105
|
+
(workspaceMember.user_id ?? workspaceMember.id) ===
|
|
106
|
+
boardMember.user_id
|
|
107
|
+
)
|
|
108
|
+
),
|
|
109
|
+
];
|
|
56
110
|
|
|
57
111
|
return { workspaceLabels, workspaceProjects, workspaceMembers };
|
|
58
112
|
}
|
|
@@ -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(
|
|
@@ -13,6 +13,8 @@ interface DragPreviewProps {
|
|
|
13
13
|
columns: TaskList[];
|
|
14
14
|
boardId: string;
|
|
15
15
|
isPersonalWorkspace: boolean;
|
|
16
|
+
canUseBoardAssignees?: boolean;
|
|
17
|
+
assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
|
|
16
18
|
isMultiSelectMode: boolean;
|
|
17
19
|
selectedTasks: Set<string>;
|
|
18
20
|
onUpdate: () => void;
|
|
@@ -26,6 +28,8 @@ export function DragPreview({
|
|
|
26
28
|
columns,
|
|
27
29
|
boardId,
|
|
28
30
|
isPersonalWorkspace,
|
|
31
|
+
canUseBoardAssignees,
|
|
32
|
+
assigneeMemberSource,
|
|
29
33
|
isMultiSelectMode,
|
|
30
34
|
selectedTasks,
|
|
31
35
|
onUpdate,
|
|
@@ -52,6 +56,8 @@ export function DragPreview({
|
|
|
52
56
|
isOverlay
|
|
53
57
|
onUpdate={onUpdate}
|
|
54
58
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
59
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
60
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
55
61
|
/>
|
|
56
62
|
{isMultiCardDrag && (
|
|
57
63
|
<>
|
|
@@ -74,6 +80,8 @@ export function DragPreview({
|
|
|
74
80
|
boardId,
|
|
75
81
|
onUpdate,
|
|
76
82
|
isPersonalWorkspace,
|
|
83
|
+
canUseBoardAssignees,
|
|
84
|
+
assigneeMemberSource,
|
|
77
85
|
isMultiSelectMode,
|
|
78
86
|
selectedTasks,
|
|
79
87
|
]);
|
|
@@ -87,11 +95,22 @@ export function DragPreview({
|
|
|
87
95
|
tasks={tasks.filter((task) => task.list_id === activeColumn.id)}
|
|
88
96
|
isOverlay
|
|
89
97
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
98
|
+
canUseBoardAssignees={canUseBoardAssignees}
|
|
99
|
+
assigneeMemberSource={assigneeMemberSource}
|
|
90
100
|
onUpdate={onUpdate}
|
|
91
101
|
wsId={wsId}
|
|
92
102
|
/>
|
|
93
103
|
) : null,
|
|
94
|
-
[
|
|
104
|
+
[
|
|
105
|
+
activeColumn,
|
|
106
|
+
tasks,
|
|
107
|
+
boardId,
|
|
108
|
+
isPersonalWorkspace,
|
|
109
|
+
canUseBoardAssignees,
|
|
110
|
+
assigneeMemberSource,
|
|
111
|
+
onUpdate,
|
|
112
|
+
wsId,
|
|
113
|
+
]
|
|
95
114
|
);
|
|
96
115
|
|
|
97
116
|
return <>{MemoizedTaskOverlay || MemoizedColumnOverlay}</>;
|