@tuturuuu/ui 0.16.0 → 0.18.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 +38 -0
- package/biome.json +1 -1
- package/package.json +78 -119
- package/src/components/ui/calendar-app/calendar-client-page.tsx +12 -3
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +38 -28
- package/src/components/ui/custom/notification-popover-client.tsx +3 -3
- package/src/components/ui/storefront/browse-panel.tsx +161 -0
- package/src/components/ui/storefront/bundle-selection-dialog.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +1 -1
- package/src/components/ui/storefront/hero-panel.tsx +35 -30
- package/src/components/ui/storefront/listing-card.tsx +10 -5
- package/src/components/ui/storefront/merch-sections.tsx +7 -5
- package/src/components/ui/storefront/product-detail.tsx +79 -6
- package/src/components/ui/storefront/product-dialog.tsx +5 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +72 -0
- package/src/components/ui/storefront/storefront-surface.tsx +67 -63
- package/src/components/ui/storefront/types.ts +28 -0
- package/src/components/ui/storefront/utils.ts +4 -5
- package/src/components/ui/tasks/progress/activity-heatmap.tsx +256 -0
- package/src/components/ui/tasks/progress/breakdown-bars.tsx +79 -0
- package/src/components/ui/tasks/progress/cumulative-progress-chart.tsx +185 -0
- package/src/components/ui/tasks/progress/daily-bar-chart.tsx +97 -0
- package/src/components/ui/tasks/progress/habit-gauge.tsx +92 -0
- package/src/components/ui/tasks/progress/mini-sparkline.tsx +56 -0
- package/src/components/ui/tasks/progress/weekday-distribution-chart.tsx +72 -0
- package/src/components/ui/text-editor/editor.tsx +7 -1
- package/src/components/ui/text-editor/extensions.ts +4 -1
- package/src/components/ui/text-editor/mention-extension.tsx +33 -121
- package/src/globals.css +1 -0
- package/src/lib/workspace-actions.ts +15 -4
- package/src/components/ui/calendar-app/components/actions-dropdown.tsx +0 -269
- package/src/components/ui/calendar-app/components/auto-schedule-comprehensive-dialog.tsx +0 -220
- package/src/components/ui/calendar-app/components/calendar-header-actions.tsx +0 -67
- package/src/components/ui/calendar-app/components/experimental-feature-dialog.tsx +0 -56
- package/src/components/ui/calendar-app/components/habits-panel.tsx +0 -335
- package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +0 -143
- package/src/components/ui/calendar-app/components/priority-dropdown.tsx +0 -113
- package/src/components/ui/calendar-app/components/priority-view.tsx +0 -954
- package/src/components/ui/calendar-app/components/quick-task-dialog.tsx +0 -335
- package/src/components/ui/calendar-app/components/quick-task-timer.tsx +0 -112
- package/src/components/ui/calendar-app/components/scheduling-dialog.tsx +0 -664
- package/src/components/ui/calendar-app/components/sidebar/calendar-sidebar.tsx +0 -128
- package/src/components/ui/calendar-app/components/sidebar/index.tsx +0 -1
- package/src/components/ui/calendar-app/components/smart-schedule-button.tsx +0 -117
- package/src/components/ui/calendar-app/components/smart-schedule-preview-dialog.tsx +0 -845
- package/src/components/ui/calendar-app/components/smart-schedule-preview-panel.tsx +0 -941
- package/src/components/ui/calendar-app/components/task-fetcher.ts +0 -51
- package/src/components/ui/calendar-app/components/task-form.tsx +0 -132
- package/src/components/ui/calendar-app/components/task-list-form.tsx +0 -84
- package/src/components/ui/calendar-app/components/task-scheduler-panel.tsx +0 -494
- package/src/components/ui/calendar-app/components/tasks-sidebar-content.tsx +0 -232
- package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +0 -31
- package/src/components/ui/calendar-app/components/time-tracker/components/active-session-card.tsx +0 -113
- package/src/components/ui/calendar-app/components/time-tracker/components/index.ts +0 -3
- package/src/components/ui/calendar-app/components/time-tracker/components/session-card.tsx +0 -153
- package/src/components/ui/calendar-app/components/time-tracker/components/stats-cards.tsx +0 -58
- package/src/components/ui/calendar-app/components/time-tracker/dialogs/create-task-dialog.tsx +0 -190
- package/src/components/ui/calendar-app/components/time-tracker/dialogs/delete-session-dialog.tsx +0 -52
- package/src/components/ui/calendar-app/components/time-tracker/dialogs/edit-session-dialog.tsx +0 -173
- package/src/components/ui/calendar-app/components/time-tracker/dialogs/index.ts +0 -3
- package/src/components/ui/calendar-app/components/time-tracker/hooks/index.ts +0 -2
- package/src/components/ui/calendar-app/components/time-tracker/hooks/use-sessions.ts +0 -191
- package/src/components/ui/calendar-app/components/time-tracker/hooks/use-time-tracker.ts +0 -408
- package/src/components/ui/calendar-app/components/time-tracker/index.tsx +0 -907
- package/src/components/ui/calendar-app/components/time-tracker/tabs/history-tab.tsx +0 -28
- package/src/components/ui/calendar-app/components/time-tracker/tabs/index.ts +0 -2
- package/src/components/ui/calendar-app/components/time-tracker/tabs/recent-sessions-tab.tsx +0 -173
- package/src/components/ui/calendar-app/components/time-tracker/time-tracker-trigger.tsx +0 -50
- package/src/components/ui/calendar-app/components/time-tracker/utils/category-colors.ts +0 -38
- package/src/components/ui/calendar-app/components/time-tracker/utils/format-time.ts +0 -26
- package/src/components/ui/calendar-app/components/time-tracker/utils/index.ts +0 -2
- package/src/components/ui/calendar-app/components/time-tracker.tsx +0 -2040
- package/src/components/ui/custom/settings/task-settings.tsx +0 -543
- package/src/components/ui/custom/settings/task-sound-settings.test.tsx +0 -146
- package/src/components/ui/text-editor/__tests__/task-mention-chip.test.tsx +0 -476
- package/src/components/ui/text-editor/task-mention-chip.tsx +0 -1397
- package/src/components/ui/text-editor/task-mention-resolution.ts +0 -235
- package/src/components/ui/text-editor/task-summary-popover.tsx +0 -360
- package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +0 -286
- package/src/components/ui/tu-do/boards/__tests__/quick-create-board-dialog.test.tsx +0 -52
- package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +0 -142
- package/src/components/ui/tu-do/boards/__tests__/workspace-projects-client-page.test.tsx +0 -169
- package/src/components/ui/tu-do/boards/analytics/GanttChart.tsx +0 -419
- package/src/components/ui/tu-do/boards/analytics/GanttControls.tsx +0 -123
- package/src/components/ui/tu-do/boards/analytics/GanttHeader.tsx +0 -48
- package/src/components/ui/tu-do/boards/analytics/GanttTimeline.tsx +0 -355
- package/src/components/ui/tu-do/boards/analytics/StatusDistribution.tsx +0 -130
- package/src/components/ui/tu-do/boards/analytics/TaskCreationAnalytics.tsx +0 -285
- package/src/components/ui/tu-do/boards/analytics/TaskDetailCard.tsx +0 -270
- package/src/components/ui/tu-do/boards/analytics/TaskGroup.tsx +0 -282
- package/src/components/ui/tu-do/boards/analytics/TaskWorkflowAnalytics.tsx +0 -280
- package/src/components/ui/tu-do/boards/board-public-link-section.tsx +0 -231
- package/src/components/ui/tu-do/boards/board-selector.tsx +0 -301
- package/src/components/ui/tu-do/boards/board-share-dialog.tsx +0 -69
- package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +0 -351
- package/src/components/ui/tu-do/boards/boardId/__tests__/list-actions.test.tsx +0 -199
- package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +0 -127
- package/src/components/ui/tu-do/boards/boardId/board-column-initial-load.test.ts +0 -38
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +0 -893
- package/src/components/ui/tu-do/boards/boardId/board-text-utils.test.ts +0 -24
- package/src/components/ui/tu-do/boards/boardId/board-text-utils.ts +0 -11
- package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +0 -543
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-external-workspaces.test.tsx +0 -392
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-move.test.tsx +0 -252
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.test.tsx +0 -57
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.tsx +0 -106
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-menu.tsx +0 -689
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-custom-date-dialog.tsx +0 -82
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-delete-dialog.tsx +0 -58
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +0 -418
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +0 -662
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-assignees.ts +0 -375
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-labels.ts +0 -278
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-projects.ts +0 -297
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations.ts +0 -12
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +0 -462
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-i18n.ts +0 -524
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-types.ts +0 -34
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-utils.ts +0 -377
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +0 -348
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-personal-external-move.ts +0 -132
- package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +0 -90
- package/src/components/ui/tu-do/boards/boardId/kanban/data/use-applied-sets.ts +0 -79
- package/src/components/ui/tu-do/boards/boardId/kanban/data/use-bulk-resources.ts +0 -112
- package/src/components/ui/tu-do/boards/boardId/kanban/data/use-filtered-resources.ts +0 -54
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +0 -80
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/auto-scroll.test.ts +0 -118
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/auto-scroll.ts +0 -160
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +0 -65
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/drag-preview.tsx +0 -117
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/kanban-sort-helpers.ts +0 -76
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +0 -367
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-geometry.test.ts +0 -67
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-geometry.ts +0 -94
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +0 -150
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-pending.test.ts +0 -37
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-pending.ts +0 -42
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-preview.test.ts +0 -318
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-preview.ts +0 -260
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-types.ts +0 -39
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +0 -47
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +0 -859
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +0 -1659
- package/src/components/ui/tu-do/boards/boardId/kanban/kanban-column-collapse.ts +0 -16
- package/src/components/ui/tu-do/boards/boardId/kanban/kanban-constants.ts +0 -43
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +0 -380
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +0 -204
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +0 -61
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +0 -54
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +0 -251
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +0 -27
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +0 -58
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +0 -238
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +0 -143
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +0 -65
- package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +0 -234
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-column-width.ts +0 -30
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +0 -1077
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +0 -447
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +0 -703
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-tasks.test.ts +0 -267
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-tasks.ts +0 -112
- package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +0 -113
- package/src/components/ui/tu-do/boards/boardId/kanban/selection/use-keyboard-shortcuts.ts +0 -137
- package/src/components/ui/tu-do/boards/boardId/kanban/selection/use-multi-select.ts +0 -119
- package/src/components/ui/tu-do/boards/boardId/kanban.tsx +0 -631
- package/src/components/ui/tu-do/boards/boardId/list-actions.tsx +0 -522
- package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-estimation-menu.test.tsx +0 -261
- package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +0 -708
- package/src/components/ui/tu-do/boards/boardId/menus/index.ts +0 -12
- package/src/components/ui/tu-do/boards/boardId/menus/task-assignees-menu.tsx +0 -176
- package/src/components/ui/tu-do/boards/boardId/menus/task-blocking-menu.tsx +0 -304
- package/src/components/ui/tu-do/boards/boardId/menus/task-due-date-menu.tsx +0 -201
- package/src/components/ui/tu-do/boards/boardId/menus/task-estimation-menu.tsx +0 -103
- package/src/components/ui/tu-do/boards/boardId/menus/task-labels-menu.tsx +0 -134
- package/src/components/ui/tu-do/boards/boardId/menus/task-move-menu.tsx +0 -66
- package/src/components/ui/tu-do/boards/boardId/menus/task-parent-menu.tsx +0 -228
- package/src/components/ui/tu-do/boards/boardId/menus/task-picker-popover.tsx +0 -300
- package/src/components/ui/tu-do/boards/boardId/menus/task-priority-menu.tsx +0 -178
- package/src/components/ui/tu-do/boards/boardId/menus/task-projects-menu.tsx +0 -143
- package/src/components/ui/tu-do/boards/boardId/menus/task-related-menu.tsx +0 -242
- package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +0 -465
- package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-utils.ts +0 -109
- package/src/components/ui/tu-do/boards/boardId/status-grouped-board.tsx +0 -533
- package/src/components/ui/tu-do/boards/boardId/status-section.tsx +0 -274
- package/src/components/ui/tu-do/boards/boardId/task-actions.tsx +0 -675
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +0 -209
- package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +0 -109
- package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardCheckbox.tsx +0 -50
- package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardDates.tsx +0 -123
- package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardHeader.tsx +0 -97
- package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardMetadata.tsx +0 -152
- package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +0 -118
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-archive-visibility.test.ts +0 -44
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-archive-visibility.ts +0 -17
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-checkbox-style.ts +0 -39
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.test.ts +0 -104
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +0 -123
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.test.ts +0 -31
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.ts +0 -9
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.test.tsx +0 -124
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.tsx +0 -88
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-label-options.ts +0 -24
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +0 -166
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +0 -148
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-resource-context.test.ts +0 -113
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-resource-context.ts +0 -50
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-visibility.test.ts +0 -41
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-visibility.ts +0 -14
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +0 -2889
- package/src/components/ui/tu-do/boards/boardId/task-card/task-scheduling-badge.tsx +0 -174
- package/src/components/ui/tu-do/boards/boardId/task-dialogs/BoardEstimationConfigDialog.tsx +0 -499
- package/src/components/ui/tu-do/boards/boardId/task-dialogs/TaskCustomDateDialog.tsx +0 -109
- package/src/components/ui/tu-do/boards/boardId/task-dialogs/TaskDeleteDialog.tsx +0 -96
- package/src/components/ui/tu-do/boards/boardId/task-dialogs/TaskNewLabelDialog.tsx +0 -178
- package/src/components/ui/tu-do/boards/boardId/task-dialogs/TaskNewProjectDialog.tsx +0 -127
- package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +0 -152
- package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +0 -996
- package/src/components/ui/tu-do/boards/boardId/task-form.tsx +0 -498
- package/src/components/ui/tu-do/boards/boardId/task-list-drag-preview.test.ts +0 -101
- package/src/components/ui/tu-do/boards/boardId/task-list-form.tsx +0 -91
- package/src/components/ui/tu-do/boards/boardId/task-list.tsx +0 -535
- package/src/components/ui/tu-do/boards/boardId/task-parent-badge-state.test.ts +0 -64
- package/src/components/ui/tu-do/boards/boardId/task-parent-badge-state.ts +0 -31
- package/src/components/ui/tu-do/boards/boardId/task.tsx +0 -2
- package/src/components/ui/tu-do/boards/boardId/timeline/task-edit-dialog.tsx +0 -109
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-display.ts +0 -103
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +0 -381
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +0 -339
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +0 -356
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +0 -169
- package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +0 -517
- package/src/components/ui/tu-do/boards/boardId/timeline-board-open-task.test.tsx +0 -164
- package/src/components/ui/tu-do/boards/boardId/timeline-board.test.tsx +0 -217
- package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +0 -834
- package/src/components/ui/tu-do/boards/boards-list-skeleton.tsx +0 -62
- package/src/components/ui/tu-do/boards/columns.tsx +0 -159
- package/src/components/ui/tu-do/boards/copy-board-dialog.tsx +0 -156
- package/src/components/ui/tu-do/boards/enhanced-boards-view.tsx +0 -347
- package/src/components/ui/tu-do/boards/form.tsx +0 -264
- package/src/components/ui/tu-do/boards/quick-create-board-dialog.tsx +0 -75
- package/src/components/ui/tu-do/boards/row-actions.tsx +0 -397
- package/src/components/ui/tu-do/boards/share-section.tsx +0 -100
- package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +0 -192
- package/src/components/ui/tu-do/boards/workspace-projects-page.tsx +0 -155
- package/src/components/ui/tu-do/cycles/task-cycles-client.tsx +0 -753
- package/src/components/ui/tu-do/cycles/task-cycles-page.tsx +0 -103
- package/src/components/ui/tu-do/drafts/draft-card.tsx +0 -204
- package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +0 -195
- package/src/components/ui/tu-do/drafts/drafts-page.tsx +0 -122
- package/src/components/ui/tu-do/drafts/task-drafts-page.tsx +0 -90
- package/src/components/ui/tu-do/estimates/client.tsx +0 -292
- package/src/components/ui/tu-do/estimates/edit-estimation-dialog.tsx +0 -374
- package/src/components/ui/tu-do/estimates/task-estimates-page.tsx +0 -57
- package/src/components/ui/tu-do/estimates/use-task-estimates.ts +0 -324
- package/src/components/ui/tu-do/habits/client.tsx +0 -263
- package/src/components/ui/tu-do/habits/habit-card.tsx +0 -183
- package/src/components/ui/tu-do/habits/habit-form-dialog.tsx +0 -701
- package/src/components/ui/tu-do/habits/habits-page.tsx +0 -27
- package/src/components/ui/tu-do/hooks/__tests__/useDraftPersistence.test.ts +0 -494
- package/src/components/ui/tu-do/hooks/__tests__/useTaskDialog.test.tsx +0 -127
- package/src/components/ui/tu-do/hooks/__tests__/useTaskDialogState.test.ts +0 -217
- package/src/components/ui/tu-do/hooks/__tests__/useTaskLabelManagement.test.tsx +0 -572
- package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +0 -144
- package/src/components/ui/tu-do/hooks/useDraftPersistence.ts +0 -199
- package/src/components/ui/tu-do/hooks/useTaskCardRelationships.ts +0 -340
- package/src/components/ui/tu-do/hooks/useTaskDialog.ts +0 -122
- package/src/components/ui/tu-do/hooks/useTaskDialogState.ts +0 -145
- package/src/components/ui/tu-do/hooks/useTaskFormState.ts +0 -206
- package/src/components/ui/tu-do/hooks/useTaskKeyboardShortcuts.ts +0 -58
- package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +0 -425
- package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +0 -454
- package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +0 -889
- package/src/components/ui/tu-do/initiatives/task-initiatives-page.tsx +0 -116
- package/src/components/ui/tu-do/labels/client.tsx +0 -201
- package/src/components/ui/tu-do/labels/components/delete-label-dialog.tsx +0 -57
- package/src/components/ui/tu-do/labels/components/label-card.tsx +0 -86
- package/src/components/ui/tu-do/labels/components/label-dialog.tsx +0 -259
- package/src/components/ui/tu-do/labels/components/label-list.tsx +0 -83
- package/src/components/ui/tu-do/labels/hooks/use-task-labels.ts +0 -170
- package/src/components/ui/tu-do/labels/task-labels-page.tsx +0 -53
- package/src/components/ui/tu-do/labels/types.ts +0 -6
- package/src/components/ui/tu-do/logs/columns.tsx +0 -755
- package/src/components/ui/tu-do/logs/logs-client.tsx +0 -1006
- package/src/components/ui/tu-do/logs/logs-timeline.tsx +0 -2483
- package/src/components/ui/tu-do/logs/task-logs-page.tsx +0 -59
- package/src/components/ui/tu-do/my-tasks/__tests__/my-tasks-content.test.tsx +0 -408
- package/src/components/ui/tu-do/my-tasks/__tests__/use-my-tasks-query.test.ts +0 -469
- package/src/components/ui/tu-do/my-tasks/__tests__/use-my-tasks-state.test.ts +0 -1017
- package/src/components/ui/tu-do/my-tasks/__tests__/use-task-context-actions.test.ts +0 -584
- package/src/components/ui/tu-do/my-tasks/ai-credit-indicator.tsx +0 -143
- package/src/components/ui/tu-do/my-tasks/board-selector-dialog.tsx +0 -239
- package/src/components/ui/tu-do/my-tasks/command-bar.tsx +0 -1351
- package/src/components/ui/tu-do/my-tasks/label-project-filter.tsx +0 -194
- package/src/components/ui/tu-do/my-tasks/my-task-context-menu.tsx +0 -356
- package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +0 -328
- package/src/components/ui/tu-do/my-tasks/my-tasks-data-loader.tsx +0 -17
- package/src/components/ui/tu-do/my-tasks/my-tasks-filters.tsx +0 -258
- package/src/components/ui/tu-do/my-tasks/my-tasks-header.tsx +0 -121
- package/src/components/ui/tu-do/my-tasks/my-tasks-page.tsx +0 -40
- package/src/components/ui/tu-do/my-tasks/personal-placement-dialog.tsx +0 -188
- package/src/components/ui/tu-do/my-tasks/task-filter.tsx +0 -235
- package/src/components/ui/tu-do/my-tasks/task-list-with-completion.tsx +0 -832
- package/src/components/ui/tu-do/my-tasks/task-list.tsx +0 -258
- package/src/components/ui/tu-do/my-tasks/task-preview-dialog.tsx +0 -1597
- package/src/components/ui/tu-do/my-tasks/task-section.tsx +0 -261
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-query.ts +0 -149
- package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +0 -1037
- package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +0 -474
- package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +0 -118
- package/src/components/ui/tu-do/notes/note-list.tsx +0 -799
- package/src/components/ui/tu-do/notes/notes-content.tsx +0 -178
- package/src/components/ui/tu-do/notes/notes-page.tsx +0 -27
- package/src/components/ui/tu-do/progress/task-progress-import-panel.tsx +0 -60
- package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +0 -156
- package/src/components/ui/tu-do/progress/task-progress-page.tsx +0 -394
- package/src/components/ui/tu-do/progress/task-progress-panels.tsx +0 -303
- package/src/components/ui/tu-do/projects/components/index.ts +0 -11
- package/src/components/ui/tu-do/projects/components/project-actions-menu.tsx +0 -66
- package/src/components/ui/tu-do/projects/components/project-badges.tsx +0 -84
- package/src/components/ui/tu-do/projects/components/project-filter-menu.tsx +0 -170
- package/src/components/ui/tu-do/projects/components/project-grid-card.tsx +0 -198
- package/src/components/ui/tu-do/projects/components/project-list-item.tsx +0 -195
- package/src/components/ui/tu-do/projects/components/project-metrics.tsx +0 -87
- package/src/components/ui/tu-do/projects/components/project-progress-meter.tsx +0 -33
- package/src/components/ui/tu-do/projects/components/project-sort-menu.tsx +0 -71
- package/src/components/ui/tu-do/projects/components/projects-empty-state.tsx +0 -38
- package/src/components/ui/tu-do/projects/components/projects-loading-state.tsx +0 -26
- package/src/components/ui/tu-do/projects/components/projects-toolbar.tsx +0 -179
- package/src/components/ui/tu-do/projects/dialogs/create-project-dialog.tsx +0 -117
- package/src/components/ui/tu-do/projects/dialogs/edit-project-dialog.tsx +0 -125
- package/src/components/ui/tu-do/projects/dialogs/index.ts +0 -3
- package/src/components/ui/tu-do/projects/dialogs/manage-tasks-dialog.tsx +0 -205
- package/src/components/ui/tu-do/projects/hooks/index.ts +0 -2
- package/src/components/ui/tu-do/projects/hooks/use-project-filters.ts +0 -248
- package/src/components/ui/tu-do/projects/hooks/use-task-projects.ts +0 -238
- package/src/components/ui/tu-do/projects/projectId/components/__tests__/tasks-tab-layout.test.tsx +0 -169
- package/src/components/ui/tu-do/projects/projectId/components/documents-tab.tsx +0 -90
- package/src/components/ui/tu-do/projects/projectId/components/index.ts +0 -9
- package/src/components/ui/tu-do/projects/projectId/components/overview/description-card.tsx +0 -77
- package/src/components/ui/tu-do/projects/projectId/components/overview/linked-documents-card.tsx +0 -81
- package/src/components/ui/tu-do/projects/projectId/components/overview/linked-tasks-card.tsx +0 -85
- package/src/components/ui/tu-do/projects/projectId/components/overview/updates-card.tsx +0 -85
- package/src/components/ui/tu-do/projects/projectId/components/overview-tab.tsx +0 -35
- package/src/components/ui/tu-do/projects/projectId/components/project-configuration.tsx +0 -229
- package/src/components/ui/tu-do/projects/projectId/components/project-header.tsx +0 -115
- package/src/components/ui/tu-do/projects/projectId/components/project-lead-selector.tsx +0 -55
- package/src/components/ui/tu-do/projects/projectId/components/project-overview-context.tsx +0 -82
- package/src/components/ui/tu-do/projects/projectId/components/project-sidebar.tsx +0 -246
- package/src/components/ui/tu-do/projects/projectId/components/tasks-tab.tsx +0 -266
- package/src/components/ui/tu-do/projects/projectId/components/update-card.tsx +0 -162
- package/src/components/ui/tu-do/projects/projectId/components/updates-tab.tsx +0 -129
- package/src/components/ui/tu-do/projects/projectId/dialogs/index.ts +0 -1
- package/src/components/ui/tu-do/projects/projectId/dialogs/link-task-dialog.tsx +0 -114
- package/src/components/ui/tu-do/projects/projectId/hooks/index.ts +0 -4
- package/src/components/ui/tu-do/projects/projectId/hooks/use-animation-variants.ts +0 -68
- package/src/components/ui/tu-do/projects/projectId/hooks/use-project-form.ts +0 -214
- package/src/components/ui/tu-do/projects/projectId/hooks/use-project-updates.ts +0 -209
- package/src/components/ui/tu-do/projects/projectId/hooks/use-task-linking.ts +0 -101
- package/src/components/ui/tu-do/projects/projectId/task-project-detail-page-client.tsx +0 -93
- package/src/components/ui/tu-do/projects/projectId/task-project-detail-page.tsx +0 -78
- package/src/components/ui/tu-do/projects/projectId/task-project-detail.tsx +0 -367
- package/src/components/ui/tu-do/projects/projectId/types.ts +0 -51
- package/src/components/ui/tu-do/projects/task-projects-client.tsx +0 -270
- package/src/components/ui/tu-do/projects/task-projects-page.tsx +0 -97
- package/src/components/ui/tu-do/projects/types.ts +0 -62
- package/src/components/ui/tu-do/providers/__tests__/task-dialog-provider.test.tsx +0 -616
- package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +0 -923
- package/src/components/ui/tu-do/providers/workspace-presence-provider.tsx +0 -88
- package/src/components/ui/tu-do/shared/AccessibleButton.tsx +0 -79
- package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +0 -143
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +0 -358
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +0 -598
- package/src/components/ui/tu-do/shared/__tests__/board-query-cache.test.ts +0 -219
- package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +0 -374
- package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +0 -1288
- package/src/components/ui/tu-do/shared/__tests__/create-list-dialog.test.tsx +0 -118
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +0 -99
- package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +0 -147
- package/src/components/ui/tu-do/shared/__tests__/task-detail-page.test.tsx +0 -170
- package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +0 -945
- package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +0 -101
- package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +0 -645
- package/src/components/ui/tu-do/shared/assignee-select.tsx +0 -592
- package/src/components/ui/tu-do/shared/board-broadcast-context.tsx +0 -45
- package/src/components/ui/tu-do/shared/board-client.tsx +0 -283
- package/src/components/ui/tu-do/shared/board-config-storage.ts +0 -94
- package/src/components/ui/tu-do/shared/board-header.tsx +0 -803
- package/src/components/ui/tu-do/shared/board-layout-settings.tsx +0 -1134
- package/src/components/ui/tu-do/shared/board-query-cache.ts +0 -157
- package/src/components/ui/tu-do/shared/board-switcher.tsx +0 -322
- package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +0 -578
- package/src/components/ui/tu-do/shared/board-views.tsx +0 -1238
- package/src/components/ui/tu-do/shared/clear-menu-item.tsx +0 -34
- package/src/components/ui/tu-do/shared/create-list-dialog.tsx +0 -401
- package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +0 -245
- package/src/components/ui/tu-do/shared/cursor-overlay.tsx +0 -78
- package/src/components/ui/tu-do/shared/custom-date-picker/custom-date-picker-dialog.tsx +0 -157
- package/src/components/ui/tu-do/shared/description-overflow-warning-dialog.tsx +0 -68
- package/src/components/ui/tu-do/shared/edit-list-dialog.tsx +0 -383
- package/src/components/ui/tu-do/shared/empty-state-card.tsx +0 -46
- package/src/components/ui/tu-do/shared/estimation-mapping.test.ts +0 -32
- package/src/components/ui/tu-do/shared/estimation-mapping.ts +0 -74
- package/src/components/ui/tu-do/shared/estimation-utils.ts +0 -54
- package/src/components/ui/tu-do/shared/fade-setting-initializer.tsx +0 -61
- package/src/components/ui/tu-do/shared/label-chip.tsx +0 -46
- package/src/components/ui/tu-do/shared/list-view-context-menu.test.tsx +0 -203
- package/src/components/ui/tu-do/shared/list-view-sorting.test.ts +0 -50
- package/src/components/ui/tu-do/shared/list-view-sorting.ts +0 -108
- package/src/components/ui/tu-do/shared/list-view.tsx +0 -1248
- package/src/components/ui/tu-do/shared/mention-system/__tests__/mention-system.test.ts +0 -250
- package/src/components/ui/tu-do/shared/mention-system/mention-menu.tsx +0 -217
- package/src/components/ui/tu-do/shared/mention-system/types.ts +0 -141
- package/src/components/ui/tu-do/shared/mention-system/use-mention-suggestions.ts +0 -244
- package/src/components/ui/tu-do/shared/progressive-loader-context.tsx +0 -45
- package/src/components/ui/tu-do/shared/recent-sidebar-events.ts +0 -58
- package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +0 -651
- package/src/components/ui/tu-do/shared/relationship-task-identifier.ts +0 -14
- package/src/components/ui/tu-do/shared/slash-commands/__tests__/slash-commands.test.ts +0 -315
- package/src/components/ui/tu-do/shared/slash-commands/definitions.ts +0 -167
- package/src/components/ui/tu-do/shared/slash-commands/slash-command-menu.tsx +0 -114
- package/src/components/ui/tu-do/shared/special-task-list-pins.ts +0 -51
- package/src/components/ui/tu-do/shared/sync-warning-dialog.tsx +0 -133
- package/src/components/ui/tu-do/shared/task-board-errors.ts +0 -19
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +0 -82
- package/src/components/ui/tu-do/shared/task-cache-patches.ts +0 -394
- package/src/components/ui/tu-do/shared/task-detail-page.tsx +0 -176
- package/src/components/ui/tu-do/shared/task-detail-server-page.tsx +0 -37
- package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +0 -596
- package/src/components/ui/tu-do/shared/task-dialog-presentation.test.ts +0 -53
- package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +0 -30
- package/src/components/ui/tu-do/shared/task-dialog-wrapper.tsx +0 -28
- package/src/components/ui/tu-do/shared/task-due-date-visibility.test.ts +0 -72
- package/src/components/ui/tu-do/shared/task-due-date-visibility.ts +0 -38
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +0 -325
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +0 -268
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/mobile-floating-save-button.tsx +0 -46
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +0 -198
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.test.tsx +0 -129
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/smart-task-suggestions-panel.tsx +0 -358
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +0 -496
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +0 -545
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-picker-panel.tsx +0 -220
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +0 -165
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-trigger-styles.ts +0 -69
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.test.tsx +0 -180
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.tsx +0 -218
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-suggestion-menus.tsx +0 -148
- package/src/components/ui/tu-do/shared/task-edit-dialog/constants.ts +0 -18
- package/src/components/ui/tu-do/shared/task-edit-dialog/context-menu-guard.test.ts +0 -37
- package/src/components/ui/tu-do/shared/task-edit-dialog/description-diff-viewer.tsx +0 -1665
- package/src/components/ui/tu-do/shared/task-edit-dialog/description-versions.test.ts +0 -97
- package/src/components/ui/tu-do/shared/task-edit-dialog/description-versions.ts +0 -210
- package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +0 -716
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-dialog-close.test.ts +0 -218
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-dialog-keyboard-shortcuts.test.ts +0 -140
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-mutations.test.tsx +0 -231
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-overrides.test.ts +0 -412
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-realtime-sync.test.tsx +0 -237
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-yjs-sync.test.ts +0 -190
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-update-shared-task.test.ts +0 -107
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/task-api.test.ts +0 -171
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/task-api.ts +0 -296
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-editor-commands.ts +0 -314
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-suggestion-menus.ts +0 -503
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-change-detection.ts +0 -175
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-data.ts +0 -401
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-dependencies.ts +0 -773
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-dialog-close.ts +0 -193
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-dialog-keyboard-shortcuts.ts +0 -365
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-form-reset.ts +0 -231
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-form-state.ts +0 -307
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +0 -609
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-overrides.ts +0 -134
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-realtime-sync.ts +0 -270
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +0 -212
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +0 -554
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-revert.ts +0 -140
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.test.ts +0 -289
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +0 -1473
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-snapshot.ts +0 -59
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-yjs-sync.ts +0 -224
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-update-shared-task.ts +0 -60
- package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +0 -368
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/components/clickable-task-item.tsx +0 -90
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/components/index.ts +0 -4
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/components/subtask-action-buttons.tsx +0 -140
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/components/tab-button.tsx +0 -76
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/components/task-relationship-action-buttons.tsx +0 -178
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +0 -140
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/index.ts +0 -35
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +0 -70
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +0 -81
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +0 -87
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/task-search-popover.tsx +0 -229
- package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +0 -194
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +0 -91
- package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +0 -365
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +0 -802
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-delete-dialog.tsx +0 -104
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-change-dialog.test.tsx +0 -63
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-change-dialog.tsx +0 -218
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-restore-banner.tsx +0 -83
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-version-restore-dialog.test.tsx +0 -120
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-description-version-restore-dialog.tsx +0 -180
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.test.tsx +0 -68
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +0 -239
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-instances-section.tsx +0 -173
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +0 -303
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +0 -2213
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +0 -315
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +0 -167
- package/src/components/ui/tu-do/shared/task-edit-dialog/types/pending-relationship.test.ts +0 -52
- package/src/components/ui/tu-do/shared/task-edit-dialog/types/pending-relationship.ts +0 -134
- package/src/components/ui/tu-do/shared/task-edit-dialog/types.ts +0 -64
- package/src/components/ui/tu-do/shared/task-edit-dialog/user-display.test.ts +0 -58
- package/src/components/ui/tu-do/shared/task-edit-dialog/user-display.ts +0 -45
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils/inline-task-target-list.test.ts +0 -98
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils/inline-task-target-list.ts +0 -39
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils.test.ts +0 -504
- package/src/components/ui/tu-do/shared/task-edit-dialog/utils.ts +0 -582
- package/src/components/ui/tu-do/shared/task-edit-dialog/yjs-prosemirror-compat.test.ts +0 -65
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +0 -3196
- package/src/components/ui/tu-do/shared/task-estimation-display.tsx +0 -52
- package/src/components/ui/tu-do/shared/task-estimation-picker.tsx +0 -226
- package/src/components/ui/tu-do/shared/task-filter.types.ts +0 -65
- package/src/components/ui/tu-do/shared/task-label-selector.tsx +0 -217
- package/src/components/ui/tu-do/shared/task-labels-display.test.tsx +0 -51
- package/src/components/ui/tu-do/shared/task-labels-display.tsx +0 -119
- package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +0 -65
- package/src/components/ui/tu-do/shared/task-projects-display.tsx +0 -80
- package/src/components/ui/tu-do/shared/task-quick-create-target-list.ts +0 -20
- package/src/components/ui/tu-do/shared/task-resource-search-field.tsx +0 -49
- package/src/components/ui/tu-do/shared/task-resource-search-filters.ts +0 -27
- package/src/components/ui/tu-do/shared/task-row-actions-menu.test.tsx +0 -138
- package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +0 -402
- package/src/components/ui/tu-do/shared/task-share-dialog/components/share-link-settings.tsx +0 -86
- package/src/components/ui/tu-do/shared/task-share-dialog/components/shares-list.tsx +0 -85
- package/src/components/ui/tu-do/shared/task-share-dialog/hooks/use-task-sharing.ts +0 -296
- package/src/components/ui/tu-do/shared/task-share-dialog.tsx +0 -144
- package/src/components/ui/tu-do/shared/task-sound-effects.test.ts +0 -189
- package/src/components/ui/tu-do/shared/task-sound-effects.tsx +0 -468
- package/src/components/ui/tu-do/shared/task-url.ts +0 -56
- package/src/components/ui/tu-do/shared/text-diff-viewer.tsx +0 -55
- package/src/components/ui/tu-do/shared/types.ts +0 -14
- package/src/components/ui/tu-do/shared/unsaved-changes-warning-dialog.tsx +0 -71
- package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +0 -312
- package/src/components/ui/tu-do/shared/user-avatar.tsx +0 -46
- package/src/components/ui/tu-do/shared/user-presence-avatars.tsx +0 -415
- package/src/components/ui/tu-do/shared/utils/translate-task-list-display-name.ts +0 -28
- package/src/components/ui/tu-do/tasks-route-context.tsx +0 -50
- package/src/components/ui/tu-do/templates/client.tsx +0 -505
- package/src/components/ui/tu-do/templates/marketplace/client.tsx +0 -487
- package/src/components/ui/tu-do/templates/marketplace/task-marketplace-page.tsx +0 -169
- package/src/components/ui/tu-do/templates/save-as-template-dialog.tsx +0 -381
- package/src/components/ui/tu-do/templates/task-template-api.ts +0 -142
- package/src/components/ui/tu-do/templates/task-template-card.tsx +0 -118
- package/src/components/ui/tu-do/templates/task-template-client.test.tsx +0 -52
- package/src/components/ui/tu-do/templates/task-template-client.tsx +0 -258
- package/src/components/ui/tu-do/templates/task-template-dialogs.test.tsx +0 -167
- package/src/components/ui/tu-do/templates/task-template-dialogs.tsx +0 -376
- package/src/components/ui/tu-do/templates/task-templates-hub.test.tsx +0 -114
- package/src/components/ui/tu-do/templates/task-templates-hub.tsx +0 -53
- package/src/components/ui/tu-do/templates/task-templates-page.tsx +0 -157
- package/src/components/ui/tu-do/templates/templateId/client.tsx +0 -674
- package/src/components/ui/tu-do/templates/templateId/edit-template-dialog.tsx +0 -300
- package/src/components/ui/tu-do/templates/templateId/share-template-dialog.tsx +0 -261
- package/src/components/ui/tu-do/templates/templateId/task-template-detail-page-client.tsx +0 -127
- package/src/components/ui/tu-do/templates/templateId/task-template-detail-page.tsx +0 -89
- package/src/components/ui/tu-do/templates/templateId/use-template-dialog.tsx +0 -165
- package/src/components/ui/tu-do/templates/types.ts +0 -53
- package/src/components/ui/tu-do/utils/__tests__/label-colors.test.ts +0 -43
- package/src/components/ui/tu-do/utils/__tests__/taskColorUtils.test.ts +0 -267
- package/src/components/ui/tu-do/utils/__tests__/taskConstants.test.ts +0 -178
- package/src/components/ui/tu-do/utils/__tests__/taskDateUtils.test.ts +0 -226
- package/src/components/ui/tu-do/utils/__tests__/weekDateUtils.test.ts +0 -182
- package/src/components/ui/tu-do/utils/label-colors.test.ts +0 -15
- package/src/components/ui/tu-do/utils/label-colors.ts +0 -59
- package/src/components/ui/tu-do/utils/taskColorUtils.ts +0 -116
- package/src/components/ui/tu-do/utils/taskConstants.ts +0 -102
- package/src/components/ui/tu-do/utils/taskDateUtils.ts +0 -81
- package/src/components/ui/tu-do/utils/taskPriorityUtils.tsx +0 -80
- package/src/components/ui/tu-do/utils/weekDateUtils.ts +0 -64
- package/src/hooks/__tests__/use-task-actions.test.tsx +0 -2098
- package/src/hooks/__tests__/useBoardPresence.test.tsx +0 -191
- package/src/hooks/__tests__/useBoardRealtime.test.tsx +0 -1036
- package/src/hooks/__tests__/useCursorTracking.test.tsx +0 -212
- package/src/hooks/task-actions-personal-external.ts +0 -313
- package/src/hooks/use-task-actions.ts +0 -1910
- package/src/hooks/useBoardPresence.ts +0 -364
- package/src/hooks/useBoardRealtime.ts +0 -428
- package/src/hooks/useBoardRealtime.types.ts +0 -56
- package/src/hooks/useBoardRealtimeEventHandler.ts +0 -452
- package/src/hooks/useCursorTracking.ts +0 -402
- package/src/hooks/usePresence.ts +0 -272
- package/src/hooks/useTaskUserRealtime.ts +0 -340
- /package/src/{components/ui/tu-do/shared → lib}/task-open-events.ts +0 -0
|
@@ -1,2040 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
4
|
-
import {
|
|
5
|
-
Calendar,
|
|
6
|
-
CheckCircle,
|
|
7
|
-
Clock,
|
|
8
|
-
Copy,
|
|
9
|
-
ExternalLink,
|
|
10
|
-
Filter,
|
|
11
|
-
History,
|
|
12
|
-
MapPin,
|
|
13
|
-
MoreHorizontal,
|
|
14
|
-
Pause,
|
|
15
|
-
Play,
|
|
16
|
-
RefreshCw,
|
|
17
|
-
RotateCcw,
|
|
18
|
-
Sparkles,
|
|
19
|
-
Square,
|
|
20
|
-
Tag,
|
|
21
|
-
Timer,
|
|
22
|
-
Trash2,
|
|
23
|
-
TrendingUp,
|
|
24
|
-
Zap,
|
|
25
|
-
} from '@tuturuuu/icons';
|
|
26
|
-
import type { TimeTrackingCategory, WorkspaceTask } from '@tuturuuu/types';
|
|
27
|
-
import { cn } from '@tuturuuu/utils/format';
|
|
28
|
-
import { usePlatform } from '@tuturuuu/utils/hooks/use-platform';
|
|
29
|
-
import { useCallback, useEffect, useState } from 'react';
|
|
30
|
-
import {
|
|
31
|
-
AlertDialog,
|
|
32
|
-
AlertDialogAction,
|
|
33
|
-
AlertDialogCancel,
|
|
34
|
-
AlertDialogContent,
|
|
35
|
-
AlertDialogDescription,
|
|
36
|
-
AlertDialogFooter,
|
|
37
|
-
AlertDialogHeader,
|
|
38
|
-
AlertDialogTitle,
|
|
39
|
-
} from '../../alert-dialog';
|
|
40
|
-
import { Badge } from '../../badge';
|
|
41
|
-
import { Button } from '../../button';
|
|
42
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../card';
|
|
43
|
-
import {
|
|
44
|
-
Dialog,
|
|
45
|
-
DialogContent,
|
|
46
|
-
DialogDescription,
|
|
47
|
-
DialogHeader,
|
|
48
|
-
DialogTitle,
|
|
49
|
-
DialogTrigger,
|
|
50
|
-
} from '../../dialog';
|
|
51
|
-
import {
|
|
52
|
-
DropdownMenu,
|
|
53
|
-
DropdownMenuContent,
|
|
54
|
-
DropdownMenuItem,
|
|
55
|
-
DropdownMenuTrigger,
|
|
56
|
-
} from '../../dropdown-menu';
|
|
57
|
-
import { Input } from '../../input';
|
|
58
|
-
import { Label } from '../../label';
|
|
59
|
-
import { Popover, PopoverContent, PopoverTrigger } from '../../popover';
|
|
60
|
-
import {
|
|
61
|
-
Select,
|
|
62
|
-
SelectContent,
|
|
63
|
-
SelectItem,
|
|
64
|
-
SelectTrigger,
|
|
65
|
-
SelectValue,
|
|
66
|
-
} from '../../select';
|
|
67
|
-
import { Separator } from '../../separator';
|
|
68
|
-
import { toast } from '../../sonner';
|
|
69
|
-
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../tabs';
|
|
70
|
-
import { Textarea } from '../../textarea';
|
|
71
|
-
import type {
|
|
72
|
-
ExtendedWorkspaceTask,
|
|
73
|
-
SessionWithRelations,
|
|
74
|
-
TimerStats,
|
|
75
|
-
} from '../../time-tracker/types';
|
|
76
|
-
|
|
77
|
-
interface TimeTrackerProps {
|
|
78
|
-
wsId: string;
|
|
79
|
-
tasks?: ExtendedWorkspaceTask[];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
interface SessionTemplate {
|
|
83
|
-
title: string;
|
|
84
|
-
description?: string;
|
|
85
|
-
category_id?: string;
|
|
86
|
-
task_id?: string;
|
|
87
|
-
category?: TimeTrackingCategory;
|
|
88
|
-
task?: WorkspaceTask;
|
|
89
|
-
usage_count: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface TaskBoard {
|
|
93
|
-
id: string;
|
|
94
|
-
name: string;
|
|
95
|
-
created_at: string;
|
|
96
|
-
task_lists: TaskList[];
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
interface TaskList {
|
|
100
|
-
id: string;
|
|
101
|
-
name: string;
|
|
102
|
-
status: string;
|
|
103
|
-
color: string;
|
|
104
|
-
position: number;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export default function TimeTracker({ wsId, tasks = [] }: TimeTrackerProps) {
|
|
108
|
-
const { modKey } = usePlatform();
|
|
109
|
-
const queryClient = useQueryClient();
|
|
110
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
111
|
-
const [currentSession, setCurrentSession] =
|
|
112
|
-
useState<SessionWithRelations | null>(null);
|
|
113
|
-
const [categories, setCategories] = useState<TimeTrackingCategory[]>([]);
|
|
114
|
-
const [recentSessions, setRecentSessions] = useState<SessionWithRelations[]>(
|
|
115
|
-
[]
|
|
116
|
-
);
|
|
117
|
-
const [templates, setTemplates] = useState<SessionTemplate[]>([]);
|
|
118
|
-
const [timerStats, setTimerStats] = useState<TimerStats>({
|
|
119
|
-
todayTime: 0,
|
|
120
|
-
weekTime: 0,
|
|
121
|
-
monthTime: 0,
|
|
122
|
-
streak: 0,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Task creation state
|
|
126
|
-
const [boards, setBoards] = useState<TaskBoard[]>([]);
|
|
127
|
-
const [showTaskCreation, setShowTaskCreation] = useState(false);
|
|
128
|
-
const [selectedBoardId, setSelectedBoardId] = useState('');
|
|
129
|
-
const [selectedListId, setSelectedListId] = useState('');
|
|
130
|
-
const [newTaskName, setNewTaskName] = useState('');
|
|
131
|
-
const [newTaskDescription, setNewTaskDescription] = useState('');
|
|
132
|
-
const [isCreatingTask, setIsCreatingTask] = useState(false);
|
|
133
|
-
|
|
134
|
-
// Timer state
|
|
135
|
-
const [elapsedTime, setElapsedTime] = useState(0);
|
|
136
|
-
const [isRunning, setIsRunning] = useState(false);
|
|
137
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
138
|
-
|
|
139
|
-
// Form state
|
|
140
|
-
const [newSessionTitle, setNewSessionTitle] = useState('');
|
|
141
|
-
const [newSessionDescription, setNewSessionDescription] = useState('');
|
|
142
|
-
const [selectedCategoryId, setSelectedCategoryId] = useState<string>('');
|
|
143
|
-
const [selectedTaskId, setSelectedTaskId] = useState<string>('');
|
|
144
|
-
const [sessionMode, setSessionMode] = useState<'task' | 'manual'>('task');
|
|
145
|
-
const [showTaskSuggestion, setShowTaskSuggestion] = useState(false);
|
|
146
|
-
|
|
147
|
-
// Dialog states
|
|
148
|
-
const [sessionToDelete, setSessionToDelete] =
|
|
149
|
-
useState<SessionWithRelations | null>(null);
|
|
150
|
-
const [sessionToEdit, setSessionToEdit] =
|
|
151
|
-
useState<SessionWithRelations | null>(null);
|
|
152
|
-
const [isDeleting, setIsDeleting] = useState(false);
|
|
153
|
-
const [isEditing, setIsEditing] = useState(false);
|
|
154
|
-
|
|
155
|
-
// Enhanced UX states
|
|
156
|
-
const [actionStates, setActionStates] = useState<{ [key: string]: boolean }>(
|
|
157
|
-
{}
|
|
158
|
-
);
|
|
159
|
-
const [justCompleted, setJustCompleted] =
|
|
160
|
-
useState<SessionWithRelations | null>(null);
|
|
161
|
-
const [showQuickActions, setShowQuickActions] = useState(false);
|
|
162
|
-
|
|
163
|
-
// Filter states
|
|
164
|
-
const [activeTab, setActiveTab] = useState<'current' | 'recent' | 'history'>(
|
|
165
|
-
'current'
|
|
166
|
-
);
|
|
167
|
-
const [filterCategoryId, setFilterCategoryId] = useState<string>('');
|
|
168
|
-
const [filterTaskId, setFilterTaskId] = useState<string>('');
|
|
169
|
-
|
|
170
|
-
// Edit form state
|
|
171
|
-
const [editTitle, setEditTitle] = useState('');
|
|
172
|
-
const [editDescription, setEditDescription] = useState('');
|
|
173
|
-
const [editCategoryId, setEditCategoryId] = useState('');
|
|
174
|
-
const [editTaskId, setEditTaskId] = useState('');
|
|
175
|
-
const [editStartTime, setEditStartTime] = useState('');
|
|
176
|
-
const [editEndTime, setEditEndTime] = useState('');
|
|
177
|
-
|
|
178
|
-
// Format time display
|
|
179
|
-
const formatTime = useCallback((seconds: number): string => {
|
|
180
|
-
const hours = Math.floor(seconds / 3600);
|
|
181
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
182
|
-
const secs = seconds % 60;
|
|
183
|
-
|
|
184
|
-
if (hours > 0) {
|
|
185
|
-
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
186
|
-
}
|
|
187
|
-
return `${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
188
|
-
}, []);
|
|
189
|
-
|
|
190
|
-
// Format duration for display
|
|
191
|
-
const formatDuration = useCallback((seconds: number): string => {
|
|
192
|
-
const hours = Math.floor(seconds / 3600);
|
|
193
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
194
|
-
|
|
195
|
-
if (hours > 0) {
|
|
196
|
-
return `${hours}h ${minutes}m`;
|
|
197
|
-
}
|
|
198
|
-
return `${minutes}m`;
|
|
199
|
-
}, []);
|
|
200
|
-
|
|
201
|
-
// API call helpers
|
|
202
|
-
const apiCall = useCallback(
|
|
203
|
-
async (url: string, options: RequestInit = {}) => {
|
|
204
|
-
const response = await fetch(url, {
|
|
205
|
-
headers: {
|
|
206
|
-
'Content-Type': 'application/json',
|
|
207
|
-
...options.headers,
|
|
208
|
-
},
|
|
209
|
-
...options,
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
if (!response.ok) {
|
|
213
|
-
const error = await response
|
|
214
|
-
.json()
|
|
215
|
-
.catch(() => ({ error: 'Unknown error' }));
|
|
216
|
-
throw new Error(error.error || `HTTP ${response.status}`);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return response.json();
|
|
220
|
-
},
|
|
221
|
-
[]
|
|
222
|
-
);
|
|
223
|
-
|
|
224
|
-
// Fetch boards with lists
|
|
225
|
-
const fetchBoards = useCallback(async () => {
|
|
226
|
-
try {
|
|
227
|
-
const response = await apiCall(
|
|
228
|
-
`/api/v1/workspaces/${wsId}/boards-with-lists`
|
|
229
|
-
);
|
|
230
|
-
setBoards(response.boards || []);
|
|
231
|
-
} catch (error) {
|
|
232
|
-
console.error('Error fetching boards:', error);
|
|
233
|
-
toast.error('Failed to load boards');
|
|
234
|
-
}
|
|
235
|
-
}, [wsId, apiCall]);
|
|
236
|
-
|
|
237
|
-
// Fetch data
|
|
238
|
-
const fetchData = useCallback(async () => {
|
|
239
|
-
try {
|
|
240
|
-
const [categoriesRes, runningRes, recentRes, statsRes, templatesRes] =
|
|
241
|
-
await Promise.all([
|
|
242
|
-
apiCall(`/api/v1/workspaces/${wsId}/time-tracking/categories`),
|
|
243
|
-
apiCall(
|
|
244
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions?type=running`
|
|
245
|
-
),
|
|
246
|
-
apiCall(
|
|
247
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions?type=recent&limit=20`
|
|
248
|
-
),
|
|
249
|
-
apiCall(
|
|
250
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions?type=stats`
|
|
251
|
-
),
|
|
252
|
-
apiCall(`/api/v1/workspaces/${wsId}/time-tracking/templates`),
|
|
253
|
-
]);
|
|
254
|
-
|
|
255
|
-
setCategories(categoriesRes.categories || []);
|
|
256
|
-
setRecentSessions(recentRes.sessions || []);
|
|
257
|
-
setTimerStats(statsRes.stats);
|
|
258
|
-
setTemplates(templatesRes.templates || []);
|
|
259
|
-
|
|
260
|
-
if (runningRes.session) {
|
|
261
|
-
setCurrentSession(runningRes.session);
|
|
262
|
-
setIsRunning(true);
|
|
263
|
-
const elapsed = Math.floor(
|
|
264
|
-
(Date.now() - new Date(runningRes.session.start_time).getTime()) /
|
|
265
|
-
1000
|
|
266
|
-
);
|
|
267
|
-
setElapsedTime(elapsed);
|
|
268
|
-
} else {
|
|
269
|
-
setCurrentSession(null);
|
|
270
|
-
setIsRunning(false);
|
|
271
|
-
setElapsedTime(0);
|
|
272
|
-
}
|
|
273
|
-
} catch (error) {
|
|
274
|
-
console.error('Error fetching time tracking data:', error);
|
|
275
|
-
toast.error('Failed to load time tracking data');
|
|
276
|
-
}
|
|
277
|
-
}, [wsId, apiCall]);
|
|
278
|
-
|
|
279
|
-
// Timer effect
|
|
280
|
-
useEffect(() => {
|
|
281
|
-
let interval: ReturnType<typeof setInterval>;
|
|
282
|
-
|
|
283
|
-
if (isRunning && currentSession) {
|
|
284
|
-
interval = setInterval(() => {
|
|
285
|
-
const elapsed = Math.floor(
|
|
286
|
-
(Date.now() - new Date(currentSession.start_time).getTime()) / 1000
|
|
287
|
-
);
|
|
288
|
-
setElapsedTime(elapsed);
|
|
289
|
-
}, 1000);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
return () => clearInterval(interval);
|
|
293
|
-
}, [isRunning, currentSession]);
|
|
294
|
-
|
|
295
|
-
// Load data on mount and when dialog opens
|
|
296
|
-
useEffect(() => {
|
|
297
|
-
if (isOpen) {
|
|
298
|
-
fetchData();
|
|
299
|
-
fetchBoards();
|
|
300
|
-
}
|
|
301
|
-
}, [isOpen, fetchData, fetchBoards]);
|
|
302
|
-
|
|
303
|
-
// Create new task
|
|
304
|
-
const createTask = async () => {
|
|
305
|
-
if (!newTaskName.trim()) {
|
|
306
|
-
toast.error('Please enter a task name');
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (!selectedListId) {
|
|
311
|
-
toast.error('Please select a list');
|
|
312
|
-
return;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
setIsCreatingTask(true);
|
|
316
|
-
|
|
317
|
-
try {
|
|
318
|
-
const response = await apiCall(`/api/v1/workspaces/${wsId}/tasks`, {
|
|
319
|
-
method: 'POST',
|
|
320
|
-
body: JSON.stringify({
|
|
321
|
-
name: newTaskName,
|
|
322
|
-
description: newTaskDescription || null,
|
|
323
|
-
listId: selectedListId,
|
|
324
|
-
}),
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
const newTask = response.task;
|
|
328
|
-
setSelectedTaskId(newTask.id);
|
|
329
|
-
|
|
330
|
-
// In task mode, set the title to the working format
|
|
331
|
-
// In manual mode, keep the user's original title
|
|
332
|
-
if (sessionMode === 'task') {
|
|
333
|
-
setNewSessionTitle(`Working on: ${newTask.name}`);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
setShowTaskCreation(false);
|
|
337
|
-
setNewTaskName('');
|
|
338
|
-
setNewTaskDescription('');
|
|
339
|
-
setSelectedBoardId('');
|
|
340
|
-
setSelectedListId('');
|
|
341
|
-
setShowTaskSuggestion(false);
|
|
342
|
-
|
|
343
|
-
toast.success(`Task "${newTask.name}" created successfully!`);
|
|
344
|
-
|
|
345
|
-
// In task mode, start timer automatically
|
|
346
|
-
// In manual mode, just link the task and let user start manually
|
|
347
|
-
if (sessionMode === 'task') {
|
|
348
|
-
await startTimerWithTask(newTask.id, newTask.name);
|
|
349
|
-
}
|
|
350
|
-
} catch (error) {
|
|
351
|
-
console.error('Error creating task:', error);
|
|
352
|
-
toast.error('Failed to create task');
|
|
353
|
-
} finally {
|
|
354
|
-
setIsCreatingTask(false);
|
|
355
|
-
}
|
|
356
|
-
};
|
|
357
|
-
|
|
358
|
-
// Start timer with task
|
|
359
|
-
const startTimerWithTask = useCallback(
|
|
360
|
-
async (taskId: string, taskName: string) => {
|
|
361
|
-
setIsLoading(true);
|
|
362
|
-
|
|
363
|
-
try {
|
|
364
|
-
const response = await apiCall(
|
|
365
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions`,
|
|
366
|
-
{
|
|
367
|
-
method: 'POST',
|
|
368
|
-
body: JSON.stringify({
|
|
369
|
-
title: `Working on: ${taskName}`,
|
|
370
|
-
description: newSessionDescription || null,
|
|
371
|
-
categoryId: selectedCategoryId || null,
|
|
372
|
-
taskId: taskId,
|
|
373
|
-
}),
|
|
374
|
-
}
|
|
375
|
-
);
|
|
376
|
-
|
|
377
|
-
setCurrentSession(response.session);
|
|
378
|
-
setIsRunning(true);
|
|
379
|
-
setElapsedTime(0);
|
|
380
|
-
setNewSessionTitle('');
|
|
381
|
-
setNewSessionDescription('');
|
|
382
|
-
setSelectedCategoryId('');
|
|
383
|
-
setSelectedTaskId('');
|
|
384
|
-
|
|
385
|
-
// Invalidate the running session query to update sidebar
|
|
386
|
-
queryClient.invalidateQueries({
|
|
387
|
-
queryKey: ['running-time-session', wsId],
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
fetchData();
|
|
391
|
-
toast.success('Timer started!');
|
|
392
|
-
} catch (error) {
|
|
393
|
-
console.error('Error starting timer:', error);
|
|
394
|
-
toast.error('Failed to start timer');
|
|
395
|
-
} finally {
|
|
396
|
-
setIsLoading(false);
|
|
397
|
-
}
|
|
398
|
-
},
|
|
399
|
-
[
|
|
400
|
-
wsId,
|
|
401
|
-
queryClient,
|
|
402
|
-
newSessionDescription,
|
|
403
|
-
selectedCategoryId,
|
|
404
|
-
fetchData,
|
|
405
|
-
apiCall,
|
|
406
|
-
]
|
|
407
|
-
);
|
|
408
|
-
|
|
409
|
-
// Start timer
|
|
410
|
-
const startTimer = useCallback(async () => {
|
|
411
|
-
if (sessionMode === 'task' && selectedTaskId) {
|
|
412
|
-
const selectedTask = tasks.find((t) => t.id === selectedTaskId);
|
|
413
|
-
if (selectedTask) {
|
|
414
|
-
await startTimerWithTask(
|
|
415
|
-
selectedTaskId,
|
|
416
|
-
selectedTask.name || 'Untitled Task'
|
|
417
|
-
);
|
|
418
|
-
return;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (sessionMode === 'task' && !selectedTaskId) {
|
|
423
|
-
setShowTaskCreation(true);
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (!newSessionTitle.trim()) {
|
|
428
|
-
toast.error('Please enter a title for your time session');
|
|
429
|
-
return;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
setIsLoading(true);
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
const response = await apiCall(
|
|
436
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions`,
|
|
437
|
-
{
|
|
438
|
-
method: 'POST',
|
|
439
|
-
body: JSON.stringify({
|
|
440
|
-
title: newSessionTitle,
|
|
441
|
-
description: newSessionDescription || null,
|
|
442
|
-
categoryId: selectedCategoryId || null,
|
|
443
|
-
taskId: selectedTaskId || null,
|
|
444
|
-
}),
|
|
445
|
-
}
|
|
446
|
-
);
|
|
447
|
-
|
|
448
|
-
setCurrentSession(response.session);
|
|
449
|
-
setIsRunning(true);
|
|
450
|
-
setElapsedTime(0);
|
|
451
|
-
setNewSessionTitle('');
|
|
452
|
-
setNewSessionDescription('');
|
|
453
|
-
setSelectedCategoryId('');
|
|
454
|
-
setSelectedTaskId('');
|
|
455
|
-
|
|
456
|
-
// Invalidate the running session query to update sidebar
|
|
457
|
-
queryClient.invalidateQueries({
|
|
458
|
-
queryKey: ['running-time-session', wsId],
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
fetchData();
|
|
462
|
-
toast.success('Timer started!');
|
|
463
|
-
} catch (error) {
|
|
464
|
-
console.error('Error starting timer:', error);
|
|
465
|
-
toast.error('Failed to start timer');
|
|
466
|
-
} finally {
|
|
467
|
-
setIsLoading(false);
|
|
468
|
-
}
|
|
469
|
-
}, [
|
|
470
|
-
sessionMode,
|
|
471
|
-
selectedTaskId,
|
|
472
|
-
tasks,
|
|
473
|
-
newSessionTitle,
|
|
474
|
-
newSessionDescription,
|
|
475
|
-
selectedCategoryId,
|
|
476
|
-
wsId,
|
|
477
|
-
queryClient,
|
|
478
|
-
fetchData,
|
|
479
|
-
apiCall,
|
|
480
|
-
startTimerWithTask,
|
|
481
|
-
]);
|
|
482
|
-
|
|
483
|
-
// Stop timer
|
|
484
|
-
const stopTimer = useCallback(async () => {
|
|
485
|
-
if (!currentSession) return;
|
|
486
|
-
|
|
487
|
-
setIsLoading(true);
|
|
488
|
-
|
|
489
|
-
try {
|
|
490
|
-
const response = await apiCall(
|
|
491
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions/${currentSession.id}`,
|
|
492
|
-
{
|
|
493
|
-
method: 'PATCH',
|
|
494
|
-
body: JSON.stringify({ action: 'stop' }),
|
|
495
|
-
}
|
|
496
|
-
);
|
|
497
|
-
|
|
498
|
-
const completedSession = response.session;
|
|
499
|
-
setJustCompleted(completedSession);
|
|
500
|
-
setCurrentSession(null);
|
|
501
|
-
setIsRunning(false);
|
|
502
|
-
setElapsedTime(0);
|
|
503
|
-
setActiveTab('recent'); // Switch to recent tab to see completed session
|
|
504
|
-
|
|
505
|
-
// Show completion celebration
|
|
506
|
-
setTimeout(() => setJustCompleted(null), 3000);
|
|
507
|
-
|
|
508
|
-
// Invalidate the running session query to update sidebar
|
|
509
|
-
queryClient.invalidateQueries({
|
|
510
|
-
queryKey: ['running-time-session', wsId],
|
|
511
|
-
});
|
|
512
|
-
|
|
513
|
-
fetchData();
|
|
514
|
-
toast.success(
|
|
515
|
-
`Session completed! Tracked ${formatDuration(completedSession.duration_seconds || 0)}`,
|
|
516
|
-
{
|
|
517
|
-
duration: 4000,
|
|
518
|
-
}
|
|
519
|
-
);
|
|
520
|
-
} catch (error) {
|
|
521
|
-
console.error('Error stopping timer:', error);
|
|
522
|
-
toast.error('Failed to stop timer');
|
|
523
|
-
} finally {
|
|
524
|
-
setIsLoading(false);
|
|
525
|
-
}
|
|
526
|
-
}, [currentSession, wsId, queryClient, fetchData, apiCall, formatDuration]);
|
|
527
|
-
|
|
528
|
-
// Pause timer
|
|
529
|
-
const pauseTimer = useCallback(async () => {
|
|
530
|
-
if (!currentSession) return;
|
|
531
|
-
|
|
532
|
-
setIsLoading(true);
|
|
533
|
-
|
|
534
|
-
try {
|
|
535
|
-
await apiCall(
|
|
536
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions/${currentSession.id}`,
|
|
537
|
-
{
|
|
538
|
-
method: 'PATCH',
|
|
539
|
-
body: JSON.stringify({ action: 'pause' }),
|
|
540
|
-
}
|
|
541
|
-
);
|
|
542
|
-
|
|
543
|
-
setCurrentSession(null);
|
|
544
|
-
setIsRunning(false);
|
|
545
|
-
setElapsedTime(0);
|
|
546
|
-
|
|
547
|
-
fetchData();
|
|
548
|
-
toast.success('Timer paused');
|
|
549
|
-
} catch (error) {
|
|
550
|
-
console.error('Error pausing timer:', error);
|
|
551
|
-
toast.error('Failed to pause timer');
|
|
552
|
-
} finally {
|
|
553
|
-
setIsLoading(false);
|
|
554
|
-
}
|
|
555
|
-
}, [currentSession, wsId, fetchData, apiCall]);
|
|
556
|
-
|
|
557
|
-
// Resume session (creates new session with same details)
|
|
558
|
-
const resumeSession = async (session: SessionWithRelations) => {
|
|
559
|
-
setActionStates((prev) => ({ ...prev, [`resume-${session.id}`]: true }));
|
|
560
|
-
|
|
561
|
-
try {
|
|
562
|
-
const response = await apiCall(
|
|
563
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions/${session.id}`,
|
|
564
|
-
{
|
|
565
|
-
method: 'PATCH',
|
|
566
|
-
body: JSON.stringify({ action: 'resume' }),
|
|
567
|
-
}
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
setCurrentSession(response.session);
|
|
571
|
-
setIsRunning(true);
|
|
572
|
-
setElapsedTime(0);
|
|
573
|
-
setActiveTab('current'); // Switch to current tab
|
|
574
|
-
|
|
575
|
-
// Invalidate the running session query to update sidebar
|
|
576
|
-
queryClient.invalidateQueries({
|
|
577
|
-
queryKey: ['running-time-session', wsId],
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
fetchData();
|
|
581
|
-
toast.success(`Started new session: "${session.title}"`);
|
|
582
|
-
} catch (error) {
|
|
583
|
-
console.error('Error resuming session:', error);
|
|
584
|
-
toast.error('Failed to start new session');
|
|
585
|
-
} finally {
|
|
586
|
-
setActionStates((prev) => ({ ...prev, [`resume-${session.id}`]: false }));
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
// Start from template
|
|
591
|
-
const startFromTemplate = async (template: SessionTemplate) => {
|
|
592
|
-
setNewSessionTitle(template.title);
|
|
593
|
-
setNewSessionDescription(template.description || '');
|
|
594
|
-
setSelectedCategoryId(template.category_id || '');
|
|
595
|
-
setSelectedTaskId(template.task_id || '');
|
|
596
|
-
setActiveTab('current');
|
|
597
|
-
};
|
|
598
|
-
|
|
599
|
-
// Duplicate session
|
|
600
|
-
const duplicateSession = async (session: SessionWithRelations) => {
|
|
601
|
-
setNewSessionTitle(session.title);
|
|
602
|
-
setNewSessionDescription(session.description || '');
|
|
603
|
-
setSelectedCategoryId(session.category_id || '');
|
|
604
|
-
setSelectedTaskId(session.task_id || '');
|
|
605
|
-
setActiveTab('current');
|
|
606
|
-
toast.success('Session settings copied');
|
|
607
|
-
};
|
|
608
|
-
|
|
609
|
-
// Edit session
|
|
610
|
-
// const openEditDialog = (session: SessionWithRelations) => {
|
|
611
|
-
// setSessionToEdit(session);
|
|
612
|
-
// setEditTitle(session.title);
|
|
613
|
-
// setEditDescription(session.description || '');
|
|
614
|
-
// setEditCategoryId(session.category_id || '');
|
|
615
|
-
// setEditTaskId(session.task_id || '');
|
|
616
|
-
// setEditStartTime(new Date(session.start_time).toISOString().slice(0, 16));
|
|
617
|
-
// setEditEndTime(
|
|
618
|
-
// session.end_time
|
|
619
|
-
// ? new Date(session.end_time).toISOString().slice(0, 16)
|
|
620
|
-
// : ''
|
|
621
|
-
// );
|
|
622
|
-
// };
|
|
623
|
-
|
|
624
|
-
const saveEdit = async () => {
|
|
625
|
-
if (!sessionToEdit) return;
|
|
626
|
-
|
|
627
|
-
setIsEditing(true);
|
|
628
|
-
|
|
629
|
-
try {
|
|
630
|
-
await apiCall(
|
|
631
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions/${sessionToEdit.id}`,
|
|
632
|
-
{
|
|
633
|
-
method: 'PATCH',
|
|
634
|
-
body: JSON.stringify({
|
|
635
|
-
action: 'edit',
|
|
636
|
-
title: editTitle,
|
|
637
|
-
description: editDescription,
|
|
638
|
-
categoryId: editCategoryId || null,
|
|
639
|
-
taskId: editTaskId || null,
|
|
640
|
-
startTime: editStartTime
|
|
641
|
-
? new Date(editStartTime).toISOString()
|
|
642
|
-
: undefined,
|
|
643
|
-
endTime: editEndTime
|
|
644
|
-
? new Date(editEndTime).toISOString()
|
|
645
|
-
: undefined,
|
|
646
|
-
}),
|
|
647
|
-
}
|
|
648
|
-
);
|
|
649
|
-
|
|
650
|
-
setSessionToEdit(null);
|
|
651
|
-
fetchData();
|
|
652
|
-
toast.success('Session updated successfully');
|
|
653
|
-
} catch (error) {
|
|
654
|
-
console.error('Error updating session:', error);
|
|
655
|
-
toast.error('Failed to update session');
|
|
656
|
-
} finally {
|
|
657
|
-
setIsEditing(false);
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
|
-
|
|
661
|
-
// Delete session
|
|
662
|
-
const deleteSession = async () => {
|
|
663
|
-
if (!sessionToDelete) return;
|
|
664
|
-
|
|
665
|
-
setIsDeleting(true);
|
|
666
|
-
|
|
667
|
-
try {
|
|
668
|
-
await apiCall(
|
|
669
|
-
`/api/v1/workspaces/${wsId}/time-tracking/sessions/${sessionToDelete.id}`,
|
|
670
|
-
{
|
|
671
|
-
method: 'DELETE',
|
|
672
|
-
}
|
|
673
|
-
);
|
|
674
|
-
|
|
675
|
-
setSessionToDelete(null);
|
|
676
|
-
fetchData();
|
|
677
|
-
toast.success('Time session deleted successfully');
|
|
678
|
-
} catch (error) {
|
|
679
|
-
console.error('Error deleting session:', error);
|
|
680
|
-
toast.error('Failed to delete session');
|
|
681
|
-
} finally {
|
|
682
|
-
setIsDeleting(false);
|
|
683
|
-
}
|
|
684
|
-
};
|
|
685
|
-
|
|
686
|
-
const getCategoryColor = (color: string) => {
|
|
687
|
-
const colorMap: Record<string, string> = {
|
|
688
|
-
RED: 'bg-dynamic-red/80',
|
|
689
|
-
BLUE: 'bg-dynamic-blue/80',
|
|
690
|
-
GREEN: 'bg-dynamic-green/80',
|
|
691
|
-
YELLOW: 'bg-dynamic-yellow/80',
|
|
692
|
-
ORANGE: 'bg-dynamic-orange/80',
|
|
693
|
-
PURPLE: 'bg-dynamic-purple/80',
|
|
694
|
-
PINK: 'bg-dynamic-pink/80',
|
|
695
|
-
INDIGO: 'bg-dynamic-indigo/80',
|
|
696
|
-
CYAN: 'bg-dynamic-cyan/80',
|
|
697
|
-
GRAY: 'bg-dynamic-gray/80',
|
|
698
|
-
};
|
|
699
|
-
return colorMap[color] || 'bg-dynamic-blue/80';
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
const filteredSessions = recentSessions.filter((session) => {
|
|
703
|
-
if (filterCategoryId && session.category_id !== filterCategoryId)
|
|
704
|
-
return false;
|
|
705
|
-
if (filterTaskId && session.task_id !== filterTaskId) return false;
|
|
706
|
-
return true;
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
// Get lists for selected board
|
|
710
|
-
const selectedBoard = boards.find((board) => board.id === selectedBoardId);
|
|
711
|
-
const availableLists = selectedBoard?.task_lists || [];
|
|
712
|
-
|
|
713
|
-
// Handle task selection change
|
|
714
|
-
const handleTaskSelectionChange = (taskId: string) => {
|
|
715
|
-
setSelectedTaskId(taskId);
|
|
716
|
-
if (taskId) {
|
|
717
|
-
const selectedTask = tasks.find((t) => t.id === taskId);
|
|
718
|
-
if (selectedTask) {
|
|
719
|
-
setNewSessionTitle(`Working on: ${selectedTask.name}`);
|
|
720
|
-
}
|
|
721
|
-
} else {
|
|
722
|
-
setNewSessionTitle('');
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
|
|
726
|
-
// Handle session mode change with cleanup
|
|
727
|
-
const handleSessionModeChange = (mode: 'task' | 'manual') => {
|
|
728
|
-
const previousMode = sessionMode;
|
|
729
|
-
setSessionMode(mode);
|
|
730
|
-
|
|
731
|
-
// Clear form state when switching modes for better UX
|
|
732
|
-
setNewSessionTitle('');
|
|
733
|
-
setNewSessionDescription('');
|
|
734
|
-
setSelectedTaskId('');
|
|
735
|
-
setShowTaskSuggestion(false);
|
|
736
|
-
|
|
737
|
-
// Reset any temporary states
|
|
738
|
-
setSelectedCategoryId('');
|
|
739
|
-
|
|
740
|
-
// Provide helpful feedback
|
|
741
|
-
if (previousMode !== mode) {
|
|
742
|
-
if (mode === 'manual') {
|
|
743
|
-
toast.success('Switched to manual mode - start typing freely!', {
|
|
744
|
-
duration: 2000,
|
|
745
|
-
});
|
|
746
|
-
} else {
|
|
747
|
-
toast.success(
|
|
748
|
-
'Switched to task-based mode - select or create a task!',
|
|
749
|
-
{
|
|
750
|
-
duration: 2000,
|
|
751
|
-
}
|
|
752
|
-
);
|
|
753
|
-
}
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
|
|
757
|
-
// Handle manual title change with task suggestion
|
|
758
|
-
const handleManualTitleChange = (title: string) => {
|
|
759
|
-
setNewSessionTitle(title);
|
|
760
|
-
|
|
761
|
-
// Check if title matches any existing task
|
|
762
|
-
const matchingTask = tasks.find(
|
|
763
|
-
(task) =>
|
|
764
|
-
task.name?.toLowerCase().includes(title.toLowerCase()) &&
|
|
765
|
-
title.length > 2
|
|
766
|
-
);
|
|
767
|
-
|
|
768
|
-
if (matchingTask && title.length > 2) {
|
|
769
|
-
setSelectedTaskId(matchingTask.id || '');
|
|
770
|
-
setShowTaskSuggestion(false);
|
|
771
|
-
} else if (title.length > 2 && !selectedTaskId) {
|
|
772
|
-
// Suggest creating a new task if title doesn't match any existing task
|
|
773
|
-
setShowTaskSuggestion(true);
|
|
774
|
-
} else {
|
|
775
|
-
setShowTaskSuggestion(false);
|
|
776
|
-
}
|
|
777
|
-
};
|
|
778
|
-
|
|
779
|
-
// Create task from manual session
|
|
780
|
-
const createTaskFromManualSession = async () => {
|
|
781
|
-
setNewTaskName(newSessionTitle);
|
|
782
|
-
setShowTaskCreation(true);
|
|
783
|
-
setShowTaskSuggestion(false);
|
|
784
|
-
};
|
|
785
|
-
|
|
786
|
-
// Keyboard shortcuts (placed after function declarations)
|
|
787
|
-
useEffect(() => {
|
|
788
|
-
const handleKeyDown = (event: KeyboardEvent) => {
|
|
789
|
-
if (!isOpen) return;
|
|
790
|
-
|
|
791
|
-
// Ctrl/Cmd + Enter to start/stop timer
|
|
792
|
-
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
|
793
|
-
event.preventDefault();
|
|
794
|
-
if (isRunning) {
|
|
795
|
-
stopTimer();
|
|
796
|
-
} else if (newSessionTitle.trim()) {
|
|
797
|
-
startTimer();
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// Escape to close dialog
|
|
802
|
-
if (event.key === 'Escape') {
|
|
803
|
-
setIsOpen(false);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// Ctrl/Cmd + P to pause
|
|
807
|
-
if ((event.ctrlKey || event.metaKey) && event.key === 'p' && isRunning) {
|
|
808
|
-
event.preventDefault();
|
|
809
|
-
pauseTimer();
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
|
|
813
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
814
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
815
|
-
}, [isOpen, isRunning, newSessionTitle, startTimer, stopTimer, pauseTimer]);
|
|
816
|
-
|
|
817
|
-
return (
|
|
818
|
-
<>
|
|
819
|
-
<Dialog open={isOpen} onOpenChange={setIsOpen}>
|
|
820
|
-
<DialogTrigger asChild>
|
|
821
|
-
<Button
|
|
822
|
-
variant={isRunning ? 'destructive' : 'outline'}
|
|
823
|
-
size="sm"
|
|
824
|
-
className={cn('gap-2', 'relative w-full')}
|
|
825
|
-
>
|
|
826
|
-
{isRunning && (
|
|
827
|
-
<div className="absolute inset-0 animate-pulse bg-linear-to-r from-red-600/20 to-transparent" />
|
|
828
|
-
)}
|
|
829
|
-
<div className="relative flex items-center gap-2">
|
|
830
|
-
{isRunning ? (
|
|
831
|
-
<>
|
|
832
|
-
<Square className="h-3 w-3 animate-pulse" />
|
|
833
|
-
<span className="@[100px]:inline hidden font-mono">
|
|
834
|
-
{formatTime(elapsedTime)}
|
|
835
|
-
</span>
|
|
836
|
-
<span className="@[100px]:hidden font-mono">
|
|
837
|
-
{Math.floor(elapsedTime / 60)}m
|
|
838
|
-
</span>
|
|
839
|
-
</>
|
|
840
|
-
) : (
|
|
841
|
-
<>
|
|
842
|
-
<Timer className="h-3 w-3" />
|
|
843
|
-
<span className="@[100px]:inline hidden">Time Tracker</span>
|
|
844
|
-
<span className="@[100px]:hidden">Timer</span>
|
|
845
|
-
</>
|
|
846
|
-
)}
|
|
847
|
-
</div>
|
|
848
|
-
</Button>
|
|
849
|
-
</DialogTrigger>
|
|
850
|
-
|
|
851
|
-
<DialogContent className="@container max-h-[95vh] max-w-7xl overflow-y-auto">
|
|
852
|
-
<DialogHeader>
|
|
853
|
-
<DialogTitle className="flex items-center gap-2">
|
|
854
|
-
<Timer className="h-5 w-5" />
|
|
855
|
-
Time Tracker
|
|
856
|
-
</DialogTitle>
|
|
857
|
-
<DialogDescription className="space-y-1">
|
|
858
|
-
<span>
|
|
859
|
-
Track your time across tasks and projects with detailed
|
|
860
|
-
analytics
|
|
861
|
-
</span>
|
|
862
|
-
<span className="mt-2 text-muted-foreground text-xs">
|
|
863
|
-
<br />•{' '}
|
|
864
|
-
<span className="rounded bg-muted px-1 py-0.5 text-xs">
|
|
865
|
-
{modKey} + Enter
|
|
866
|
-
</span>{' '}
|
|
867
|
-
to start/stop
|
|
868
|
-
<br />•{' '}
|
|
869
|
-
<span className="rounded bg-muted px-1 py-0.5 text-xs">
|
|
870
|
-
{modKey} + P
|
|
871
|
-
</span>{' '}
|
|
872
|
-
to pause
|
|
873
|
-
<br />•{' '}
|
|
874
|
-
<span className="rounded bg-muted px-1 py-0.5 text-xs">
|
|
875
|
-
Esc
|
|
876
|
-
</span>{' '}
|
|
877
|
-
to close
|
|
878
|
-
</span>
|
|
879
|
-
</DialogDescription>
|
|
880
|
-
</DialogHeader>
|
|
881
|
-
|
|
882
|
-
<Tabs
|
|
883
|
-
value={activeTab}
|
|
884
|
-
onValueChange={(v) =>
|
|
885
|
-
setActiveTab(v as 'current' | 'recent' | 'history')
|
|
886
|
-
}
|
|
887
|
-
>
|
|
888
|
-
<TabsList className="grid w-full grid-cols-3">
|
|
889
|
-
<TabsTrigger value="current" className="flex items-center gap-2">
|
|
890
|
-
<Play className="h-4 w-4" />
|
|
891
|
-
Current
|
|
892
|
-
</TabsTrigger>
|
|
893
|
-
<TabsTrigger value="recent" className="flex items-center gap-2">
|
|
894
|
-
<Clock className="h-4 w-4" />
|
|
895
|
-
Recent
|
|
896
|
-
</TabsTrigger>
|
|
897
|
-
<TabsTrigger value="history" className="flex items-center gap-2">
|
|
898
|
-
<History className="h-4 w-4" />
|
|
899
|
-
History
|
|
900
|
-
</TabsTrigger>
|
|
901
|
-
</TabsList>
|
|
902
|
-
|
|
903
|
-
<div className="grid @5xl:grid-cols-2 grid-cols-1 gap-6">
|
|
904
|
-
{/* Current Session Tab */}
|
|
905
|
-
<TabsContent value="current" className="@container space-y-4">
|
|
906
|
-
<Card className="transition-all hover:shadow-md">
|
|
907
|
-
<CardHeader className="pb-3">
|
|
908
|
-
<CardTitle className="flex items-center gap-2 @lg:text-lg text-base">
|
|
909
|
-
<Clock className="@lg:h-5 h-4 @lg:w-5 w-4" />
|
|
910
|
-
Current Session
|
|
911
|
-
</CardTitle>
|
|
912
|
-
</CardHeader>
|
|
913
|
-
<CardContent className="space-y-4">
|
|
914
|
-
{currentSession ? (
|
|
915
|
-
<div className="space-y-4 text-center">
|
|
916
|
-
<div className="relative overflow-hidden rounded-lg bg-linear-to-br from-red-50 to-red-100 @lg:p-6 p-4 dark:from-red-950/20 dark:to-red-900/20">
|
|
917
|
-
<div className="absolute inset-0 animate-pulse bg-linear-to-r from-red-500/10 to-transparent opacity-30"></div>
|
|
918
|
-
<div className="relative">
|
|
919
|
-
<div className="font-bold font-mono @lg:text-4xl text-3xl text-red-600 transition-all duration-300 dark:text-red-400">
|
|
920
|
-
{formatTime(elapsedTime)}
|
|
921
|
-
</div>
|
|
922
|
-
<div className="mt-2 flex items-center gap-2 @lg:text-sm text-red-600/70 text-xs dark:text-red-400/70">
|
|
923
|
-
<div className="h-2 w-2 animate-pulse rounded-full bg-red-500"></div>
|
|
924
|
-
Started at{' '}
|
|
925
|
-
{new Date(
|
|
926
|
-
currentSession.start_time
|
|
927
|
-
).toLocaleTimeString()}
|
|
928
|
-
</div>
|
|
929
|
-
</div>
|
|
930
|
-
</div>
|
|
931
|
-
|
|
932
|
-
<div className="text-left">
|
|
933
|
-
<h3 className="font-medium @lg:text-base text-sm">
|
|
934
|
-
{currentSession.title}
|
|
935
|
-
</h3>
|
|
936
|
-
{currentSession.description && (
|
|
937
|
-
<p className="mt-1 @lg:text-sm text-muted-foreground text-xs">
|
|
938
|
-
{currentSession.description}
|
|
939
|
-
</p>
|
|
940
|
-
)}
|
|
941
|
-
<div className="mt-2 flex flex-wrap @lg:gap-2 gap-1">
|
|
942
|
-
{currentSession.category && (
|
|
943
|
-
<Badge
|
|
944
|
-
className={cn(
|
|
945
|
-
'@lg:text-sm text-xs',
|
|
946
|
-
getCategoryColor(
|
|
947
|
-
currentSession.category.color || 'BLUE'
|
|
948
|
-
)
|
|
949
|
-
)}
|
|
950
|
-
>
|
|
951
|
-
{currentSession.category.name}
|
|
952
|
-
</Badge>
|
|
953
|
-
)}
|
|
954
|
-
{currentSession.task && (
|
|
955
|
-
<div className="flex items-center gap-2">
|
|
956
|
-
<div className="flex items-center gap-1.5 rounded-md border border-dynamic-blue/20 bg-linear-to-r from-dynamic-blue/10 to-dynamic-blue/5 px-2 py-1">
|
|
957
|
-
<CheckCircle className="h-3 w-3 text-dynamic-blue" />
|
|
958
|
-
<span className="font-medium @lg:text-sm text-dynamic-blue text-xs">
|
|
959
|
-
{currentSession.task.name}
|
|
960
|
-
</span>
|
|
961
|
-
</div>
|
|
962
|
-
</div>
|
|
963
|
-
)}
|
|
964
|
-
</div>
|
|
965
|
-
{currentSession.task &&
|
|
966
|
-
(() => {
|
|
967
|
-
const taskWithDetails = tasks.find(
|
|
968
|
-
(t) => t.id === currentSession.task?.id
|
|
969
|
-
);
|
|
970
|
-
return taskWithDetails?.board_name &&
|
|
971
|
-
taskWithDetails?.list_name ? (
|
|
972
|
-
<div className="mt-1 flex items-center gap-2 text-muted-foreground text-xs">
|
|
973
|
-
<div className="flex items-center gap-1">
|
|
974
|
-
<MapPin className="h-3 w-3" />
|
|
975
|
-
<span>{taskWithDetails.board_name}</span>
|
|
976
|
-
</div>
|
|
977
|
-
<span>•</span>
|
|
978
|
-
<div className="flex items-center gap-1">
|
|
979
|
-
<Tag className="h-3 w-3" />
|
|
980
|
-
<span>{taskWithDetails.list_name}</span>
|
|
981
|
-
</div>
|
|
982
|
-
</div>
|
|
983
|
-
) : null;
|
|
984
|
-
})()}
|
|
985
|
-
</div>
|
|
986
|
-
|
|
987
|
-
<div className="flex gap-2">
|
|
988
|
-
<Button
|
|
989
|
-
onClick={pauseTimer}
|
|
990
|
-
disabled={isLoading}
|
|
991
|
-
variant="outline"
|
|
992
|
-
className="flex-1"
|
|
993
|
-
>
|
|
994
|
-
<Pause className="mr-2 h-4 w-4" />
|
|
995
|
-
Pause
|
|
996
|
-
</Button>
|
|
997
|
-
<Button
|
|
998
|
-
onClick={stopTimer}
|
|
999
|
-
disabled={isLoading}
|
|
1000
|
-
variant="destructive"
|
|
1001
|
-
className="flex-1"
|
|
1002
|
-
>
|
|
1003
|
-
<Square className="mr-2 h-4 w-4" />
|
|
1004
|
-
Stop
|
|
1005
|
-
</Button>
|
|
1006
|
-
</div>
|
|
1007
|
-
</div>
|
|
1008
|
-
) : (
|
|
1009
|
-
<div className="space-y-4">
|
|
1010
|
-
<div className="rounded-lg border-2 border-muted-foreground/25 border-dashed @lg:p-6 p-4 text-center">
|
|
1011
|
-
<Clock className="mx-auto mb-2 @lg:h-12 h-8 @lg:w-12 w-8 text-muted-foreground" />
|
|
1012
|
-
<p className="@lg:text-base text-muted-foreground text-sm">
|
|
1013
|
-
Ready to start tracking time
|
|
1014
|
-
</p>
|
|
1015
|
-
</div>
|
|
1016
|
-
|
|
1017
|
-
{/* Session Mode Toggle */}
|
|
1018
|
-
<Tabs
|
|
1019
|
-
value={sessionMode}
|
|
1020
|
-
onValueChange={(v) =>
|
|
1021
|
-
handleSessionModeChange(v as 'task' | 'manual')
|
|
1022
|
-
}
|
|
1023
|
-
>
|
|
1024
|
-
<TabsList className="grid h-full w-full grid-cols-2 bg-muted/50">
|
|
1025
|
-
<TabsTrigger
|
|
1026
|
-
value="task"
|
|
1027
|
-
className="flex items-center gap-2 data-[state=active]:bg-background data-[state=active]:shadow-sm"
|
|
1028
|
-
>
|
|
1029
|
-
<CheckCircle className="h-4 w-4" />
|
|
1030
|
-
<div className="flex flex-col items-start">
|
|
1031
|
-
<span className="font-medium text-sm">
|
|
1032
|
-
Task-based
|
|
1033
|
-
</span>
|
|
1034
|
-
<span className="text-muted-foreground text-xs">
|
|
1035
|
-
Select or create task
|
|
1036
|
-
</span>
|
|
1037
|
-
</div>
|
|
1038
|
-
</TabsTrigger>
|
|
1039
|
-
<TabsTrigger
|
|
1040
|
-
value="manual"
|
|
1041
|
-
className="flex items-center gap-2 data-[state=active]:bg-background data-[state=active]:shadow-sm"
|
|
1042
|
-
>
|
|
1043
|
-
<Timer className="h-4 w-4" />
|
|
1044
|
-
<div className="flex flex-col items-start">
|
|
1045
|
-
<span className="font-medium text-sm">
|
|
1046
|
-
Manual
|
|
1047
|
-
</span>
|
|
1048
|
-
<span className="text-muted-foreground text-xs">
|
|
1049
|
-
Free-form entry
|
|
1050
|
-
</span>
|
|
1051
|
-
</div>
|
|
1052
|
-
</TabsTrigger>
|
|
1053
|
-
</TabsList>
|
|
1054
|
-
|
|
1055
|
-
<TabsContent
|
|
1056
|
-
value="task"
|
|
1057
|
-
className="fade-in-50 slide-in-from-bottom-2 animate-in space-y-4 duration-300"
|
|
1058
|
-
>
|
|
1059
|
-
<div className="space-y-3">
|
|
1060
|
-
<Label className="font-medium text-sm">
|
|
1061
|
-
Select a task to track time for:
|
|
1062
|
-
</Label>
|
|
1063
|
-
<Select
|
|
1064
|
-
value={selectedTaskId}
|
|
1065
|
-
onValueChange={handleTaskSelectionChange}
|
|
1066
|
-
>
|
|
1067
|
-
<SelectTrigger className="@lg:text-base text-sm transition-all duration-200">
|
|
1068
|
-
<SelectValue placeholder="Choose a task or create new..." />
|
|
1069
|
-
</SelectTrigger>
|
|
1070
|
-
<SelectContent className="w-100">
|
|
1071
|
-
{tasks.map((task) => (
|
|
1072
|
-
<SelectItem
|
|
1073
|
-
key={task.id}
|
|
1074
|
-
value={task.id || ''}
|
|
1075
|
-
className="p-0"
|
|
1076
|
-
>
|
|
1077
|
-
<div className="flex w-full items-start gap-3 p-3">
|
|
1078
|
-
<div className="flex h-8 w-8 items-center justify-center rounded-lg border border-dynamic-blue/30 bg-linear-to-br from-dynamic-blue/20 to-dynamic-blue/10">
|
|
1079
|
-
<CheckCircle className="h-4 w-4 text-dynamic-blue" />
|
|
1080
|
-
</div>
|
|
1081
|
-
<div className="min-w-0 flex-1">
|
|
1082
|
-
<div className="flex items-center gap-2">
|
|
1083
|
-
<span className="font-medium text-sm">
|
|
1084
|
-
{task.name}
|
|
1085
|
-
</span>
|
|
1086
|
-
<ExternalLink className="h-3 w-3 text-muted-foreground" />
|
|
1087
|
-
</div>
|
|
1088
|
-
{task.description && (
|
|
1089
|
-
<p className="mt-1 line-clamp-2 text-muted-foreground text-xs">
|
|
1090
|
-
{task.description}
|
|
1091
|
-
</p>
|
|
1092
|
-
)}
|
|
1093
|
-
{task.board_name &&
|
|
1094
|
-
task.list_name && (
|
|
1095
|
-
<div className="mt-2 flex items-center gap-2">
|
|
1096
|
-
<div className="flex items-center gap-1.5 rounded-md bg-muted/60 px-2 py-1">
|
|
1097
|
-
<MapPin className="h-3 w-3 text-muted-foreground" />
|
|
1098
|
-
<span className="font-medium text-xs">
|
|
1099
|
-
{task.board_name}
|
|
1100
|
-
</span>
|
|
1101
|
-
</div>
|
|
1102
|
-
<div className="flex items-center gap-1.5 rounded-md border border-dynamic-green/20 bg-linear-to-r from-dynamic-green/10 to-dynamic-green/5 px-2 py-1">
|
|
1103
|
-
<Tag className="h-3 w-3 text-dynamic-green" />
|
|
1104
|
-
<span className="font-medium text-dynamic-green text-xs">
|
|
1105
|
-
{task.list_name}
|
|
1106
|
-
</span>
|
|
1107
|
-
</div>
|
|
1108
|
-
</div>
|
|
1109
|
-
)}
|
|
1110
|
-
</div>
|
|
1111
|
-
</div>
|
|
1112
|
-
</SelectItem>
|
|
1113
|
-
))}
|
|
1114
|
-
</SelectContent>
|
|
1115
|
-
</Select>
|
|
1116
|
-
|
|
1117
|
-
{!selectedTaskId && (
|
|
1118
|
-
<div className="text-center">
|
|
1119
|
-
<p className="mb-2 text-muted-foreground text-sm">
|
|
1120
|
-
No task selected? We'll help you create one!
|
|
1121
|
-
</p>
|
|
1122
|
-
</div>
|
|
1123
|
-
)}
|
|
1124
|
-
</div>
|
|
1125
|
-
|
|
1126
|
-
<Textarea
|
|
1127
|
-
placeholder="Add session notes (optional)"
|
|
1128
|
-
value={newSessionDescription}
|
|
1129
|
-
onChange={(e) =>
|
|
1130
|
-
setNewSessionDescription(e.target.value)
|
|
1131
|
-
}
|
|
1132
|
-
rows={2}
|
|
1133
|
-
className="@lg:text-base text-sm"
|
|
1134
|
-
/>
|
|
1135
|
-
|
|
1136
|
-
<Select
|
|
1137
|
-
value={selectedCategoryId}
|
|
1138
|
-
onValueChange={setSelectedCategoryId}
|
|
1139
|
-
>
|
|
1140
|
-
<SelectTrigger className="@lg:text-base text-sm">
|
|
1141
|
-
<SelectValue placeholder="Category (optional)" />
|
|
1142
|
-
</SelectTrigger>
|
|
1143
|
-
<SelectContent>
|
|
1144
|
-
{categories.map((category) => (
|
|
1145
|
-
<SelectItem
|
|
1146
|
-
key={category.id}
|
|
1147
|
-
value={category.id}
|
|
1148
|
-
>
|
|
1149
|
-
<div className="flex items-center gap-2">
|
|
1150
|
-
<div
|
|
1151
|
-
className={cn(
|
|
1152
|
-
'h-3 w-3 rounded-full',
|
|
1153
|
-
getCategoryColor(
|
|
1154
|
-
category.color || 'BLUE'
|
|
1155
|
-
)
|
|
1156
|
-
)}
|
|
1157
|
-
/>
|
|
1158
|
-
{category.name}
|
|
1159
|
-
</div>
|
|
1160
|
-
</SelectItem>
|
|
1161
|
-
))}
|
|
1162
|
-
</SelectContent>
|
|
1163
|
-
</Select>
|
|
1164
|
-
|
|
1165
|
-
<Button
|
|
1166
|
-
onClick={startTimer}
|
|
1167
|
-
disabled={isLoading}
|
|
1168
|
-
className="w-full border border-border bg-muted text-foreground hover:border-accent hover:bg-muted/80 dark:bg-muted dark:text-foreground dark:hover:bg-accent"
|
|
1169
|
-
size="lg"
|
|
1170
|
-
>
|
|
1171
|
-
<Play className="mr-2 h-4 w-4" />
|
|
1172
|
-
{selectedTaskId
|
|
1173
|
-
? 'Start Timer'
|
|
1174
|
-
: 'Create Task & Start Timer'}
|
|
1175
|
-
</Button>
|
|
1176
|
-
</TabsContent>
|
|
1177
|
-
|
|
1178
|
-
<TabsContent
|
|
1179
|
-
value="manual"
|
|
1180
|
-
className="fade-in-50 slide-in-from-bottom-2 animate-in space-y-4 duration-300"
|
|
1181
|
-
>
|
|
1182
|
-
<div className="space-y-2">
|
|
1183
|
-
<Input
|
|
1184
|
-
placeholder="What are you working on?"
|
|
1185
|
-
value={newSessionTitle}
|
|
1186
|
-
onChange={(e) =>
|
|
1187
|
-
handleManualTitleChange(e.target.value)
|
|
1188
|
-
}
|
|
1189
|
-
className="@lg:text-base text-sm"
|
|
1190
|
-
autoFocus={sessionMode === 'manual'}
|
|
1191
|
-
/>
|
|
1192
|
-
|
|
1193
|
-
{/* Task suggestion */}
|
|
1194
|
-
{showTaskSuggestion &&
|
|
1195
|
-
newSessionTitle.length > 2 && (
|
|
1196
|
-
<div className="rounded-lg border border-dynamic-blue/30 bg-linear-to-r from-dynamic-blue/10 to-dynamic-blue/5 p-3 shadow-sm">
|
|
1197
|
-
<div className="flex items-start justify-between gap-3">
|
|
1198
|
-
<div className="flex items-start gap-2">
|
|
1199
|
-
<div className="rounded-full bg-dynamic-blue/20 p-1">
|
|
1200
|
-
<Sparkles className="h-3 w-3 text-dynamic-blue" />
|
|
1201
|
-
</div>
|
|
1202
|
-
<div className="flex-1">
|
|
1203
|
-
<span className="font-medium text-dynamic-blue text-sm">
|
|
1204
|
-
Convert to task?
|
|
1205
|
-
</span>
|
|
1206
|
-
<p className="mt-0.5 text-muted-foreground text-xs">
|
|
1207
|
-
Create "{newSessionTitle}" as a new
|
|
1208
|
-
task for better organization and
|
|
1209
|
-
tracking.
|
|
1210
|
-
</p>
|
|
1211
|
-
</div>
|
|
1212
|
-
</div>
|
|
1213
|
-
<Button
|
|
1214
|
-
variant="outline"
|
|
1215
|
-
size="sm"
|
|
1216
|
-
onClick={createTaskFromManualSession}
|
|
1217
|
-
className="h-8 border-dynamic-blue/30 bg-dynamic-blue/10 text-dynamic-blue text-xs hover:bg-dynamic-blue/20"
|
|
1218
|
-
>
|
|
1219
|
-
Create Task
|
|
1220
|
-
</Button>
|
|
1221
|
-
</div>
|
|
1222
|
-
</div>
|
|
1223
|
-
)}
|
|
1224
|
-
|
|
1225
|
-
{/* Show selected task info */}
|
|
1226
|
-
{selectedTaskId && !showTaskSuggestion && (
|
|
1227
|
-
<div className="rounded-lg border border-dynamic-green/30 bg-linear-to-r from-dynamic-green/5 to-dynamic-green/3 p-4 shadow-sm">
|
|
1228
|
-
<div className="flex items-start gap-3">
|
|
1229
|
-
<div className="flex h-10 w-10 items-center justify-center rounded-lg border border-dynamic-green/30 bg-linear-to-br from-dynamic-green/20 to-dynamic-green/10">
|
|
1230
|
-
<CheckCircle className="h-5 w-5 text-dynamic-green" />
|
|
1231
|
-
</div>
|
|
1232
|
-
<div className="min-w-0 flex-1">
|
|
1233
|
-
<div className="flex items-center justify-between gap-2">
|
|
1234
|
-
<div className="flex items-center gap-2">
|
|
1235
|
-
<span className="font-semibold text-dynamic-green text-sm">
|
|
1236
|
-
Task Linked Successfully
|
|
1237
|
-
</span>
|
|
1238
|
-
<Button
|
|
1239
|
-
variant="ghost"
|
|
1240
|
-
size="sm"
|
|
1241
|
-
className="h-6 w-6 p-0 text-muted-foreground hover:text-foreground"
|
|
1242
|
-
>
|
|
1243
|
-
<ExternalLink className="h-3 w-3" />
|
|
1244
|
-
</Button>
|
|
1245
|
-
</div>
|
|
1246
|
-
<Button
|
|
1247
|
-
variant="ghost"
|
|
1248
|
-
size="sm"
|
|
1249
|
-
onClick={() => {
|
|
1250
|
-
setSelectedTaskId('');
|
|
1251
|
-
setShowTaskSuggestion(
|
|
1252
|
-
newSessionTitle.length > 2
|
|
1253
|
-
);
|
|
1254
|
-
}}
|
|
1255
|
-
className="h-7 px-2 text-muted-foreground text-xs hover:text-foreground"
|
|
1256
|
-
>
|
|
1257
|
-
Unlink
|
|
1258
|
-
</Button>
|
|
1259
|
-
</div>
|
|
1260
|
-
{(() => {
|
|
1261
|
-
const selectedTask = tasks.find(
|
|
1262
|
-
(t) => t.id === selectedTaskId
|
|
1263
|
-
);
|
|
1264
|
-
return selectedTask ? (
|
|
1265
|
-
<div className="mt-2 space-y-2">
|
|
1266
|
-
<p className="font-medium text-foreground text-sm">
|
|
1267
|
-
{selectedTask.name}
|
|
1268
|
-
</p>
|
|
1269
|
-
{selectedTask.description && (
|
|
1270
|
-
<p className="line-clamp-2 text-muted-foreground text-xs">
|
|
1271
|
-
{selectedTask.description}
|
|
1272
|
-
</p>
|
|
1273
|
-
)}
|
|
1274
|
-
{selectedTask.board_name &&
|
|
1275
|
-
selectedTask.list_name && (
|
|
1276
|
-
<div className="flex items-center gap-2">
|
|
1277
|
-
<div className="flex items-center gap-1.5 rounded-md bg-muted/60 px-2 py-1">
|
|
1278
|
-
<MapPin className="h-3 w-3 text-muted-foreground" />
|
|
1279
|
-
<span className="font-medium text-xs">
|
|
1280
|
-
{selectedTask.board_name}
|
|
1281
|
-
</span>
|
|
1282
|
-
</div>
|
|
1283
|
-
<div className="flex items-center gap-1.5 rounded-md border border-dynamic-green/20 bg-linear-to-r from-dynamic-green/10 to-dynamic-green/5 px-2 py-1">
|
|
1284
|
-
<Tag className="h-3 w-3 text-dynamic-green" />
|
|
1285
|
-
<span className="font-medium text-dynamic-green text-xs">
|
|
1286
|
-
{selectedTask.list_name}
|
|
1287
|
-
</span>
|
|
1288
|
-
</div>
|
|
1289
|
-
</div>
|
|
1290
|
-
)}
|
|
1291
|
-
<p className="text-dynamic-green/80 text-xs">
|
|
1292
|
-
Time will be automatically tracked
|
|
1293
|
-
for this task
|
|
1294
|
-
</p>
|
|
1295
|
-
</div>
|
|
1296
|
-
) : null;
|
|
1297
|
-
})()}
|
|
1298
|
-
</div>
|
|
1299
|
-
</div>
|
|
1300
|
-
</div>
|
|
1301
|
-
)}
|
|
1302
|
-
</div>
|
|
1303
|
-
|
|
1304
|
-
<Textarea
|
|
1305
|
-
placeholder="Add description (optional)"
|
|
1306
|
-
value={newSessionDescription}
|
|
1307
|
-
onChange={(e) =>
|
|
1308
|
-
setNewSessionDescription(e.target.value)
|
|
1309
|
-
}
|
|
1310
|
-
rows={3}
|
|
1311
|
-
className="@lg:text-base text-sm"
|
|
1312
|
-
/>
|
|
1313
|
-
|
|
1314
|
-
<Select
|
|
1315
|
-
value={selectedCategoryId}
|
|
1316
|
-
onValueChange={setSelectedCategoryId}
|
|
1317
|
-
>
|
|
1318
|
-
<SelectTrigger className="@lg:text-base text-sm">
|
|
1319
|
-
<SelectValue placeholder="Category (optional)" />
|
|
1320
|
-
</SelectTrigger>
|
|
1321
|
-
<SelectContent>
|
|
1322
|
-
{categories.map((category) => (
|
|
1323
|
-
<SelectItem
|
|
1324
|
-
key={category.id}
|
|
1325
|
-
value={category.id}
|
|
1326
|
-
>
|
|
1327
|
-
<div className="flex items-center gap-2">
|
|
1328
|
-
<div
|
|
1329
|
-
className={cn(
|
|
1330
|
-
'h-3 w-3 rounded-full',
|
|
1331
|
-
getCategoryColor(
|
|
1332
|
-
category.color || 'BLUE'
|
|
1333
|
-
)
|
|
1334
|
-
)}
|
|
1335
|
-
/>
|
|
1336
|
-
{category.name}
|
|
1337
|
-
</div>
|
|
1338
|
-
</SelectItem>
|
|
1339
|
-
))}
|
|
1340
|
-
</SelectContent>
|
|
1341
|
-
</Select>
|
|
1342
|
-
|
|
1343
|
-
<Button
|
|
1344
|
-
onClick={startTimer}
|
|
1345
|
-
disabled={!newSessionTitle.trim() || isLoading}
|
|
1346
|
-
className="w-full border border-border bg-muted text-foreground hover:border-accent hover:bg-muted/80 dark:bg-muted dark:text-foreground dark:hover:bg-accent"
|
|
1347
|
-
size="lg"
|
|
1348
|
-
>
|
|
1349
|
-
<Play className="mr-2 h-4 w-4" />
|
|
1350
|
-
Start Timer
|
|
1351
|
-
</Button>
|
|
1352
|
-
</TabsContent>
|
|
1353
|
-
</Tabs>
|
|
1354
|
-
|
|
1355
|
-
{/* Quick Actions */}
|
|
1356
|
-
{(recentSessions.length > 0 ||
|
|
1357
|
-
templates.length > 0) && (
|
|
1358
|
-
<div className="space-y-3">
|
|
1359
|
-
<div className="flex items-center justify-between">
|
|
1360
|
-
<Label className="text-muted-foreground text-xs">
|
|
1361
|
-
Quick Start:
|
|
1362
|
-
</Label>
|
|
1363
|
-
<Button
|
|
1364
|
-
variant="ghost"
|
|
1365
|
-
size="sm"
|
|
1366
|
-
onClick={() =>
|
|
1367
|
-
setShowQuickActions(!showQuickActions)
|
|
1368
|
-
}
|
|
1369
|
-
className="h-6 px-2 text-xs"
|
|
1370
|
-
>
|
|
1371
|
-
{showQuickActions ? 'Less' : 'More'}
|
|
1372
|
-
</Button>
|
|
1373
|
-
</div>
|
|
1374
|
-
|
|
1375
|
-
<div className="space-y-2">
|
|
1376
|
-
{/* Most recent session */}
|
|
1377
|
-
{recentSessions.length > 0 &&
|
|
1378
|
-
recentSessions[0] && (
|
|
1379
|
-
<Button
|
|
1380
|
-
variant="outline"
|
|
1381
|
-
size="sm"
|
|
1382
|
-
onClick={() => {
|
|
1383
|
-
const session = recentSessions[0];
|
|
1384
|
-
if (session) {
|
|
1385
|
-
duplicateSession(session);
|
|
1386
|
-
}
|
|
1387
|
-
}}
|
|
1388
|
-
className="w-full justify-start text-xs"
|
|
1389
|
-
>
|
|
1390
|
-
<RotateCcw className="mr-2 h-3 w-3" />
|
|
1391
|
-
Repeat: {recentSessions[0].title}
|
|
1392
|
-
</Button>
|
|
1393
|
-
)}
|
|
1394
|
-
|
|
1395
|
-
{/* Templates */}
|
|
1396
|
-
{showQuickActions &&
|
|
1397
|
-
templates.slice(0, 3).map((template) => (
|
|
1398
|
-
<Button
|
|
1399
|
-
key={`template-${template.title}`}
|
|
1400
|
-
variant="outline"
|
|
1401
|
-
size="sm"
|
|
1402
|
-
onClick={() => startFromTemplate(template)}
|
|
1403
|
-
className="w-full justify-start text-xs"
|
|
1404
|
-
>
|
|
1405
|
-
<Copy className="mr-2 h-3 w-3" />
|
|
1406
|
-
{template.title}
|
|
1407
|
-
<Badge
|
|
1408
|
-
variant="secondary"
|
|
1409
|
-
className="ml-auto text-xs"
|
|
1410
|
-
>
|
|
1411
|
-
{template.usage_count}×
|
|
1412
|
-
</Badge>
|
|
1413
|
-
</Button>
|
|
1414
|
-
))}
|
|
1415
|
-
</div>
|
|
1416
|
-
</div>
|
|
1417
|
-
)}
|
|
1418
|
-
</div>
|
|
1419
|
-
)}
|
|
1420
|
-
</CardContent>
|
|
1421
|
-
</Card>
|
|
1422
|
-
|
|
1423
|
-
{/* Completion Celebration */}
|
|
1424
|
-
{justCompleted && (
|
|
1425
|
-
<div className="fade-in fixed inset-0 z-50 flex animate-in items-center justify-center bg-black/20 backdrop-blur-sm duration-300">
|
|
1426
|
-
<div className="zoom-in animate-in rounded-lg border bg-background p-6 shadow-xl duration-300">
|
|
1427
|
-
<div className="text-center">
|
|
1428
|
-
<CheckCircle className="mx-auto mb-4 h-12 w-12 animate-pulse text-green-500" />
|
|
1429
|
-
<h3 className="mb-2 font-semibold text-lg">
|
|
1430
|
-
Session Completed!
|
|
1431
|
-
</h3>
|
|
1432
|
-
<p className="mb-1 text-muted-foreground">
|
|
1433
|
-
{justCompleted.title}
|
|
1434
|
-
</p>
|
|
1435
|
-
<p className="font-medium text-green-600 text-sm">
|
|
1436
|
-
{formatDuration(justCompleted.duration_seconds || 0)}{' '}
|
|
1437
|
-
tracked
|
|
1438
|
-
</p>
|
|
1439
|
-
</div>
|
|
1440
|
-
</div>
|
|
1441
|
-
</div>
|
|
1442
|
-
)}
|
|
1443
|
-
|
|
1444
|
-
{/* Stats Cards */}
|
|
1445
|
-
<div className="grid grid-cols-3 @lg:gap-4 gap-2">
|
|
1446
|
-
<Card className="group cursor-pointer transition-all hover:scale-105 hover:shadow-md">
|
|
1447
|
-
<CardContent className="@lg:p-4 p-3">
|
|
1448
|
-
<div className="flex items-center gap-2">
|
|
1449
|
-
<Calendar className="h-4 w-4 text-blue-500 transition-transform group-hover:scale-110" />
|
|
1450
|
-
<div className="min-w-0">
|
|
1451
|
-
<p className="@lg:text-sm text-muted-foreground text-xs">
|
|
1452
|
-
Today
|
|
1453
|
-
</p>
|
|
1454
|
-
<p className="truncate font-medium @lg:text-base text-sm transition-all">
|
|
1455
|
-
{formatDuration(timerStats.todayTime)}
|
|
1456
|
-
</p>
|
|
1457
|
-
</div>
|
|
1458
|
-
</div>
|
|
1459
|
-
</CardContent>
|
|
1460
|
-
</Card>
|
|
1461
|
-
|
|
1462
|
-
<Card className="group cursor-pointer transition-all hover:scale-105 hover:shadow-md">
|
|
1463
|
-
<CardContent className="@lg:p-4 p-3">
|
|
1464
|
-
<div className="flex items-center gap-2">
|
|
1465
|
-
<TrendingUp className="h-4 w-4 text-green-500 transition-transform group-hover:scale-110" />
|
|
1466
|
-
<div className="min-w-0">
|
|
1467
|
-
<p className="@lg:text-sm text-muted-foreground text-xs">
|
|
1468
|
-
Week
|
|
1469
|
-
</p>
|
|
1470
|
-
<p className="truncate font-medium @lg:text-base text-sm transition-all">
|
|
1471
|
-
{formatDuration(timerStats.weekTime)}
|
|
1472
|
-
</p>
|
|
1473
|
-
</div>
|
|
1474
|
-
</div>
|
|
1475
|
-
</CardContent>
|
|
1476
|
-
</Card>
|
|
1477
|
-
|
|
1478
|
-
<Card className="group cursor-pointer transition-all hover:scale-105 hover:shadow-md">
|
|
1479
|
-
<CardContent className="@lg:p-4 p-3">
|
|
1480
|
-
<div className="flex items-center gap-2">
|
|
1481
|
-
<Zap className="h-4 w-4 text-purple-500 transition-transform group-hover:scale-110" />
|
|
1482
|
-
<div className="min-w-0">
|
|
1483
|
-
<p className="@lg:text-sm text-muted-foreground text-xs">
|
|
1484
|
-
Month
|
|
1485
|
-
</p>
|
|
1486
|
-
<p className="truncate font-medium @lg:text-base text-sm transition-all">
|
|
1487
|
-
{formatDuration(timerStats.monthTime)}
|
|
1488
|
-
</p>
|
|
1489
|
-
</div>
|
|
1490
|
-
</div>
|
|
1491
|
-
</CardContent>
|
|
1492
|
-
</Card>
|
|
1493
|
-
</div>
|
|
1494
|
-
</TabsContent>
|
|
1495
|
-
|
|
1496
|
-
{/* Recent Sessions Tab */}
|
|
1497
|
-
<TabsContent value="recent" className="@container space-y-4">
|
|
1498
|
-
<Card className="transition-all hover:shadow-md">
|
|
1499
|
-
<CardHeader className="pb-3">
|
|
1500
|
-
<div className="flex items-center justify-between">
|
|
1501
|
-
<CardTitle className="flex items-center gap-2 @lg:text-lg text-base">
|
|
1502
|
-
<Zap className="@lg:h-5 h-4 @lg:w-5 w-4" />
|
|
1503
|
-
Recent Sessions
|
|
1504
|
-
</CardTitle>
|
|
1505
|
-
<Popover>
|
|
1506
|
-
<PopoverTrigger asChild>
|
|
1507
|
-
<Button variant="outline" size="sm">
|
|
1508
|
-
<Filter className="mr-2 h-4 w-4" />
|
|
1509
|
-
Filter
|
|
1510
|
-
</Button>
|
|
1511
|
-
</PopoverTrigger>
|
|
1512
|
-
<PopoverContent className="w-80" align="end">
|
|
1513
|
-
<div className="space-y-4">
|
|
1514
|
-
<div>
|
|
1515
|
-
<Label className="font-medium text-sm">
|
|
1516
|
-
Category
|
|
1517
|
-
</Label>
|
|
1518
|
-
<Select
|
|
1519
|
-
value={filterCategoryId}
|
|
1520
|
-
onValueChange={setFilterCategoryId}
|
|
1521
|
-
>
|
|
1522
|
-
<SelectTrigger>
|
|
1523
|
-
<SelectValue placeholder="All categories" />
|
|
1524
|
-
</SelectTrigger>
|
|
1525
|
-
<SelectContent>
|
|
1526
|
-
<SelectItem value="">
|
|
1527
|
-
All categories
|
|
1528
|
-
</SelectItem>
|
|
1529
|
-
{categories.map((category) => (
|
|
1530
|
-
<SelectItem
|
|
1531
|
-
key={category.id}
|
|
1532
|
-
value={category.id}
|
|
1533
|
-
>
|
|
1534
|
-
<div className="flex items-center gap-2">
|
|
1535
|
-
<div
|
|
1536
|
-
className={cn(
|
|
1537
|
-
'h-3 w-3 rounded-full',
|
|
1538
|
-
getCategoryColor(
|
|
1539
|
-
category.color || 'BLUE'
|
|
1540
|
-
)
|
|
1541
|
-
)}
|
|
1542
|
-
/>
|
|
1543
|
-
{category.name}
|
|
1544
|
-
</div>
|
|
1545
|
-
</SelectItem>
|
|
1546
|
-
))}
|
|
1547
|
-
</SelectContent>
|
|
1548
|
-
</Select>
|
|
1549
|
-
</div>
|
|
1550
|
-
<div>
|
|
1551
|
-
<Label className="font-medium text-sm">
|
|
1552
|
-
Task
|
|
1553
|
-
</Label>
|
|
1554
|
-
<Select
|
|
1555
|
-
value={filterTaskId}
|
|
1556
|
-
onValueChange={setFilterTaskId}
|
|
1557
|
-
>
|
|
1558
|
-
<SelectTrigger>
|
|
1559
|
-
<SelectValue placeholder="All tasks" />
|
|
1560
|
-
</SelectTrigger>
|
|
1561
|
-
<SelectContent>
|
|
1562
|
-
<SelectItem value="">All tasks</SelectItem>
|
|
1563
|
-
{tasks.map((task) => (
|
|
1564
|
-
<SelectItem
|
|
1565
|
-
key={task.id}
|
|
1566
|
-
value={task.id || ''}
|
|
1567
|
-
>
|
|
1568
|
-
{task.name}
|
|
1569
|
-
</SelectItem>
|
|
1570
|
-
))}
|
|
1571
|
-
</SelectContent>
|
|
1572
|
-
</Select>
|
|
1573
|
-
</div>
|
|
1574
|
-
</div>
|
|
1575
|
-
</PopoverContent>
|
|
1576
|
-
</Popover>
|
|
1577
|
-
</div>
|
|
1578
|
-
</CardHeader>
|
|
1579
|
-
<CardContent>
|
|
1580
|
-
{filteredSessions.length === 0 ? (
|
|
1581
|
-
<div className="py-8 text-center">
|
|
1582
|
-
<div className="relative mx-auto mb-3 h-16 w-16">
|
|
1583
|
-
<Zap className="h-16 w-16 text-muted-foreground/50" />
|
|
1584
|
-
{recentSessions.length === 0 && (
|
|
1585
|
-
<Sparkles className="absolute -top-1 -right-1 h-6 w-6 animate-pulse text-primary" />
|
|
1586
|
-
)}
|
|
1587
|
-
</div>
|
|
1588
|
-
<p className="@lg:text-base text-muted-foreground text-sm">
|
|
1589
|
-
{recentSessions.length === 0
|
|
1590
|
-
? 'Ready to start tracking time?'
|
|
1591
|
-
: 'No sessions match your filters'}
|
|
1592
|
-
</p>
|
|
1593
|
-
<p className="mt-1 @lg:text-sm text-muted-foreground text-xs">
|
|
1594
|
-
{recentSessions.length === 0
|
|
1595
|
-
? 'Start your first timer to see your productivity journey!'
|
|
1596
|
-
: 'Try adjusting your filters above'}
|
|
1597
|
-
</p>
|
|
1598
|
-
{recentSessions.length === 0 && (
|
|
1599
|
-
<Button
|
|
1600
|
-
variant="outline"
|
|
1601
|
-
size="sm"
|
|
1602
|
-
onClick={() => setActiveTab('current')}
|
|
1603
|
-
className="mt-4"
|
|
1604
|
-
>
|
|
1605
|
-
<Play className="mr-2 h-4 w-4" />
|
|
1606
|
-
Start First Timer
|
|
1607
|
-
</Button>
|
|
1608
|
-
)}
|
|
1609
|
-
</div>
|
|
1610
|
-
) : (
|
|
1611
|
-
<div className="@lg:max-h-125 max-h-96 space-y-3 overflow-y-auto">
|
|
1612
|
-
{filteredSessions.map((session) => (
|
|
1613
|
-
<div
|
|
1614
|
-
key={session.id}
|
|
1615
|
-
className={cn(
|
|
1616
|
-
'group relative rounded-lg border @lg:p-4 p-3 transition-all hover:bg-accent/50 hover:shadow-sm',
|
|
1617
|
-
justCompleted?.id === session.id &&
|
|
1618
|
-
'slide-in-from-top animate-in bg-green-50 ring-2 ring-green-500 duration-500 dark:bg-green-950/20'
|
|
1619
|
-
)}
|
|
1620
|
-
>
|
|
1621
|
-
<div className="flex items-start justify-between gap-3">
|
|
1622
|
-
<div className="min-w-0 flex-1">
|
|
1623
|
-
<h4 className="truncate font-medium @lg:text-base text-sm">
|
|
1624
|
-
{session.title}
|
|
1625
|
-
</h4>
|
|
1626
|
-
{session.description && (
|
|
1627
|
-
<p className="mt-1 line-clamp-2 @lg:text-sm text-muted-foreground text-xs">
|
|
1628
|
-
{session.description}
|
|
1629
|
-
</p>
|
|
1630
|
-
)}
|
|
1631
|
-
<div className="mt-2 flex flex-wrap items-center @lg:gap-2 gap-1">
|
|
1632
|
-
{session.category && (
|
|
1633
|
-
<Badge
|
|
1634
|
-
variant="secondary"
|
|
1635
|
-
className={cn(
|
|
1636
|
-
'@lg:text-sm text-xs',
|
|
1637
|
-
getCategoryColor(
|
|
1638
|
-
session.category.color || 'BLUE'
|
|
1639
|
-
)
|
|
1640
|
-
)}
|
|
1641
|
-
>
|
|
1642
|
-
{session.category.name}
|
|
1643
|
-
</Badge>
|
|
1644
|
-
)}
|
|
1645
|
-
{session.task && (
|
|
1646
|
-
<div className="flex items-center gap-1.5 rounded-md border border-dynamic-blue/20 bg-linear-to-r from-dynamic-blue/10 to-dynamic-blue/5 px-2 py-1">
|
|
1647
|
-
<CheckCircle className="h-3 w-3 text-dynamic-blue" />
|
|
1648
|
-
<span className="font-medium @lg:text-sm text-dynamic-blue text-xs">
|
|
1649
|
-
{session.task.name}
|
|
1650
|
-
</span>
|
|
1651
|
-
</div>
|
|
1652
|
-
)}
|
|
1653
|
-
</div>
|
|
1654
|
-
{session.task &&
|
|
1655
|
-
(() => {
|
|
1656
|
-
const taskWithDetails = tasks.find(
|
|
1657
|
-
(t) => t.id === session.task?.id
|
|
1658
|
-
);
|
|
1659
|
-
return taskWithDetails?.board_name &&
|
|
1660
|
-
taskWithDetails?.list_name ? (
|
|
1661
|
-
<div className="mt-1 flex items-center gap-2 text-muted-foreground text-xs">
|
|
1662
|
-
<div className="flex items-center gap-1">
|
|
1663
|
-
<MapPin className="h-3 w-3" />
|
|
1664
|
-
<span>
|
|
1665
|
-
{taskWithDetails.board_name}
|
|
1666
|
-
</span>
|
|
1667
|
-
</div>
|
|
1668
|
-
<span>•</span>
|
|
1669
|
-
<div className="flex items-center gap-1">
|
|
1670
|
-
<Tag className="h-3 w-3" />
|
|
1671
|
-
<span>
|
|
1672
|
-
{taskWithDetails.list_name}
|
|
1673
|
-
</span>
|
|
1674
|
-
</div>
|
|
1675
|
-
</div>
|
|
1676
|
-
) : null;
|
|
1677
|
-
})()}
|
|
1678
|
-
</div>
|
|
1679
|
-
|
|
1680
|
-
<div className="flex items-start gap-2">
|
|
1681
|
-
<div className="text-right">
|
|
1682
|
-
<p className="font-medium @lg:text-base text-sm">
|
|
1683
|
-
{session.duration_seconds
|
|
1684
|
-
? formatDuration(session.duration_seconds)
|
|
1685
|
-
: '-'}
|
|
1686
|
-
</p>
|
|
1687
|
-
<p className="@lg:text-sm text-muted-foreground text-xs">
|
|
1688
|
-
{new Date(
|
|
1689
|
-
session.start_time
|
|
1690
|
-
).toLocaleDateString()}
|
|
1691
|
-
</p>
|
|
1692
|
-
</div>
|
|
1693
|
-
|
|
1694
|
-
<DropdownMenu>
|
|
1695
|
-
<DropdownMenuTrigger asChild>
|
|
1696
|
-
<Button
|
|
1697
|
-
variant="ghost"
|
|
1698
|
-
size="sm"
|
|
1699
|
-
className="h-8 w-8 p-0 opacity-0 transition-opacity group-hover:opacity-100"
|
|
1700
|
-
>
|
|
1701
|
-
<MoreHorizontal className="h-4 w-4" />
|
|
1702
|
-
</Button>
|
|
1703
|
-
</DropdownMenuTrigger>
|
|
1704
|
-
<DropdownMenuContent align="end">
|
|
1705
|
-
<DropdownMenuItem
|
|
1706
|
-
onClick={() => resumeSession(session)}
|
|
1707
|
-
disabled={
|
|
1708
|
-
actionStates[`resume-${session.id}`]
|
|
1709
|
-
}
|
|
1710
|
-
>
|
|
1711
|
-
{actionStates[`resume-${session.id}`] ? (
|
|
1712
|
-
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
|
1713
|
-
) : (
|
|
1714
|
-
<RotateCcw className="mr-2 h-4 w-4" />
|
|
1715
|
-
)}
|
|
1716
|
-
Start New Session
|
|
1717
|
-
</DropdownMenuItem>
|
|
1718
|
-
<DropdownMenuItem
|
|
1719
|
-
onClick={() => duplicateSession(session)}
|
|
1720
|
-
>
|
|
1721
|
-
<Copy className="mr-2 h-4 w-4" />
|
|
1722
|
-
Duplicate
|
|
1723
|
-
</DropdownMenuItem>
|
|
1724
|
-
{/* <DropdownMenuItem
|
|
1725
|
-
onClick={() => openEditDialog(session)}
|
|
1726
|
-
>
|
|
1727
|
-
<Edit className="mr-2 h-4 w-4" />
|
|
1728
|
-
Edit Session
|
|
1729
|
-
</DropdownMenuItem> */}
|
|
1730
|
-
<Separator />
|
|
1731
|
-
<DropdownMenuItem
|
|
1732
|
-
onClick={() =>
|
|
1733
|
-
setSessionToDelete(session)
|
|
1734
|
-
}
|
|
1735
|
-
className="text-destructive focus:text-destructive"
|
|
1736
|
-
>
|
|
1737
|
-
<Trash2 className="mr-2 h-4 w-4" />
|
|
1738
|
-
Delete Session
|
|
1739
|
-
</DropdownMenuItem>
|
|
1740
|
-
</DropdownMenuContent>
|
|
1741
|
-
</DropdownMenu>
|
|
1742
|
-
</div>
|
|
1743
|
-
</div>
|
|
1744
|
-
</div>
|
|
1745
|
-
))}
|
|
1746
|
-
</div>
|
|
1747
|
-
)}
|
|
1748
|
-
</CardContent>
|
|
1749
|
-
</Card>
|
|
1750
|
-
</TabsContent>
|
|
1751
|
-
|
|
1752
|
-
{/* History Tab - Same as recent but with different API call */}
|
|
1753
|
-
<TabsContent value="history" className="@container space-y-4">
|
|
1754
|
-
<Card className="transition-all hover:shadow-md">
|
|
1755
|
-
<CardHeader className="pb-3">
|
|
1756
|
-
<CardTitle className="flex items-center gap-2 @lg:text-lg text-base">
|
|
1757
|
-
<History className="@lg:h-5 h-4 @lg:w-5 w-4" />
|
|
1758
|
-
Session History
|
|
1759
|
-
</CardTitle>
|
|
1760
|
-
</CardHeader>
|
|
1761
|
-
<CardContent>
|
|
1762
|
-
<div className="py-8 text-center">
|
|
1763
|
-
<History className="mx-auto mb-3 @lg:h-12 h-8 @lg:w-12 w-8 text-muted-foreground" />
|
|
1764
|
-
<p className="@lg:text-base text-muted-foreground text-sm">
|
|
1765
|
-
Full history view coming soon
|
|
1766
|
-
</p>
|
|
1767
|
-
<p className="mt-1 @lg:text-sm text-muted-foreground text-xs">
|
|
1768
|
-
Advanced filtering, date ranges, and export features
|
|
1769
|
-
</p>
|
|
1770
|
-
</div>
|
|
1771
|
-
</CardContent>
|
|
1772
|
-
</Card>
|
|
1773
|
-
</TabsContent>
|
|
1774
|
-
</div>
|
|
1775
|
-
</Tabs>
|
|
1776
|
-
</DialogContent>
|
|
1777
|
-
</Dialog>
|
|
1778
|
-
|
|
1779
|
-
{/* Edit Session Dialog */}
|
|
1780
|
-
<Dialog
|
|
1781
|
-
open={!!sessionToEdit}
|
|
1782
|
-
onOpenChange={() => setSessionToEdit(null)}
|
|
1783
|
-
>
|
|
1784
|
-
<DialogContent className="max-w-md">
|
|
1785
|
-
<DialogHeader>
|
|
1786
|
-
<DialogTitle>Edit Session</DialogTitle>
|
|
1787
|
-
<DialogDescription>
|
|
1788
|
-
Modify the details of this time tracking session
|
|
1789
|
-
</DialogDescription>
|
|
1790
|
-
</DialogHeader>
|
|
1791
|
-
<div className="space-y-4">
|
|
1792
|
-
<div>
|
|
1793
|
-
<Label htmlFor="edit-title">Title</Label>
|
|
1794
|
-
<Input
|
|
1795
|
-
id="edit-title"
|
|
1796
|
-
value={editTitle}
|
|
1797
|
-
onChange={(e) => setEditTitle(e.target.value)}
|
|
1798
|
-
placeholder="Session title"
|
|
1799
|
-
/>
|
|
1800
|
-
</div>
|
|
1801
|
-
<div>
|
|
1802
|
-
<Label htmlFor="edit-description">Description</Label>
|
|
1803
|
-
<Textarea
|
|
1804
|
-
id="edit-description"
|
|
1805
|
-
value={editDescription}
|
|
1806
|
-
onChange={(e) => setEditDescription(e.target.value)}
|
|
1807
|
-
placeholder="Optional description"
|
|
1808
|
-
rows={3}
|
|
1809
|
-
/>
|
|
1810
|
-
</div>
|
|
1811
|
-
<div className="grid grid-cols-2 gap-2">
|
|
1812
|
-
<div>
|
|
1813
|
-
<Label htmlFor="edit-category">Category</Label>
|
|
1814
|
-
<Select
|
|
1815
|
-
value={editCategoryId}
|
|
1816
|
-
onValueChange={setEditCategoryId}
|
|
1817
|
-
>
|
|
1818
|
-
<SelectTrigger>
|
|
1819
|
-
<SelectValue placeholder="Select category" />
|
|
1820
|
-
</SelectTrigger>
|
|
1821
|
-
<SelectContent>
|
|
1822
|
-
<SelectItem value="">No category</SelectItem>
|
|
1823
|
-
{categories.map((category) => (
|
|
1824
|
-
<SelectItem key={category.id} value={category.id}>
|
|
1825
|
-
{category.name}
|
|
1826
|
-
</SelectItem>
|
|
1827
|
-
))}
|
|
1828
|
-
</SelectContent>
|
|
1829
|
-
</Select>
|
|
1830
|
-
</div>
|
|
1831
|
-
<div>
|
|
1832
|
-
<Label htmlFor="edit-task">Task</Label>
|
|
1833
|
-
<Select value={editTaskId} onValueChange={setEditTaskId}>
|
|
1834
|
-
<SelectTrigger>
|
|
1835
|
-
<SelectValue placeholder="Select task" />
|
|
1836
|
-
</SelectTrigger>
|
|
1837
|
-
<SelectContent>
|
|
1838
|
-
<SelectItem value="">No task</SelectItem>
|
|
1839
|
-
{tasks.map((task) => (
|
|
1840
|
-
<SelectItem key={task.id} value={task.id || ''}>
|
|
1841
|
-
{task.name}
|
|
1842
|
-
</SelectItem>
|
|
1843
|
-
))}
|
|
1844
|
-
</SelectContent>
|
|
1845
|
-
</Select>
|
|
1846
|
-
</div>
|
|
1847
|
-
</div>
|
|
1848
|
-
{sessionToEdit && !sessionToEdit.is_running && (
|
|
1849
|
-
<div className="grid grid-cols-2 gap-2">
|
|
1850
|
-
<div>
|
|
1851
|
-
<Label htmlFor="edit-start-time">Start Time</Label>
|
|
1852
|
-
<Input
|
|
1853
|
-
id="edit-start-time"
|
|
1854
|
-
type="datetime-local"
|
|
1855
|
-
value={editStartTime}
|
|
1856
|
-
onChange={(e) => setEditStartTime(e.target.value)}
|
|
1857
|
-
/>
|
|
1858
|
-
</div>
|
|
1859
|
-
<div>
|
|
1860
|
-
<Label htmlFor="edit-end-time">End Time</Label>
|
|
1861
|
-
<Input
|
|
1862
|
-
id="edit-end-time"
|
|
1863
|
-
type="datetime-local"
|
|
1864
|
-
value={editEndTime}
|
|
1865
|
-
onChange={(e) => setEditEndTime(e.target.value)}
|
|
1866
|
-
/>
|
|
1867
|
-
</div>
|
|
1868
|
-
</div>
|
|
1869
|
-
)}
|
|
1870
|
-
<div className="flex gap-2 pt-4">
|
|
1871
|
-
<Button
|
|
1872
|
-
variant="outline"
|
|
1873
|
-
onClick={() => setSessionToEdit(null)}
|
|
1874
|
-
className="flex-1"
|
|
1875
|
-
>
|
|
1876
|
-
Cancel
|
|
1877
|
-
</Button>
|
|
1878
|
-
<Button
|
|
1879
|
-
onClick={saveEdit}
|
|
1880
|
-
disabled={isEditing || !editTitle.trim()}
|
|
1881
|
-
className="flex-1"
|
|
1882
|
-
>
|
|
1883
|
-
{isEditing ? 'Saving...' : 'Save Changes'}
|
|
1884
|
-
</Button>
|
|
1885
|
-
</div>
|
|
1886
|
-
</div>
|
|
1887
|
-
</DialogContent>
|
|
1888
|
-
</Dialog>
|
|
1889
|
-
|
|
1890
|
-
{/* Delete Confirmation Dialog */}
|
|
1891
|
-
<AlertDialog
|
|
1892
|
-
open={!!sessionToDelete}
|
|
1893
|
-
onOpenChange={() => setSessionToDelete(null)}
|
|
1894
|
-
>
|
|
1895
|
-
<AlertDialogContent>
|
|
1896
|
-
<AlertDialogHeader>
|
|
1897
|
-
<AlertDialogTitle>Delete Time Session</AlertDialogTitle>
|
|
1898
|
-
<AlertDialogDescription>
|
|
1899
|
-
Are you sure you want to delete the session "
|
|
1900
|
-
{sessionToDelete?.title}"? This action cannot be undone and will
|
|
1901
|
-
permanently remove the tracked time.
|
|
1902
|
-
</AlertDialogDescription>
|
|
1903
|
-
</AlertDialogHeader>
|
|
1904
|
-
<AlertDialogFooter>
|
|
1905
|
-
<AlertDialogCancel disabled={isDeleting}>Cancel</AlertDialogCancel>
|
|
1906
|
-
<AlertDialogAction
|
|
1907
|
-
onClick={deleteSession}
|
|
1908
|
-
disabled={isDeleting}
|
|
1909
|
-
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
|
1910
|
-
>
|
|
1911
|
-
{isDeleting ? 'Deleting...' : 'Delete Session'}
|
|
1912
|
-
</AlertDialogAction>
|
|
1913
|
-
</AlertDialogFooter>
|
|
1914
|
-
</AlertDialogContent>
|
|
1915
|
-
</AlertDialog>
|
|
1916
|
-
|
|
1917
|
-
{/* Task Creation Dialog */}
|
|
1918
|
-
<Dialog open={showTaskCreation} onOpenChange={setShowTaskCreation}>
|
|
1919
|
-
<DialogContent className="max-w-md">
|
|
1920
|
-
<DialogHeader>
|
|
1921
|
-
<DialogTitle className="flex items-center gap-2">
|
|
1922
|
-
<CheckCircle className="h-5 w-5" />
|
|
1923
|
-
Create New Task
|
|
1924
|
-
</DialogTitle>
|
|
1925
|
-
<DialogDescription>
|
|
1926
|
-
Create a new task to track time for. We'll start the timer
|
|
1927
|
-
automatically once the task is created.
|
|
1928
|
-
</DialogDescription>
|
|
1929
|
-
</DialogHeader>
|
|
1930
|
-
<div className="space-y-4">
|
|
1931
|
-
<div>
|
|
1932
|
-
<Label htmlFor="task-name">Task Name</Label>
|
|
1933
|
-
<Input
|
|
1934
|
-
id="task-name"
|
|
1935
|
-
value={newTaskName}
|
|
1936
|
-
onChange={(e) => setNewTaskName(e.target.value)}
|
|
1937
|
-
placeholder="What are you working on?"
|
|
1938
|
-
autoFocus
|
|
1939
|
-
/>
|
|
1940
|
-
</div>
|
|
1941
|
-
<div>
|
|
1942
|
-
<Label htmlFor="task-description">Description (Optional)</Label>
|
|
1943
|
-
<Textarea
|
|
1944
|
-
id="task-description"
|
|
1945
|
-
value={newTaskDescription}
|
|
1946
|
-
onChange={(e) => setNewTaskDescription(e.target.value)}
|
|
1947
|
-
placeholder="Add details about this task..."
|
|
1948
|
-
rows={3}
|
|
1949
|
-
/>
|
|
1950
|
-
</div>
|
|
1951
|
-
<div>
|
|
1952
|
-
<Label htmlFor="board-select">Board</Label>
|
|
1953
|
-
<Select
|
|
1954
|
-
value={selectedBoardId}
|
|
1955
|
-
onValueChange={(value) => {
|
|
1956
|
-
setSelectedBoardId(value);
|
|
1957
|
-
setSelectedListId(''); // Reset list when board changes
|
|
1958
|
-
}}
|
|
1959
|
-
>
|
|
1960
|
-
<SelectTrigger>
|
|
1961
|
-
<SelectValue placeholder="Select a board" />
|
|
1962
|
-
</SelectTrigger>
|
|
1963
|
-
<SelectContent>
|
|
1964
|
-
{boards.map((board) => (
|
|
1965
|
-
<SelectItem key={board.id} value={board.id}>
|
|
1966
|
-
{board.name}
|
|
1967
|
-
</SelectItem>
|
|
1968
|
-
))}
|
|
1969
|
-
</SelectContent>
|
|
1970
|
-
</Select>
|
|
1971
|
-
</div>
|
|
1972
|
-
{selectedBoardId && (
|
|
1973
|
-
<div>
|
|
1974
|
-
<Label htmlFor="list-select">List</Label>
|
|
1975
|
-
<Select
|
|
1976
|
-
value={selectedListId}
|
|
1977
|
-
onValueChange={setSelectedListId}
|
|
1978
|
-
>
|
|
1979
|
-
<SelectTrigger>
|
|
1980
|
-
<SelectValue placeholder="Select a list" />
|
|
1981
|
-
</SelectTrigger>
|
|
1982
|
-
<SelectContent>
|
|
1983
|
-
{availableLists.map((list) => (
|
|
1984
|
-
<SelectItem key={list.id} value={list.id}>
|
|
1985
|
-
<div className="flex items-center gap-2">
|
|
1986
|
-
<div
|
|
1987
|
-
className={cn(
|
|
1988
|
-
'h-3 w-3 rounded-full',
|
|
1989
|
-
getCategoryColor(list.color.toUpperCase())
|
|
1990
|
-
)}
|
|
1991
|
-
/>
|
|
1992
|
-
{list.name}
|
|
1993
|
-
</div>
|
|
1994
|
-
</SelectItem>
|
|
1995
|
-
))}
|
|
1996
|
-
</SelectContent>
|
|
1997
|
-
</Select>
|
|
1998
|
-
</div>
|
|
1999
|
-
)}
|
|
2000
|
-
<div className="flex gap-2 pt-4">
|
|
2001
|
-
<Button
|
|
2002
|
-
variant="outline"
|
|
2003
|
-
onClick={() => {
|
|
2004
|
-
setShowTaskCreation(false);
|
|
2005
|
-
setNewTaskName('');
|
|
2006
|
-
setNewTaskDescription('');
|
|
2007
|
-
setSelectedBoardId('');
|
|
2008
|
-
setSelectedListId('');
|
|
2009
|
-
}}
|
|
2010
|
-
className="flex-1"
|
|
2011
|
-
disabled={isCreatingTask}
|
|
2012
|
-
>
|
|
2013
|
-
Cancel
|
|
2014
|
-
</Button>
|
|
2015
|
-
<Button
|
|
2016
|
-
onClick={createTask}
|
|
2017
|
-
disabled={
|
|
2018
|
-
isCreatingTask || !newTaskName.trim() || !selectedListId
|
|
2019
|
-
}
|
|
2020
|
-
className="flex-1 border border-border bg-muted text-foreground hover:border-accent hover:bg-muted/80 dark:bg-muted dark:text-foreground dark:hover:bg-accent"
|
|
2021
|
-
>
|
|
2022
|
-
{isCreatingTask ? (
|
|
2023
|
-
<>
|
|
2024
|
-
<RefreshCw className="mr-2 h-4 w-4 animate-spin" />
|
|
2025
|
-
Creating...
|
|
2026
|
-
</>
|
|
2027
|
-
) : (
|
|
2028
|
-
<>
|
|
2029
|
-
<CheckCircle className="mr-2 h-4 w-4" />
|
|
2030
|
-
Create & Start Timer
|
|
2031
|
-
</>
|
|
2032
|
-
)}
|
|
2033
|
-
</Button>
|
|
2034
|
-
</div>
|
|
2035
|
-
</div>
|
|
2036
|
-
</DialogContent>
|
|
2037
|
-
</Dialog>
|
|
2038
|
-
</>
|
|
2039
|
-
);
|
|
2040
|
-
}
|