@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
|
@@ -48,6 +48,13 @@ import { Separator } from '../../separator';
|
|
|
48
48
|
import { Switch } from '../../switch';
|
|
49
49
|
import type { CalendarConnectionsManagerState } from './use-calendar-connections-manager';
|
|
50
50
|
|
|
51
|
+
function isWritableProviderAccess(accessRole?: string | null) {
|
|
52
|
+
if (!accessRole) return true;
|
|
53
|
+
return ['owner', 'writer', 'write', 'editor'].includes(
|
|
54
|
+
accessRole.toLowerCase()
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
51
58
|
export function CalendarConnectionsSettingsContent({
|
|
52
59
|
state,
|
|
53
60
|
className,
|
|
@@ -75,13 +82,26 @@ export function CalendarConnectionsSettingsContent({
|
|
|
75
82
|
setNewCalendarName,
|
|
76
83
|
setShowCreateCalendarDialog,
|
|
77
84
|
showCreateCalendarDialog,
|
|
85
|
+
syncPreferencesData,
|
|
86
|
+
syncPreferencesMutation,
|
|
78
87
|
systemCalendars,
|
|
79
88
|
t,
|
|
80
89
|
togglingIds,
|
|
81
90
|
togglingTuturuuuIds,
|
|
82
91
|
toggleAccountExpanded,
|
|
83
92
|
toggleWorkspaceCalendarMutation,
|
|
93
|
+
updateConnectionSyncSettingsMutation,
|
|
84
94
|
} = state;
|
|
95
|
+
const externalSyncOptions =
|
|
96
|
+
syncPreferencesData?.options.filter(
|
|
97
|
+
(option) => option.provider !== 'tuturuuu'
|
|
98
|
+
) ?? [];
|
|
99
|
+
const syncSettingsUnavailable =
|
|
100
|
+
syncPreferencesData?.settingsAvailable === false;
|
|
101
|
+
const syncSettingsDisabled =
|
|
102
|
+
!syncPreferencesData ||
|
|
103
|
+
syncSettingsUnavailable ||
|
|
104
|
+
syncPreferencesMutation.isPending;
|
|
85
105
|
|
|
86
106
|
return (
|
|
87
107
|
<div className={className ? `space-y-4 ${className}` : 'space-y-4'}>
|
|
@@ -128,6 +148,126 @@ export function CalendarConnectionsSettingsContent({
|
|
|
128
148
|
</Select>
|
|
129
149
|
</div>
|
|
130
150
|
|
|
151
|
+
<div className="space-y-3 rounded-md border p-3">
|
|
152
|
+
<div>
|
|
153
|
+
<h4 className="font-medium text-sm">
|
|
154
|
+
{t('two_way_sync') || 'Two-way sync'}
|
|
155
|
+
</h4>
|
|
156
|
+
<p className="text-muted-foreground text-xs">
|
|
157
|
+
{t('two_way_sync_desc') ||
|
|
158
|
+
'Control how Tuturuuu imports provider changes and mirrors native events outward.'}
|
|
159
|
+
</p>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
{syncSettingsUnavailable && (
|
|
163
|
+
<p className="rounded-md bg-muted px-3 py-2 text-muted-foreground text-xs">
|
|
164
|
+
{t('calendar_sync_settings_unavailable') ||
|
|
165
|
+
'Sync controls will be available after the latest calendar migration is applied.'}
|
|
166
|
+
</p>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
<div className="space-y-2">
|
|
170
|
+
<div className="flex items-center justify-between gap-3">
|
|
171
|
+
<div className="min-w-0">
|
|
172
|
+
<p className="font-medium text-sm">
|
|
173
|
+
{t('import_external_changes') || 'Import external changes'}
|
|
174
|
+
</p>
|
|
175
|
+
<p className="text-muted-foreground text-xs">
|
|
176
|
+
{t('import_external_changes_desc') ||
|
|
177
|
+
'Bring Google and Outlook event changes into Tuturuuu.'}
|
|
178
|
+
</p>
|
|
179
|
+
</div>
|
|
180
|
+
<Switch
|
|
181
|
+
checked={syncPreferencesData?.inboundSyncEnabled ?? true}
|
|
182
|
+
onCheckedChange={(checked) =>
|
|
183
|
+
syncPreferencesMutation.mutate({
|
|
184
|
+
inboundSyncEnabled: checked,
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
disabled={syncSettingsDisabled}
|
|
188
|
+
/>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<div className="flex items-center justify-between gap-3">
|
|
192
|
+
<div className="min-w-0">
|
|
193
|
+
<p className="font-medium text-sm">
|
|
194
|
+
{t('sync_tuturuuu_events_outward') ||
|
|
195
|
+
'Sync Tuturuuu events outward'}
|
|
196
|
+
</p>
|
|
197
|
+
<p className="text-muted-foreground text-xs">
|
|
198
|
+
{t('sync_tuturuuu_events_outward_desc') ||
|
|
199
|
+
'Mirror native Tuturuuu event creates and edits to a connected provider calendar.'}
|
|
200
|
+
</p>
|
|
201
|
+
</div>
|
|
202
|
+
<Switch
|
|
203
|
+
checked={syncPreferencesData?.outboundSyncEnabled ?? false}
|
|
204
|
+
onCheckedChange={(checked) =>
|
|
205
|
+
syncPreferencesMutation.mutate({
|
|
206
|
+
outboundSyncEnabled: checked,
|
|
207
|
+
})
|
|
208
|
+
}
|
|
209
|
+
disabled={
|
|
210
|
+
syncSettingsDisabled || externalSyncOptions.length === 0
|
|
211
|
+
}
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<Select
|
|
217
|
+
value={syncPreferencesData?.defaultOutboundConnectionId ?? 'none'}
|
|
218
|
+
onValueChange={(value) =>
|
|
219
|
+
syncPreferencesMutation.mutate({
|
|
220
|
+
defaultOutboundConnectionId: value === 'none' ? null : value,
|
|
221
|
+
})
|
|
222
|
+
}
|
|
223
|
+
disabled={syncSettingsDisabled || externalSyncOptions.length === 0}
|
|
224
|
+
>
|
|
225
|
+
<SelectTrigger className="w-full">
|
|
226
|
+
<SelectValue
|
|
227
|
+
placeholder={
|
|
228
|
+
t('choose_outbound_calendar') || 'Choose outbound calendar'
|
|
229
|
+
}
|
|
230
|
+
/>
|
|
231
|
+
</SelectTrigger>
|
|
232
|
+
<SelectContent>
|
|
233
|
+
<SelectItem value="none">
|
|
234
|
+
{t('no_outbound_calendar') || 'No outbound calendar'}
|
|
235
|
+
</SelectItem>
|
|
236
|
+
{externalSyncOptions.map((option) => (
|
|
237
|
+
<SelectItem key={option.id} value={option.connectionId}>
|
|
238
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
239
|
+
<span
|
|
240
|
+
className="h-2.5 w-2.5 shrink-0 rounded-full"
|
|
241
|
+
style={{
|
|
242
|
+
backgroundColor: option.color ?? undefined,
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
<span className="truncate">{option.label}</span>
|
|
246
|
+
<Badge variant="secondary" className="capitalize">
|
|
247
|
+
{option.provider}
|
|
248
|
+
</Badge>
|
|
249
|
+
</div>
|
|
250
|
+
</SelectItem>
|
|
251
|
+
))}
|
|
252
|
+
</SelectContent>
|
|
253
|
+
</Select>
|
|
254
|
+
|
|
255
|
+
<div className="flex items-center justify-between gap-3 rounded-md bg-muted/40 px-3 py-2">
|
|
256
|
+
<div className="min-w-0">
|
|
257
|
+
<p className="font-medium text-xs">
|
|
258
|
+
{t('conflict_policy') || 'Conflict policy'}
|
|
259
|
+
</p>
|
|
260
|
+
<p className="text-muted-foreground text-xs">
|
|
261
|
+
{t('latest_write_wins_desc') ||
|
|
262
|
+
'When both sides changed, the most recently written event wins.'}
|
|
263
|
+
</p>
|
|
264
|
+
</div>
|
|
265
|
+
<Badge variant="outline">
|
|
266
|
+
{t('latest_write_wins') || 'Latest write wins'}
|
|
267
|
+
</Badge>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
131
271
|
{/* Tuturuuu Calendars Section */}
|
|
132
272
|
<div className="space-y-3">
|
|
133
273
|
<div className="flex items-center justify-between">
|
|
@@ -370,40 +510,106 @@ export function CalendarConnectionsSettingsContent({
|
|
|
370
510
|
<Separator />
|
|
371
511
|
<div className="space-y-2 p-3">
|
|
372
512
|
{(calendarsByAccount[account.id] || []).length > 0 ? (
|
|
373
|
-
(calendarsByAccount[account.id] || []).map((cal) =>
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
>
|
|
389
|
-
|
|
390
|
-
|
|
513
|
+
(calendarsByAccount[account.id] || []).map((cal) => {
|
|
514
|
+
const syncControlsDisabled =
|
|
515
|
+
!cal.connectionExists ||
|
|
516
|
+
!cal.is_enabled ||
|
|
517
|
+
syncSettingsDisabled ||
|
|
518
|
+
updateConnectionSyncSettingsMutation.isPending;
|
|
519
|
+
const canWriteToProvider = isWritableProviderAccess(
|
|
520
|
+
cal.accessRole
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<div
|
|
525
|
+
key={cal.id}
|
|
526
|
+
className="rounded-md px-2 py-1.5 hover:bg-muted/50"
|
|
527
|
+
>
|
|
528
|
+
<div className="flex items-center justify-between gap-2">
|
|
529
|
+
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
530
|
+
<div
|
|
531
|
+
className="h-3 w-3 shrink-0 rounded-full"
|
|
532
|
+
style={{
|
|
533
|
+
backgroundColor: cal.color || '#4285f4',
|
|
534
|
+
}}
|
|
535
|
+
/>
|
|
536
|
+
<span
|
|
537
|
+
className="line-clamp-1 break-all text-sm"
|
|
538
|
+
title={cal.calendar_name}
|
|
539
|
+
>
|
|
540
|
+
{cal.calendar_name}
|
|
541
|
+
</span>
|
|
542
|
+
</div>
|
|
543
|
+
<Switch
|
|
544
|
+
checked={cal.is_enabled}
|
|
545
|
+
onCheckedChange={() =>
|
|
546
|
+
handleToggle(cal.id, cal.is_enabled, {
|
|
547
|
+
calendar_id: cal.calendar_id,
|
|
548
|
+
calendar_name: cal.calendar_name,
|
|
549
|
+
color: cal.color,
|
|
550
|
+
connectionExists: cal.connectionExists,
|
|
551
|
+
accountId: cal.accountId,
|
|
552
|
+
accessRole: cal.accessRole,
|
|
553
|
+
})
|
|
554
|
+
}
|
|
555
|
+
disabled={togglingIds.has(cal.id)}
|
|
556
|
+
/>
|
|
557
|
+
</div>
|
|
558
|
+
|
|
559
|
+
{cal.connectionExists && (
|
|
560
|
+
<div className="mt-2 grid grid-cols-3 gap-2 pl-5">
|
|
561
|
+
<label className="flex items-center justify-between gap-2 rounded-md bg-muted/40 px-2 py-1 text-muted-foreground text-xs">
|
|
562
|
+
<span>{t('import') || 'Import'}</span>
|
|
563
|
+
<Switch
|
|
564
|
+
checked={cal.syncInboundEnabled}
|
|
565
|
+
onCheckedChange={(checked) =>
|
|
566
|
+
updateConnectionSyncSettingsMutation.mutate(
|
|
567
|
+
{
|
|
568
|
+
id: cal.id,
|
|
569
|
+
syncInboundEnabled: checked,
|
|
570
|
+
}
|
|
571
|
+
)
|
|
572
|
+
}
|
|
573
|
+
disabled={syncControlsDisabled}
|
|
574
|
+
/>
|
|
575
|
+
</label>
|
|
576
|
+
<label className="flex items-center justify-between gap-2 rounded-md bg-muted/40 px-2 py-1 text-muted-foreground text-xs">
|
|
577
|
+
<span>{t('send') || 'Send'}</span>
|
|
578
|
+
<Switch
|
|
579
|
+
checked={cal.syncOutboundEnabled}
|
|
580
|
+
onCheckedChange={(checked) =>
|
|
581
|
+
updateConnectionSyncSettingsMutation.mutate(
|
|
582
|
+
{
|
|
583
|
+
id: cal.id,
|
|
584
|
+
syncOutboundEnabled: checked,
|
|
585
|
+
}
|
|
586
|
+
)
|
|
587
|
+
}
|
|
588
|
+
disabled={
|
|
589
|
+
syncControlsDisabled || !canWriteToProvider
|
|
590
|
+
}
|
|
591
|
+
/>
|
|
592
|
+
</label>
|
|
593
|
+
<label className="flex items-center justify-between gap-2 rounded-md bg-muted/40 px-2 py-1 text-muted-foreground text-xs">
|
|
594
|
+
<span>{t('deletes') || 'Deletes'}</span>
|
|
595
|
+
<Switch
|
|
596
|
+
checked={cal.syncDeleteEnabled}
|
|
597
|
+
onCheckedChange={(checked) =>
|
|
598
|
+
updateConnectionSyncSettingsMutation.mutate(
|
|
599
|
+
{
|
|
600
|
+
id: cal.id,
|
|
601
|
+
syncDeleteEnabled: checked,
|
|
602
|
+
}
|
|
603
|
+
)
|
|
604
|
+
}
|
|
605
|
+
disabled={syncControlsDisabled}
|
|
606
|
+
/>
|
|
607
|
+
</label>
|
|
608
|
+
</div>
|
|
609
|
+
)}
|
|
391
610
|
</div>
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
onCheckedChange={() =>
|
|
395
|
-
handleToggle(cal.id, cal.is_enabled, {
|
|
396
|
-
calendar_id: cal.calendar_id,
|
|
397
|
-
calendar_name: cal.calendar_name,
|
|
398
|
-
color: cal.color,
|
|
399
|
-
connectionExists: cal.connectionExists,
|
|
400
|
-
accountId: cal.accountId,
|
|
401
|
-
})
|
|
402
|
-
}
|
|
403
|
-
disabled={togglingIds.has(cal.id)}
|
|
404
|
-
/>
|
|
405
|
-
</div>
|
|
406
|
-
))
|
|
611
|
+
);
|
|
612
|
+
})
|
|
407
613
|
) : (
|
|
408
614
|
<p className="py-2 text-center text-muted-foreground text-xs">
|
|
409
615
|
{t('no_calendars_found')}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { createAdminClient } from '@tuturuuu/supabase/next/server';
|
|
2
|
+
import type { ExtendedWorkspaceTask } from '../../time-tracker/types';
|
|
3
|
+
|
|
4
|
+
interface LoadSmartSchedulingTasksOptions {
|
|
5
|
+
resolvedWsId: string;
|
|
6
|
+
userId: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface AccessibleTaskRow {
|
|
10
|
+
task_id: string;
|
|
11
|
+
task_name: string | null;
|
|
12
|
+
task_description: string | null;
|
|
13
|
+
task_creator_id: string | null;
|
|
14
|
+
task_list_id: string | null;
|
|
15
|
+
task_start_date: string | null;
|
|
16
|
+
task_end_date: string | null;
|
|
17
|
+
task_priority: ExtendedWorkspaceTask['priority'];
|
|
18
|
+
task_completed_at: string | null;
|
|
19
|
+
task_closed_at: string | null;
|
|
20
|
+
task_deleted_at: string | null;
|
|
21
|
+
task_estimation_points: number | null;
|
|
22
|
+
task_created_at: string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface TaskListWorkspaceRow {
|
|
26
|
+
id?: string | null;
|
|
27
|
+
workspace_boards?: {
|
|
28
|
+
ws_id?: string | null;
|
|
29
|
+
} | null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface TaskSchedulingRow {
|
|
33
|
+
task_id?: string | null;
|
|
34
|
+
total_duration: number | null;
|
|
35
|
+
is_splittable: boolean | null;
|
|
36
|
+
min_split_duration_minutes: number | null;
|
|
37
|
+
max_split_duration_minutes: number | null;
|
|
38
|
+
calendar_hours: ExtendedWorkspaceTask['calendar_hours'];
|
|
39
|
+
auto_schedule: boolean | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function loadSmartSchedulingTasks({
|
|
43
|
+
resolvedWsId,
|
|
44
|
+
userId,
|
|
45
|
+
}: LoadSmartSchedulingTasksOptions): Promise<ExtendedWorkspaceTask[]> {
|
|
46
|
+
const supabase = await createAdminClient({ noCookie: true });
|
|
47
|
+
const { data: rpcTasks } = await supabase.rpc('get_user_accessible_tasks', {
|
|
48
|
+
p_user_id: userId,
|
|
49
|
+
p_ws_id: resolvedWsId,
|
|
50
|
+
p_include_deleted: false,
|
|
51
|
+
p_list_statuses: ['not_started', 'active'],
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const tasksBase = ((rpcTasks ?? []) as AccessibleTaskRow[]).map((task) => ({
|
|
55
|
+
id: task.task_id,
|
|
56
|
+
name: task.task_name,
|
|
57
|
+
description: task.task_description,
|
|
58
|
+
creator_id: task.task_creator_id,
|
|
59
|
+
list_id: task.task_list_id,
|
|
60
|
+
start_date: task.task_start_date,
|
|
61
|
+
end_date: task.task_end_date,
|
|
62
|
+
due_date: task.task_end_date,
|
|
63
|
+
priority: task.task_priority,
|
|
64
|
+
completed_at: task.task_completed_at,
|
|
65
|
+
closed_at: task.task_closed_at,
|
|
66
|
+
deleted_at: task.task_deleted_at,
|
|
67
|
+
estimation_points: task.task_estimation_points,
|
|
68
|
+
created_at: task.task_created_at,
|
|
69
|
+
})) as ExtendedWorkspaceTask[];
|
|
70
|
+
|
|
71
|
+
const listIds = Array.from(
|
|
72
|
+
new Set(tasksBase.map((task) => task.list_id).filter(Boolean))
|
|
73
|
+
) as string[];
|
|
74
|
+
|
|
75
|
+
const wsIdByListId = new Map<string, string>();
|
|
76
|
+
if (listIds.length > 0) {
|
|
77
|
+
const { data: lists } = await supabase
|
|
78
|
+
.from('task_lists')
|
|
79
|
+
.select(
|
|
80
|
+
`
|
|
81
|
+
id,
|
|
82
|
+
workspace_boards!inner (
|
|
83
|
+
ws_id
|
|
84
|
+
)
|
|
85
|
+
`
|
|
86
|
+
)
|
|
87
|
+
.in('id', listIds);
|
|
88
|
+
|
|
89
|
+
(lists as TaskListWorkspaceRow[] | null)?.forEach((list) => {
|
|
90
|
+
const taskWsId = list.workspace_boards?.ws_id;
|
|
91
|
+
if (list.id && taskWsId) wsIdByListId.set(list.id, taskWsId);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const tasks = tasksBase.map((task) => ({
|
|
96
|
+
...task,
|
|
97
|
+
ws_id:
|
|
98
|
+
(task.list_id ? wsIdByListId.get(task.list_id) : undefined) ??
|
|
99
|
+
resolvedWsId,
|
|
100
|
+
})) as ExtendedWorkspaceTask[];
|
|
101
|
+
|
|
102
|
+
const taskIds = tasks.map((task) => task.id).filter(Boolean);
|
|
103
|
+
const settingsByTaskId = new Map<string, TaskSchedulingRow>();
|
|
104
|
+
|
|
105
|
+
if (taskIds.length > 0) {
|
|
106
|
+
const { data: schedulingRows } = await supabase
|
|
107
|
+
.from('task_user_scheduling_settings')
|
|
108
|
+
.select(
|
|
109
|
+
`
|
|
110
|
+
task_id,
|
|
111
|
+
total_duration,
|
|
112
|
+
is_splittable,
|
|
113
|
+
min_split_duration_minutes,
|
|
114
|
+
max_split_duration_minutes,
|
|
115
|
+
calendar_hours,
|
|
116
|
+
auto_schedule
|
|
117
|
+
`
|
|
118
|
+
)
|
|
119
|
+
.eq('user_id', userId)
|
|
120
|
+
.in('task_id', taskIds);
|
|
121
|
+
|
|
122
|
+
(schedulingRows as TaskSchedulingRow[] | null)?.forEach((row) => {
|
|
123
|
+
if (row.task_id) settingsByTaskId.set(row.task_id, row);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return tasks.map((task) => {
|
|
128
|
+
const settings = settingsByTaskId.get(task.id);
|
|
129
|
+
return settings
|
|
130
|
+
? ({
|
|
131
|
+
...task,
|
|
132
|
+
total_duration: settings.total_duration,
|
|
133
|
+
is_splittable: settings.is_splittable ?? false,
|
|
134
|
+
min_split_duration_minutes:
|
|
135
|
+
settings.min_split_duration_minutes ?? null,
|
|
136
|
+
max_split_duration_minutes:
|
|
137
|
+
settings.max_split_duration_minutes ?? null,
|
|
138
|
+
calendar_hours: settings.calendar_hours ?? null,
|
|
139
|
+
auto_schedule: settings.auto_schedule ?? false,
|
|
140
|
+
} as ExtendedWorkspaceTask)
|
|
141
|
+
: task;
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
} from '@tuturuuu/icons';
|
|
24
24
|
import {
|
|
25
25
|
listWorkspaceTaskLists,
|
|
26
|
+
listWorkspaceTasks,
|
|
26
27
|
updateWorkspaceTask,
|
|
27
28
|
} from '@tuturuuu/internal-api/tasks';
|
|
28
29
|
import type { TaskPriority } from '@tuturuuu/types/primitives/Priority';
|
|
@@ -40,7 +41,6 @@ import ActionsDropdown from './actions-dropdown';
|
|
|
40
41
|
import PriorityDropdown from './priority-dropdown';
|
|
41
42
|
import { QuickTaskDialog } from './quick-task-dialog';
|
|
42
43
|
import { SchedulingDialog } from './scheduling-dialog';
|
|
43
|
-
import { getAssignedTasks } from './task-fetcher';
|
|
44
44
|
|
|
45
45
|
// Priority labels (matching task-properties-section.tsx)
|
|
46
46
|
const PRIORITY_LABELS: Record<TaskPriority, string> = {
|
|
@@ -356,8 +356,15 @@ export default function PriorityView({
|
|
|
356
356
|
setSearchError(null);
|
|
357
357
|
|
|
358
358
|
try {
|
|
359
|
-
const results = await
|
|
360
|
-
|
|
359
|
+
const results = await listWorkspaceTasks(wsId, {
|
|
360
|
+
assigneeIds: [assigneeId],
|
|
361
|
+
closed: 'exclude',
|
|
362
|
+
completed: 'exclude',
|
|
363
|
+
includeArchivedBoards: true,
|
|
364
|
+
limit: 50,
|
|
365
|
+
q: searchQuery.trim(),
|
|
366
|
+
});
|
|
367
|
+
setSearchResults(results.tasks as ExtendedWorkspaceTask[]);
|
|
361
368
|
} catch (error) {
|
|
362
369
|
console.error('Error searching tasks:', error);
|
|
363
370
|
setSearchError('Failed to search tasks');
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createAdminClient } from '@tuturuuu/supabase/next/server';
|
|
2
1
|
import type { ExtendedWorkspaceTask } from '../../time-tracker/types';
|
|
3
2
|
import { CalendarSidebar } from './sidebar';
|
|
4
3
|
|
|
@@ -6,126 +5,15 @@ interface TasksSidebarProps {
|
|
|
6
5
|
resolvedWsId: string;
|
|
7
6
|
locale: string;
|
|
8
7
|
userId: string;
|
|
8
|
+
tasks?: ExtendedWorkspaceTask[];
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export default
|
|
11
|
+
export default function TasksSidebar({
|
|
12
12
|
resolvedWsId,
|
|
13
13
|
locale,
|
|
14
14
|
userId,
|
|
15
|
+
tasks = [],
|
|
15
16
|
}: TasksSidebarProps) {
|
|
16
|
-
// Use the same RPC as the tasks page to get accessible tasks
|
|
17
|
-
const supabase = await createAdminClient({ noCookie: true });
|
|
18
|
-
const { data: rpcTasks } = await supabase.rpc('get_user_accessible_tasks', {
|
|
19
|
-
p_user_id: userId,
|
|
20
|
-
p_ws_id: resolvedWsId,
|
|
21
|
-
p_include_deleted: false,
|
|
22
|
-
p_list_statuses: ['not_started', 'active'],
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
// Map RPC results to match expected structure (same as my-tasks-data-loader.tsx)
|
|
26
|
-
const tasksBase = (rpcTasks?.map((task) => ({
|
|
27
|
-
id: task.task_id,
|
|
28
|
-
name: task.task_name,
|
|
29
|
-
description: task.task_description,
|
|
30
|
-
creator_id: task.task_creator_id,
|
|
31
|
-
list_id: task.task_list_id,
|
|
32
|
-
start_date: task.task_start_date,
|
|
33
|
-
end_date: task.task_end_date,
|
|
34
|
-
due_date: task.task_end_date, // Map end_date to due_date for display
|
|
35
|
-
priority: task.task_priority,
|
|
36
|
-
completed_at: task.task_completed_at,
|
|
37
|
-
closed_at: task.task_closed_at,
|
|
38
|
-
deleted_at: task.task_deleted_at,
|
|
39
|
-
estimation_points: task.task_estimation_points,
|
|
40
|
-
created_at: task.task_created_at,
|
|
41
|
-
// Scheduling fields are per-user now. These legacy RPC fields may not exist after migration.
|
|
42
|
-
})) || []) as ExtendedWorkspaceTask[];
|
|
43
|
-
|
|
44
|
-
// Enrich tasks with the *actual* workspace UUID (ws_id) via list -> board relation.
|
|
45
|
-
// This is critical for personal workspace views where tasks may belong to other workspaces.
|
|
46
|
-
const listIds = Array.from(
|
|
47
|
-
new Set(tasksBase.map((t) => t.list_id).filter(Boolean))
|
|
48
|
-
) as string[];
|
|
49
|
-
|
|
50
|
-
const wsIdByListId = new Map<string, string>();
|
|
51
|
-
if (listIds.length > 0) {
|
|
52
|
-
const { data: lists } = await supabase
|
|
53
|
-
.from('task_lists')
|
|
54
|
-
.select(
|
|
55
|
-
`
|
|
56
|
-
id,
|
|
57
|
-
workspace_boards!inner (
|
|
58
|
-
ws_id
|
|
59
|
-
)
|
|
60
|
-
`
|
|
61
|
-
)
|
|
62
|
-
.in('id', listIds);
|
|
63
|
-
|
|
64
|
-
(lists as any[] | null)?.forEach((l) => {
|
|
65
|
-
const resolvedTaskWsId = l?.workspace_boards?.ws_id;
|
|
66
|
-
if (l?.id && resolvedTaskWsId) wsIdByListId.set(l.id, resolvedTaskWsId);
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const tasks = tasksBase.map((t) => ({
|
|
71
|
-
...t,
|
|
72
|
-
ws_id:
|
|
73
|
-
(t.list_id ? wsIdByListId.get(t.list_id) : undefined) ??
|
|
74
|
-
// Fallback: if a task is list-less, treat it as current workspace-scoped.
|
|
75
|
-
resolvedWsId,
|
|
76
|
-
})) as ExtendedWorkspaceTask[];
|
|
77
|
-
|
|
78
|
-
// Merge per-user scheduling settings so the calendar UI can still show duration/hour type.
|
|
79
|
-
const taskIds = tasks.map((t) => t.id).filter(Boolean);
|
|
80
|
-
const settingsByTaskId = new Map<
|
|
81
|
-
string,
|
|
82
|
-
{
|
|
83
|
-
total_duration: number | null;
|
|
84
|
-
is_splittable: boolean | null;
|
|
85
|
-
min_split_duration_minutes: number | null;
|
|
86
|
-
max_split_duration_minutes: number | null;
|
|
87
|
-
calendar_hours: any;
|
|
88
|
-
auto_schedule: boolean | null;
|
|
89
|
-
}
|
|
90
|
-
>();
|
|
91
|
-
|
|
92
|
-
if (taskIds.length > 0) {
|
|
93
|
-
const { data: schedulingRows } = await (supabase as any)
|
|
94
|
-
.from('task_user_scheduling_settings')
|
|
95
|
-
.select(
|
|
96
|
-
`
|
|
97
|
-
task_id,
|
|
98
|
-
total_duration,
|
|
99
|
-
is_splittable,
|
|
100
|
-
min_split_duration_minutes,
|
|
101
|
-
max_split_duration_minutes,
|
|
102
|
-
calendar_hours,
|
|
103
|
-
auto_schedule
|
|
104
|
-
`
|
|
105
|
-
)
|
|
106
|
-
.eq('user_id', userId)
|
|
107
|
-
.in('task_id', taskIds);
|
|
108
|
-
|
|
109
|
-
(schedulingRows as any[] | null)?.forEach((r) => {
|
|
110
|
-
if (r?.task_id) settingsByTaskId.set(r.task_id, r);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const tasksWithScheduling = tasks.map((t) => {
|
|
115
|
-
const s = settingsByTaskId.get(t.id);
|
|
116
|
-
return s
|
|
117
|
-
? ({
|
|
118
|
-
...t,
|
|
119
|
-
total_duration: s.total_duration,
|
|
120
|
-
is_splittable: s.is_splittable ?? false,
|
|
121
|
-
min_split_duration_minutes: s.min_split_duration_minutes ?? null,
|
|
122
|
-
max_split_duration_minutes: s.max_split_duration_minutes ?? null,
|
|
123
|
-
calendar_hours: s.calendar_hours ?? null,
|
|
124
|
-
auto_schedule: s.auto_schedule ?? false,
|
|
125
|
-
} as ExtendedWorkspaceTask)
|
|
126
|
-
: t;
|
|
127
|
-
});
|
|
128
|
-
|
|
129
17
|
// Personal workspace = workspace ID matches user ID (no need for auto-assignment)
|
|
130
18
|
const isPersonalWorkspace = resolvedWsId === userId;
|
|
131
19
|
|
|
@@ -135,7 +23,7 @@ export default async function TasksSidebar({
|
|
|
135
23
|
// Always pass the resolved workspace UUID, not the route slug (e.g. "personal").
|
|
136
24
|
wsId={resolvedWsId}
|
|
137
25
|
assigneeId={userId}
|
|
138
|
-
tasks={
|
|
26
|
+
tasks={tasks}
|
|
139
27
|
locale={locale}
|
|
140
28
|
isPersonalWorkspace={isPersonalWorkspace}
|
|
141
29
|
/>
|