@tuturuuu/ui 0.7.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 +88 -0
- package/biome.json +1 -1
- package/package.json +75 -73
- 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/currency-input.test.tsx +43 -0
- package/src/components/ui/currency-input.tsx +1 -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-access/workspace-access-default-role-card.tsx +60 -35
- package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
- package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
- package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
- package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
- package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
- package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
- package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
- 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 +3 -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-card.tsx +21 -9
- 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/money-input.test.tsx +64 -0
- package/src/components/ui/money-input.tsx +63 -0
- 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 +104 -80
- package/src/components/ui/storefront/checkout-overlay.tsx +26 -0
- package/src/components/ui/storefront/hero-panel.tsx +2 -8
- package/src/components/ui/storefront/image-panel.tsx +6 -0
- package/src/components/ui/storefront/index.ts +11 -0
- package/src/components/ui/storefront/listing-card.tsx +84 -22
- package/src/components/ui/storefront/merch-sections.tsx +70 -0
- package/src/components/ui/storefront/product-detail.tsx +289 -0
- package/src/components/ui/storefront/product-dialog.tsx +72 -0
- package/src/components/ui/storefront/storefront-surface.test.tsx +221 -3
- package/src/components/ui/storefront/storefront-surface.tsx +288 -153
- package/src/components/ui/storefront/types.ts +27 -1
- package/src/components/ui/storefront/utils.ts +117 -27
- package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
- package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
- package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
- package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -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/content-migration.ts +41 -18
- package/src/components/ui/text-editor/editor.tsx +69 -14
- package/src/components/ui/text-editor/extensions.ts +9 -3
- package/src/components/ui/text-editor/highlight-extension.ts +22 -0
- package/src/components/ui/text-editor/image-extension.ts +40 -18
- package/src/components/ui/text-editor/tool-bar.tsx +9 -16
- package/src/components/ui/text-editor/video-extension.ts +11 -2
- 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/__tests__/workspace-projects-client-page.test.tsx +70 -1
- 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-external-retry.test.tsx +127 -0
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +113 -46
- 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 +51 -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.test.ts +63 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +127 -38
- 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 +410 -4
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +106 -14
- 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 +186 -0
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +59 -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/boardId/timeline/timeline-display.ts +9 -0
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
- package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
- package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
- 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 +237 -3
- 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 +465 -937
- 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 +596 -82
- package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
- 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-dialog-presentation.test.ts +53 -0
- package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -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/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 +44 -15
- 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/__tests__/useBoardRealtime.test.tsx +2 -2
- package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -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/useBoardRealtime.ts +6 -3
- package/src/hooks/useBoardRealtime.types.ts +11 -0
- package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
- package/src/hooks/useCursorTracking.ts +91 -27
- package/src/hooks/useTaskUserRealtime.ts +5 -3
|
@@ -43,6 +43,7 @@ interface VirtualizedTaskListProps {
|
|
|
43
43
|
onLoadMore?: () => void;
|
|
44
44
|
hasMore?: boolean;
|
|
45
45
|
isLoadingMore?: boolean;
|
|
46
|
+
readOnly?: boolean;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
interface TaskListContentProps {
|
|
@@ -64,6 +65,7 @@ interface TaskListContentProps {
|
|
|
64
65
|
bulkUpdateCustomDueDate?: (date: Date | null) => Promise<void>;
|
|
65
66
|
startIndex?: number;
|
|
66
67
|
taskOrder?: Pick<Task, 'id'>[];
|
|
68
|
+
readOnly?: boolean;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
export function getTaskDragPreviewSlotIndex({
|
|
@@ -132,6 +134,7 @@ function TaskListContent({
|
|
|
132
134
|
bulkUpdateCustomDueDate,
|
|
133
135
|
startIndex = 0,
|
|
134
136
|
taskOrder = tasks,
|
|
137
|
+
readOnly = false,
|
|
135
138
|
}: TaskListContentProps) {
|
|
136
139
|
const slotIndex = getTaskDragPreviewSlotIndex({
|
|
137
140
|
columnId: column.id,
|
|
@@ -170,6 +173,7 @@ function TaskListContent({
|
|
|
170
173
|
optimisticUpdateInProgress={optimisticUpdateInProgress}
|
|
171
174
|
selectedTasks={selectedTasks}
|
|
172
175
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
176
|
+
readOnly={readOnly}
|
|
173
177
|
/>
|
|
174
178
|
{slotIndex === globalIndex + 1 && (
|
|
175
179
|
<DragPreviewSlot
|
|
@@ -238,6 +242,7 @@ function VirtualizedTaskListInner({
|
|
|
238
242
|
onLoadMore,
|
|
239
243
|
hasMore,
|
|
240
244
|
isLoadingMore,
|
|
245
|
+
readOnly = false,
|
|
241
246
|
}: VirtualizedTaskListProps) {
|
|
242
247
|
const t = useTranslations('common');
|
|
243
248
|
const tTasks = useTranslations('ws-tasks');
|
|
@@ -474,6 +479,7 @@ function VirtualizedTaskListInner({
|
|
|
474
479
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
475
480
|
startIndex={startIndex}
|
|
476
481
|
taskOrder={tasks}
|
|
482
|
+
readOnly={readOnly}
|
|
477
483
|
/>
|
|
478
484
|
</div>
|
|
479
485
|
</div>
|
|
@@ -502,6 +508,7 @@ function VirtualizedTaskListInner({
|
|
|
502
508
|
optimisticUpdateInProgress={optimisticUpdateInProgress}
|
|
503
509
|
bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
|
|
504
510
|
taskOrder={tasks}
|
|
511
|
+
readOnly={readOnly}
|
|
505
512
|
/>
|
|
506
513
|
{loadMoreSentinel}
|
|
507
514
|
</SortableContext>
|
|
@@ -14,6 +14,15 @@ export const HANDLE_WIDTH = 12;
|
|
|
14
14
|
export const DRAG_ACTIVATION_PX = 6;
|
|
15
15
|
export const COLLAPSED_UNSCHEDULED_PREVIEW_COUNT = 4;
|
|
16
16
|
|
|
17
|
+
export function getTimelineDayGridBackground(dayWidth: number, opacity = 0.45) {
|
|
18
|
+
const safeDayWidth = Math.max(1, dayWidth);
|
|
19
|
+
const lineStart = Math.max(0, safeDayWidth - 1);
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
backgroundImage: `repeating-linear-gradient(to right, transparent 0, transparent ${lineStart}px, hsl(var(--border) / ${opacity}) ${lineStart}px, hsl(var(--border) / ${opacity}) ${safeDayWidth}px)`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
export function getDensityConfig(density: Density) {
|
|
18
27
|
switch (density) {
|
|
19
28
|
case 'compact':
|
|
@@ -6,7 +6,10 @@ import { cn } from '@tuturuuu/utils/format';
|
|
|
6
6
|
import dayjs from 'dayjs';
|
|
7
7
|
import type { useTranslations } from 'next-intl';
|
|
8
8
|
import type { PointerEvent as ReactPointerEvent } from 'react';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getListStatusBadgeClasses,
|
|
11
|
+
getTimelineDayGridBackground,
|
|
12
|
+
} from './timeline-display';
|
|
10
13
|
import { TimelineTaskRow } from './timeline-task-row';
|
|
11
14
|
import type {
|
|
12
15
|
TimelineInteractionMode,
|
|
@@ -197,7 +200,6 @@ export function TimelineGrid({
|
|
|
197
200
|
groupId={group.id}
|
|
198
201
|
localTasks={localTasks}
|
|
199
202
|
dayWidth={dayWidth}
|
|
200
|
-
timelineWidth={timelineWidth}
|
|
201
203
|
groupHeaderHeight={groupHeaderHeight}
|
|
202
204
|
isMoveTargetGroup={isMoveTargetGroup}
|
|
203
205
|
isPreviewGroup={isPreviewGroup}
|
|
@@ -257,7 +259,6 @@ export function TimelineGrid({
|
|
|
257
259
|
groupId={group.id}
|
|
258
260
|
localTasks={localTasks}
|
|
259
261
|
dayWidth={dayWidth}
|
|
260
|
-
timelineWidth={timelineWidth}
|
|
261
262
|
groupHeaderHeight={rowHeight}
|
|
262
263
|
isMoveTargetGroup={isMoveTargetGroup}
|
|
263
264
|
isPreviewGroup={isPreviewGroup}
|
|
@@ -281,7 +282,6 @@ function TimelineGroupDropTarget({
|
|
|
281
282
|
groupId,
|
|
282
283
|
localTasks,
|
|
283
284
|
dayWidth,
|
|
284
|
-
timelineWidth,
|
|
285
285
|
groupHeaderHeight,
|
|
286
286
|
isMoveTargetGroup,
|
|
287
287
|
isPreviewGroup,
|
|
@@ -295,7 +295,6 @@ function TimelineGroupDropTarget({
|
|
|
295
295
|
groupId: string;
|
|
296
296
|
localTasks: Task[];
|
|
297
297
|
dayWidth: number;
|
|
298
|
-
timelineWidth: number;
|
|
299
298
|
groupHeaderHeight: number;
|
|
300
299
|
isMoveTargetGroup: boolean;
|
|
301
300
|
isPreviewGroup: boolean;
|
|
@@ -364,17 +363,10 @@ function TimelineGroupDropTarget({
|
|
|
364
363
|
</div>
|
|
365
364
|
);
|
|
366
365
|
})()}
|
|
367
|
-
<div
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
<div
|
|
372
|
-
key={`${groupId}-drop-${index}`}
|
|
373
|
-
className="h-full border-border/35 border-r"
|
|
374
|
-
style={{ width: dayWidth }}
|
|
375
|
-
/>
|
|
376
|
-
))}
|
|
377
|
-
</div>
|
|
366
|
+
<div
|
|
367
|
+
className="pointer-events-none absolute inset-0"
|
|
368
|
+
style={getTimelineDayGridBackground(dayWidth, 0.35)}
|
|
369
|
+
/>
|
|
378
370
|
</div>
|
|
379
371
|
);
|
|
380
372
|
}
|
|
@@ -18,7 +18,6 @@ import {
|
|
|
18
18
|
DropdownMenuSubTrigger,
|
|
19
19
|
} from '@tuturuuu/ui/dropdown-menu';
|
|
20
20
|
import { cn } from '@tuturuuu/utils/format';
|
|
21
|
-
import dayjs from 'dayjs';
|
|
22
21
|
import type { useTranslations } from 'next-intl';
|
|
23
22
|
import type { PointerEvent as ReactPointerEvent } from 'react';
|
|
24
23
|
import { useState } from 'react';
|
|
@@ -26,6 +25,7 @@ import { TaskRowActionsMenu } from '../../../shared/task-row-actions-menu';
|
|
|
26
25
|
import {
|
|
27
26
|
getStatusToneClasses,
|
|
28
27
|
getTaskEyebrow,
|
|
28
|
+
getTimelineDayGridBackground,
|
|
29
29
|
HANDLE_WIDTH,
|
|
30
30
|
} from './timeline-display';
|
|
31
31
|
import type {
|
|
@@ -263,30 +263,10 @@ export function TimelineTaskRow({
|
|
|
263
263
|
style={{ minHeight: rowHeight }}
|
|
264
264
|
onDoubleClick={() => onOpenEditor(item.task)}
|
|
265
265
|
>
|
|
266
|
-
<div
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const isToday = todayVisible && index === todayIndex;
|
|
271
|
-
const date = item.start
|
|
272
|
-
? dayjs(item.start).add(index - item.offsetDays, 'day')
|
|
273
|
-
: null;
|
|
274
|
-
const isWeekend = date ? [0, 6].includes(date.day()) : false;
|
|
275
|
-
|
|
276
|
-
return (
|
|
277
|
-
<div
|
|
278
|
-
key={`${item.task.id}-${index}`}
|
|
279
|
-
className={cn(
|
|
280
|
-
'h-full border-border/45 border-r',
|
|
281
|
-
index === 0 && 'border-l border-l-border/50',
|
|
282
|
-
isWeekend && 'bg-muted/[0.12]',
|
|
283
|
-
isToday && 'bg-dynamic-blue/[0.035]'
|
|
284
|
-
)}
|
|
285
|
-
style={{ width: dayWidth }}
|
|
286
|
-
/>
|
|
287
|
-
);
|
|
288
|
-
})}
|
|
289
|
-
</div>
|
|
266
|
+
<div
|
|
267
|
+
className="pointer-events-none absolute inset-0 border-border/50 border-l"
|
|
268
|
+
style={getTimelineDayGridBackground(dayWidth)}
|
|
269
|
+
/>
|
|
290
270
|
|
|
291
271
|
{todayVisible && (
|
|
292
272
|
<div
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import type { Task } from '@tuturuuu/types/primitives/Task';
|
|
2
2
|
import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
|
|
3
3
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
buildTimelineModel,
|
|
6
|
+
computeTimelineSpans,
|
|
7
|
+
MAX_TIMELINE_DAYS,
|
|
8
|
+
} from './timeline-utils';
|
|
5
9
|
|
|
6
10
|
const lists: TaskList[] = [
|
|
7
11
|
{
|
|
@@ -108,6 +112,37 @@ describe('timeline row model', () => {
|
|
|
108
112
|
);
|
|
109
113
|
});
|
|
110
114
|
|
|
115
|
+
it('caps the rendered day window for extreme scheduled task ranges', () => {
|
|
116
|
+
vi.useFakeTimers();
|
|
117
|
+
vi.setSystemTime(new Date('2026-05-07T12:00:00.000Z'));
|
|
118
|
+
|
|
119
|
+
const model = buildTimelineModel(
|
|
120
|
+
[
|
|
121
|
+
task({
|
|
122
|
+
id: 'old-task',
|
|
123
|
+
name: 'Very old task',
|
|
124
|
+
start_date: '2020-01-01T00:00:00.000Z',
|
|
125
|
+
end_date: '2020-01-02T23:59:59.999Z',
|
|
126
|
+
}),
|
|
127
|
+
task({
|
|
128
|
+
id: 'future-task',
|
|
129
|
+
name: 'Very future task',
|
|
130
|
+
start_date: '2035-01-01T00:00:00.000Z',
|
|
131
|
+
end_date: '2035-01-02T23:59:59.999Z',
|
|
132
|
+
}),
|
|
133
|
+
],
|
|
134
|
+
lists
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
expect(model.days).toHaveLength(MAX_TIMELINE_DAYS);
|
|
138
|
+
expect(model.rangeStart.getTime()).toBeLessThanOrEqual(
|
|
139
|
+
new Date('2026-05-07T00:00:00.000Z').getTime()
|
|
140
|
+
);
|
|
141
|
+
expect(model.rangeEnd.getTime()).toBeGreaterThanOrEqual(
|
|
142
|
+
new Date('2026-05-07T00:00:00.000Z').getTime()
|
|
143
|
+
);
|
|
144
|
+
});
|
|
145
|
+
|
|
111
146
|
it('keeps span computation stable for existing analytics exports', () => {
|
|
112
147
|
vi.useFakeTimers();
|
|
113
148
|
vi.setSystemTime(new Date('2026-05-07T12:00:00.000Z'));
|
|
@@ -62,6 +62,7 @@ const DEFAULT_PAST_PADDING_DAYS = 3;
|
|
|
62
62
|
const DEFAULT_FUTURE_PADDING_DAYS = 6;
|
|
63
63
|
const EMPTY_PAST_PADDING_DAYS = 7;
|
|
64
64
|
const EMPTY_FUTURE_PADDING_DAYS = 14;
|
|
65
|
+
export const MAX_TIMELINE_DAYS = 180;
|
|
65
66
|
const TIMELINE_LIST_STATUS_ORDER: Record<string, number> = {
|
|
66
67
|
not_started: 0,
|
|
67
68
|
active: 1,
|
|
@@ -126,6 +127,48 @@ export function buildMonthSegments(days: Date[]) {
|
|
|
126
127
|
return segments;
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
function capTimelineRange(
|
|
131
|
+
rangeStart: Date,
|
|
132
|
+
rangeEnd: Date,
|
|
133
|
+
todayMid: dayjs.Dayjs
|
|
134
|
+
) {
|
|
135
|
+
const start = dayjs(rangeStart).startOf('day');
|
|
136
|
+
const end = dayjs(rangeEnd).startOf('day');
|
|
137
|
+
const totalDays = end.diff(start, 'day') + 1;
|
|
138
|
+
|
|
139
|
+
if (totalDays <= MAX_TIMELINE_DAYS) {
|
|
140
|
+
return {
|
|
141
|
+
rangeStart: start.toDate(),
|
|
142
|
+
rangeEnd: end.toDate(),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const latestStart = end.subtract(MAX_TIMELINE_DAYS - 1, 'day');
|
|
147
|
+
let cappedStart = start;
|
|
148
|
+
|
|
149
|
+
if (
|
|
150
|
+
(todayMid.isAfter(start) || todayMid.isSame(start, 'day')) &&
|
|
151
|
+
(todayMid.isBefore(end) || todayMid.isSame(end, 'day'))
|
|
152
|
+
) {
|
|
153
|
+
cappedStart = todayMid.subtract(Math.floor(MAX_TIMELINE_DAYS / 2), 'day');
|
|
154
|
+
} else if (todayMid.isAfter(end)) {
|
|
155
|
+
cappedStart = latestStart;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (cappedStart.isBefore(start)) {
|
|
159
|
+
cappedStart = start;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (cappedStart.isAfter(latestStart)) {
|
|
163
|
+
cappedStart = latestStart;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
rangeStart: cappedStart.toDate(),
|
|
168
|
+
rangeEnd: cappedStart.add(MAX_TIMELINE_DAYS - 1, 'day').toDate(),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
129
172
|
function withTaskRows(
|
|
130
173
|
items: Array<Omit<TimelineItem, 'offsetDays'>>,
|
|
131
174
|
rangeStart: Date
|
|
@@ -280,7 +323,7 @@ export function buildTimelineModel(
|
|
|
280
323
|
});
|
|
281
324
|
}
|
|
282
325
|
|
|
283
|
-
const
|
|
326
|
+
const rawRangeStart = scheduled.length
|
|
284
327
|
? dayjs(
|
|
285
328
|
scheduled.reduce(
|
|
286
329
|
(min, item) => (item.start < min ? item.start : min),
|
|
@@ -292,7 +335,7 @@ export function buildTimelineModel(
|
|
|
292
335
|
.toDate()
|
|
293
336
|
: todayMid.subtract(EMPTY_PAST_PADDING_DAYS, 'day').toDate();
|
|
294
337
|
|
|
295
|
-
const
|
|
338
|
+
const rawRangeEnd = scheduled.length
|
|
296
339
|
? dayjs(
|
|
297
340
|
scheduled.reduce(
|
|
298
341
|
(max, item) => (item.end > max ? item.end : max),
|
|
@@ -304,6 +347,12 @@ export function buildTimelineModel(
|
|
|
304
347
|
.toDate()
|
|
305
348
|
: todayMid.add(EMPTY_FUTURE_PADDING_DAYS, 'day').toDate();
|
|
306
349
|
|
|
350
|
+
const { rangeStart, rangeEnd } = capTimelineRange(
|
|
351
|
+
rawRangeStart,
|
|
352
|
+
rawRangeEnd,
|
|
353
|
+
todayMid
|
|
354
|
+
);
|
|
355
|
+
|
|
307
356
|
const days = enumerateDays(rangeStart, rangeEnd);
|
|
308
357
|
const monthSegments = buildMonthSegments(days);
|
|
309
358
|
const todayIndex = Math.round(
|
|
@@ -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
|
+
}
|
|
@@ -72,6 +72,15 @@ export default function WorkspaceProjectsClientPage({
|
|
|
72
72
|
const isPermissionLoading = permissionQuery.isLoading;
|
|
73
73
|
|
|
74
74
|
const resolvedWsId = workspace?.id;
|
|
75
|
+
const workspaceAccess = workspace as
|
|
76
|
+
| { access_type?: string; guest_products?: string[] }
|
|
77
|
+
| null
|
|
78
|
+
| undefined;
|
|
79
|
+
const isWorkspaceGuestTasksAccess =
|
|
80
|
+
workspaceAccess?.access_type === 'guest' &&
|
|
81
|
+
workspaceAccess.guest_products?.includes('tasks');
|
|
82
|
+
const canReadBoards =
|
|
83
|
+
canManageProjects === true || isWorkspaceGuestTasksAccess;
|
|
75
84
|
|
|
76
85
|
const {
|
|
77
86
|
data: boardsPayload,
|
|
@@ -85,9 +94,10 @@ export default function WorkspaceProjectsClientPage({
|
|
|
85
94
|
page: Number.parseInt(page, 10),
|
|
86
95
|
pageSize: Number.parseInt(pageSize, 10),
|
|
87
96
|
}),
|
|
88
|
-
enabled: Boolean(resolvedWsId),
|
|
97
|
+
enabled: Boolean(resolvedWsId && canReadBoards),
|
|
89
98
|
});
|
|
90
|
-
const isGuestBoardAccess =
|
|
99
|
+
const isGuestBoardAccess =
|
|
100
|
+
isWorkspaceGuestTasksAccess || boardsPayload?.access_type === 'guest';
|
|
91
101
|
|
|
92
102
|
useEffect(() => {
|
|
93
103
|
if (
|
|
@@ -112,7 +122,7 @@ export default function WorkspaceProjectsClientPage({
|
|
|
112
122
|
isWorkspacePending ||
|
|
113
123
|
isWorkspaceUserLoading ||
|
|
114
124
|
(isPermissionLoading && !isGuestBoardAccess) ||
|
|
115
|
-
isBoardsPending
|
|
125
|
+
(canReadBoards && isBoardsPending)
|
|
116
126
|
) {
|
|
117
127
|
return <BoardsListSkeleton />;
|
|
118
128
|
}
|
|
@@ -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
|
|