@tuturuuu/ui 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/biome.json +1 -1
  3. package/package.json +74 -71
  4. package/src/components/ui/accordion.tsx +1 -1
  5. package/src/components/ui/breadcrumb.tsx +1 -1
  6. package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
  7. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
  8. package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
  9. package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
  10. package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
  11. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
  12. package/src/components/ui/calendar.tsx +1 -1
  13. package/src/components/ui/carousel.tsx +1 -1
  14. package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
  15. package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
  16. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
  17. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
  18. package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
  19. package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
  20. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
  21. package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
  22. package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
  23. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
  24. package/src/components/ui/checkbox.tsx +1 -1
  25. package/src/components/ui/color-picker.tsx +1 -1
  26. package/src/components/ui/command.tsx +1 -1
  27. package/src/components/ui/context-menu.tsx +5 -1
  28. package/src/components/ui/custom/__tests__/settings-dialog-search.test.ts +78 -0
  29. package/src/components/ui/custom/__tests__/settings-dialog-shell-compile-graph.test.ts +76 -0
  30. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
  31. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +46 -1
  32. package/src/components/ui/custom/combobox.test.tsx +195 -0
  33. package/src/components/ui/custom/combobox.tsx +273 -156
  34. package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
  35. package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
  36. package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
  37. package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
  38. package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
  39. package/src/components/ui/custom/nav-link.test.tsx +165 -0
  40. package/src/components/ui/custom/nav-link.tsx +69 -11
  41. package/src/components/ui/custom/navigation.tsx +1 -0
  42. package/src/components/ui/custom/settings/task-settings.tsx +104 -0
  43. package/src/components/ui/custom/settings-dialog-search-loader.d.ts +5 -0
  44. package/src/components/ui/custom/settings-dialog-search-loader.js +3 -0
  45. package/src/components/ui/custom/settings-dialog-search.ts +75 -0
  46. package/src/components/ui/custom/settings-dialog-shell.tsx +65 -28
  47. package/src/components/ui/custom/theme-toggle.tsx +1 -1
  48. package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
  49. package/src/components/ui/custom/workspace-select.tsx +25 -19
  50. package/src/components/ui/dialog.test.tsx +52 -0
  51. package/src/components/ui/dialog.tsx +6 -2
  52. package/src/components/ui/dropdown-menu.tsx +5 -1
  53. package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
  54. package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
  55. package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
  56. package/src/components/ui/finance/debts/debts-page.tsx +15 -2
  57. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
  58. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
  59. package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
  60. package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
  61. package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
  62. package/src/components/ui/finance/invoices/utils.ts +3 -1
  63. package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
  64. package/src/components/ui/finance/transactions/form-types.ts +1 -0
  65. package/src/components/ui/finance/transactions/form.tsx +2 -0
  66. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
  67. package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
  68. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
  69. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
  70. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
  71. package/src/components/ui/finance/wallets/form.test.tsx +51 -3
  72. package/src/components/ui/finance/wallets/form.tsx +15 -4
  73. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  74. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
  75. package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
  76. package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
  77. package/src/components/ui/input-otp.tsx +1 -1
  78. package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
  79. package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
  80. package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
  81. package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
  82. package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
  83. package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
  84. package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
  85. package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
  86. package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
  87. package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
  88. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
  89. package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
  90. package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
  91. package/src/components/ui/navigation-menu.tsx +1 -1
  92. package/src/components/ui/pagination.tsx +1 -1
  93. package/src/components/ui/radio-group.tsx +1 -1
  94. package/src/components/ui/select.tsx +5 -1
  95. package/src/components/ui/sheet.tsx +1 -1
  96. package/src/components/ui/sidebar.tsx +1 -1
  97. package/src/components/ui/storefront/cart-popover.tsx +61 -0
  98. package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
  99. package/src/components/ui/storefront/cart-summary.tsx +93 -154
  100. package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
  101. package/src/components/ui/storefront/listing-card.tsx +1 -1
  102. package/src/components/ui/storefront/merch-sections.tsx +70 -0
  103. package/src/components/ui/storefront/product-detail.tsx +1 -1
  104. package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
  105. package/src/components/ui/storefront/storefront-surface.tsx +101 -166
  106. package/src/components/ui/storefront/types.ts +4 -0
  107. package/src/components/ui/storefront/utils.ts +6 -0
  108. package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
  109. package/src/components/ui/text-editor/background-color-extension.ts +62 -0
  110. package/src/components/ui/text-editor/color-controls.tsx +284 -0
  111. package/src/components/ui/text-editor/editor.tsx +69 -14
  112. package/src/components/ui/text-editor/extensions.ts +8 -2
  113. package/src/components/ui/text-editor/highlight-extension.ts +22 -0
  114. package/src/components/ui/text-editor/tool-bar.tsx +9 -16
  115. package/src/components/ui/toast.tsx +1 -1
  116. package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +286 -0
  117. package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -0
  118. package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
  119. package/src/components/ui/tu-do/boards/board-share-dialog.tsx +15 -226
  120. package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +351 -0
  121. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +121 -39
  122. package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +7 -0
  123. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
  124. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
  125. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
  126. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-types.ts +3 -3
  127. package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
  128. package/src/components/ui/tu-do/boards/boardId/kanban/data/use-bulk-resources.ts +59 -5
  129. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
  130. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
  131. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/drag-preview.tsx +20 -1
  132. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
  133. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
  134. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
  135. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
  136. package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
  137. package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
  138. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
  139. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
  140. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
  141. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
  142. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
  143. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
  144. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
  145. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
  146. package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
  147. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +642 -5
  148. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +224 -15
  149. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +535 -53
  150. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +101 -33
  151. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +235 -113
  152. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +50 -5
  153. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +12 -2
  154. package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +10 -1
  155. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
  156. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +20 -0
  157. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +10 -0
  158. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +271 -36
  159. package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
  160. package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
  161. package/src/components/ui/tu-do/boards/boardId/task-list.tsx +22 -0
  162. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +9 -0
  163. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +9 -0
  164. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +9 -0
  165. package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +35 -3
  166. package/src/components/ui/tu-do/boards/form.tsx +1 -1
  167. package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
  168. package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
  169. package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
  170. package/src/components/ui/tu-do/hooks/__tests__/useTaskLabelManagement.test.tsx +48 -0
  171. package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +144 -0
  172. package/src/components/ui/tu-do/hooks/useTaskDialog.ts +7 -0
  173. package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +115 -106
  174. package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +115 -122
  175. package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
  176. package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
  177. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
  178. package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
  179. package/src/components/ui/tu-do/progress/task-progress-import-panel.tsx +60 -0
  180. package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +156 -0
  181. package/src/components/ui/tu-do/progress/task-progress-page.tsx +348 -0
  182. package/src/components/ui/tu-do/progress/task-progress-panels.tsx +301 -0
  183. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +26 -0
  184. package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +81 -10
  185. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +141 -1
  186. package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +377 -36
  187. package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +374 -0
  188. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +419 -5
  189. package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +38 -0
  190. package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
  191. package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
  192. package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +3 -0
  193. package/src/components/ui/tu-do/shared/assignee-select.tsx +77 -26
  194. package/src/components/ui/tu-do/shared/board-client.tsx +15 -10
  195. package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
  196. package/src/components/ui/tu-do/shared/board-header.tsx +471 -975
  197. package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
  198. package/src/components/ui/tu-do/shared/board-switcher.tsx +244 -220
  199. package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
  200. package/src/components/ui/tu-do/shared/board-views.tsx +577 -85
  201. package/src/components/ui/tu-do/shared/list-view.tsx +246 -2
  202. package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
  203. package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
  204. package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
  205. package/src/components/ui/tu-do/shared/task-cache-patches.ts +394 -0
  206. package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +21 -1
  207. package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +5 -1
  208. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +25 -2
  209. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +7 -1
  210. package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
  211. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-data.ts +79 -10
  212. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +76 -77
  213. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +63 -0
  214. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +78 -69
  215. package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +28 -8
  216. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +14 -3
  217. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +6 -1
  218. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +6 -1
  219. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +6 -1
  220. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +8 -0
  221. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
  222. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
  223. package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
  224. package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +8 -1
  225. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +150 -0
  226. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +61 -35
  227. package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +44 -2
  228. package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
  229. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +11 -1
  230. package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
  231. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +11 -0
  232. package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +2 -0
  233. package/src/declarations.d.ts +1 -0
  234. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
  235. package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
  236. package/src/hooks/__tests__/useBoardPresence.test.tsx +191 -0
  237. package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
  238. package/src/hooks/use-calendar-sync.tsx +247 -243
  239. package/src/hooks/use-calendar.tsx +323 -138
  240. package/src/hooks/use-task-actions.ts +24 -0
  241. package/src/hooks/use-user-workspace-config.ts +75 -0
  242. package/src/hooks/use-workspace-currency.ts +8 -3
  243. package/src/hooks/useBoardPresence.ts +364 -0
  244. package/src/hooks/useBoardRealtimeEventHandler.ts +45 -90
  245. package/src/lib/workspace-actions.ts +2 -6
@@ -1,15 +1,62 @@
1
1
  'use client';
2
2
 
3
- import { AlertTriangle, CalendarClock } from '@tuturuuu/icons';
3
+ import {
4
+ AlertTriangle,
5
+ ArrowDownAZ,
6
+ ArrowUpAZ,
7
+ CalendarClock,
8
+ ChevronLeft,
9
+ ChevronRight,
10
+ ExternalLink,
11
+ FileText,
12
+ Filter,
13
+ Pin,
14
+ PinOff,
15
+ RotateCcw,
16
+ } from '@tuturuuu/icons';
4
17
  import type { Task } from '@tuturuuu/types/primitives/Task';
5
18
  import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
6
19
  import { Badge } from '@tuturuuu/ui/badge';
20
+ import { Button } from '@tuturuuu/ui/button';
21
+ import { Card } from '@tuturuuu/ui/card';
22
+ import {
23
+ DropdownMenu,
24
+ DropdownMenuCheckboxItem,
25
+ DropdownMenuContent,
26
+ DropdownMenuItem,
27
+ DropdownMenuRadioGroup,
28
+ DropdownMenuRadioItem,
29
+ DropdownMenuSeparator,
30
+ DropdownMenuTrigger,
31
+ } from '@tuturuuu/ui/dropdown-menu';
7
32
  import { cn } from '@tuturuuu/utils/format';
33
+ import type React from 'react';
34
+ import { useMemo, useState } from 'react';
8
35
  import { TaskCard } from '../../task';
9
36
  import type { KanbanDeadlineSections } from './kanban-deadline-tasks';
10
37
 
38
+ export type KanbanDeadlineSection = keyof KanbanDeadlineSections;
39
+ export type KanbanDeadlineCollapsedState = Partial<
40
+ Record<KanbanDeadlineSection, boolean>
41
+ >;
42
+
11
43
  export interface KanbanDeadlineLabels {
44
+ collapseSection?: (name: string) => string;
45
+ expandSection?: (name: string) => string;
46
+ filter?: string;
12
47
  overdue: string;
48
+ pinSection?: (name: string) => string;
49
+ reset?: string;
50
+ showDocuments?: string;
51
+ showExternalTasks?: string;
52
+ sort?: string;
53
+ sortCreatedAsc?: string;
54
+ sortCreatedDesc?: string;
55
+ sortDueAsc?: string;
56
+ sortDueDesc?: string;
57
+ sortNameAsc?: string;
58
+ sortSourceAsc?: string;
59
+ unpinSection?: (name: string) => string;
13
60
  upcoming: string;
14
61
  }
15
62
 
@@ -18,12 +65,27 @@ interface KanbanDeadlinePanelsProps {
18
65
  boardId: string;
19
66
  bulkUpdateCustomDueDate: (date: Date | null) => Promise<void>;
20
67
  isPersonalWorkspace: boolean;
68
+ canUseBoardAssignees?: boolean;
69
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
21
70
  labels: KanbanDeadlineLabels;
22
71
  onClearSelection: () => void;
72
+ onSectionCollapsedChange?: (
73
+ section: KanbanDeadlineSection,
74
+ collapsed: boolean
75
+ ) => void;
76
+ onSectionPinnedChange?: (
77
+ section: KanbanDeadlineSection,
78
+ pinned: boolean
79
+ ) => void;
23
80
  onTaskSelect: (taskId: string, event: React.MouseEvent) => void;
24
81
  onUpdate: () => void;
25
82
  optimisticUpdateInProgress: Set<string>;
26
83
  sections: KanbanDeadlineSections;
84
+ loading?: boolean;
85
+ collapsedSections?: KanbanDeadlineCollapsedState;
86
+ pinnedSections?: KanbanDeadlineCollapsedState;
87
+ stickyOffsets?: Partial<Record<KanbanDeadlineSection, string>>;
88
+ deadlineNow?: number;
27
89
  selectedTasks: Set<string>;
28
90
  taskLists: TaskList[];
29
91
  isMultiSelectMode: boolean;
@@ -33,11 +95,102 @@ interface KanbanDeadlinePanelsProps {
33
95
  interface DeadlineSectionConfig {
34
96
  icon: typeof AlertTriangle;
35
97
  label: string;
98
+ collapsedClassName: string;
36
99
  panelClassName: string;
37
- section: keyof KanbanDeadlineSections;
100
+ section: KanbanDeadlineSection;
38
101
  titleClassName: string;
39
102
  }
40
103
 
104
+ type DeadlineTaskSortBy =
105
+ | 'created-asc'
106
+ | 'created-desc'
107
+ | 'due-asc'
108
+ | 'due-desc'
109
+ | 'name-asc'
110
+ | 'source-asc';
111
+
112
+ const DEFAULT_DEADLINE_TASK_SORT_BY: DeadlineTaskSortBy = 'due-asc';
113
+ const DOCUMENT_LIST_STATUS = 'documents';
114
+
115
+ function getTaskTime(value: string | null | undefined) {
116
+ if (!value) return null;
117
+ const time = new Date(value).getTime();
118
+ return Number.isFinite(time) ? time : null;
119
+ }
120
+
121
+ function compareNullableTaskTime(
122
+ a: string | null | undefined,
123
+ b: string | null | undefined,
124
+ ascending: boolean
125
+ ) {
126
+ const aTime = getTaskTime(a);
127
+ const bTime = getTaskTime(b);
128
+
129
+ if (aTime === null && bTime === null) return 0;
130
+ if (aTime === null) return 1;
131
+ if (bTime === null) return -1;
132
+
133
+ return ascending ? aTime - bTime : bTime - aTime;
134
+ }
135
+
136
+ function getDeadlineTaskSourceSortText(task: Task) {
137
+ return [
138
+ task.source_workspace_name,
139
+ task.source_board_name,
140
+ task.source_list_name,
141
+ task.name,
142
+ ]
143
+ .filter(Boolean)
144
+ .join(' / ')
145
+ .toLowerCase();
146
+ }
147
+
148
+ function sortDeadlineTasks(tasks: Task[], sortBy: DeadlineTaskSortBy) {
149
+ const sorted = [...tasks];
150
+
151
+ sorted.sort((a, b) => {
152
+ switch (sortBy) {
153
+ case 'created-asc':
154
+ return (
155
+ compareNullableTaskTime(a.created_at, b.created_at, true) ||
156
+ a.name.localeCompare(b.name)
157
+ );
158
+ case 'created-desc':
159
+ return (
160
+ compareNullableTaskTime(a.created_at, b.created_at, false) ||
161
+ a.name.localeCompare(b.name)
162
+ );
163
+ case 'due-desc':
164
+ return (
165
+ compareNullableTaskTime(a.end_date, b.end_date, false) ||
166
+ compareNullableTaskTime(a.created_at, b.created_at, false) ||
167
+ a.name.localeCompare(b.name)
168
+ );
169
+ case 'name-asc':
170
+ return (
171
+ a.name.localeCompare(b.name) ||
172
+ compareNullableTaskTime(a.end_date, b.end_date, true)
173
+ );
174
+ case 'source-asc':
175
+ return (
176
+ getDeadlineTaskSourceSortText(a).localeCompare(
177
+ getDeadlineTaskSourceSortText(b)
178
+ ) ||
179
+ compareNullableTaskTime(a.end_date, b.end_date, true) ||
180
+ a.name.localeCompare(b.name)
181
+ );
182
+ default:
183
+ return (
184
+ compareNullableTaskTime(a.end_date, b.end_date, true) ||
185
+ compareNullableTaskTime(a.created_at, b.created_at, false) ||
186
+ a.name.localeCompare(b.name)
187
+ );
188
+ }
189
+ });
190
+
191
+ return sorted;
192
+ }
193
+
41
194
  function getFallbackTaskList(lists: TaskList[]) {
42
195
  return (
43
196
  lists.find((list) => list.status === 'active') ??
@@ -46,6 +199,21 @@ function getFallbackTaskList(lists: TaskList[]) {
46
199
  );
47
200
  }
48
201
 
202
+ function isDocumentDeadlineTask(task: Task, taskList?: TaskList) {
203
+ return (
204
+ task.source_list_status === DOCUMENT_LIST_STATUS ||
205
+ taskList?.status === DOCUMENT_LIST_STATUS
206
+ );
207
+ }
208
+
209
+ function isExternalDeadlineTask(task: Task, taskList?: TaskList) {
210
+ return (
211
+ task.is_personal_external === true ||
212
+ taskList?.is_external_staging === true ||
213
+ Boolean(task.source_workspace_id)
214
+ );
215
+ }
216
+
49
217
  function getTaskListForDeadlineTask(task: Task, lists: TaskList[]) {
50
218
  return (
51
219
  lists.find((list) => String(list.id) === String(task.list_id)) ??
@@ -53,18 +221,56 @@ function getTaskListForDeadlineTask(task: Task, lists: TaskList[]) {
53
221
  );
54
222
  }
55
223
 
224
+ function DeadlineSectionSkeleton({
225
+ section,
226
+ }: {
227
+ section: KanbanDeadlineSection;
228
+ }) {
229
+ return (
230
+ <div
231
+ aria-hidden="true"
232
+ className="space-y-2"
233
+ data-testid={`kanban-deadline-section-${section}-loading`}
234
+ >
235
+ {Array.from({ length: 3 }, (_, index) => (
236
+ <div
237
+ className="rounded-lg border border-border/60 bg-background/60 p-3"
238
+ key={index}
239
+ >
240
+ <div className="h-3 w-3/4 animate-pulse rounded bg-muted" />
241
+ <div className="mt-3 h-2 w-1/2 animate-pulse rounded bg-muted" />
242
+ <div className="mt-2 flex gap-2">
243
+ <div className="h-2 w-14 animate-pulse rounded bg-muted" />
244
+ <div className="h-2 w-10 animate-pulse rounded bg-muted" />
245
+ </div>
246
+ </div>
247
+ ))}
248
+ </div>
249
+ );
250
+ }
251
+
56
252
  function DeadlineSection({
57
253
  availableLists,
58
254
  boardId,
59
255
  bulkUpdateCustomDueDate,
256
+ collapsed,
60
257
  config,
258
+ deadlineNow,
259
+ loading,
260
+ labels,
61
261
  isMultiSelectMode,
62
262
  isPersonalWorkspace,
263
+ canUseBoardAssignees,
264
+ assigneeMemberSource,
63
265
  onClearSelection,
266
+ onCollapsedChange,
267
+ onPinnedChange,
64
268
  onTaskSelect,
65
269
  onUpdate,
66
270
  optimisticUpdateInProgress,
271
+ pinned,
67
272
  selectedTasks,
273
+ stickyOffset,
68
274
  taskLists,
69
275
  tasks,
70
276
  workspaceId,
@@ -72,31 +278,125 @@ function DeadlineSection({
72
278
  availableLists: TaskList[];
73
279
  boardId: string;
74
280
  bulkUpdateCustomDueDate: (date: Date | null) => Promise<void>;
281
+ collapsed: boolean;
75
282
  config: DeadlineSectionConfig;
283
+ deadlineNow?: number;
284
+ loading?: boolean;
285
+ labels: KanbanDeadlineLabels;
76
286
  isMultiSelectMode: boolean;
77
287
  isPersonalWorkspace: boolean;
288
+ canUseBoardAssignees?: boolean;
289
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
78
290
  onClearSelection: () => void;
291
+ onCollapsedChange?: (
292
+ section: KanbanDeadlineSection,
293
+ collapsed: boolean
294
+ ) => void;
295
+ onPinnedChange?: (section: KanbanDeadlineSection, pinned: boolean) => void;
79
296
  onTaskSelect: (taskId: string, event: React.MouseEvent) => void;
80
297
  onUpdate: () => void;
81
298
  optimisticUpdateInProgress: Set<string>;
299
+ pinned?: boolean;
82
300
  selectedTasks: Set<string>;
301
+ stickyOffset?: string;
83
302
  taskLists: TaskList[];
84
303
  tasks: Task[];
85
304
  workspaceId: string;
86
305
  }) {
87
- if (tasks.length === 0) return null;
88
-
89
306
  const Icon = config.icon;
307
+ const collapseLabel =
308
+ labels.collapseSection?.(config.label) ?? `Collapse ${config.label}`;
309
+ const expandLabel =
310
+ labels.expandSection?.(config.label) ?? `Expand ${config.label}`;
311
+ const pinLabel = pinned
312
+ ? (labels.unpinSection?.(config.label) ?? `Unpin ${config.label}`)
313
+ : (labels.pinSection?.(config.label) ?? `Pin ${config.label}`);
314
+ const [includeDocuments, setIncludeDocuments] = useState(true);
315
+ const [includeExternal, setIncludeExternal] = useState(true);
316
+ const [sortBy, setSortBy] = useState<DeadlineTaskSortBy>(
317
+ DEFAULT_DEADLINE_TASK_SORT_BY
318
+ );
319
+ const taskListById = useMemo(
320
+ () => new Map(taskLists.map((list) => [String(list.id), list] as const)),
321
+ [taskLists]
322
+ );
323
+ const visibleTasks = useMemo(() => {
324
+ const filteredTasks = tasks.filter((task) => {
325
+ const taskList = taskListById.get(String(task.list_id));
326
+
327
+ if (!includeDocuments && isDocumentDeadlineTask(task, taskList)) {
328
+ return false;
329
+ }
330
+
331
+ if (!includeExternal && isExternalDeadlineTask(task, taskList)) {
332
+ return false;
333
+ }
334
+
335
+ return true;
336
+ });
337
+
338
+ return sortDeadlineTasks(filteredTasks, sortBy);
339
+ }, [includeDocuments, includeExternal, sortBy, taskListById, tasks]);
340
+ const filterCount = (includeDocuments ? 0 : 1) + (includeExternal ? 0 : 1);
341
+ const stickyStyle: React.CSSProperties | undefined = stickyOffset
342
+ ? {
343
+ left: `calc(var(--kanban-snap-left-padding) + ${stickyOffset})`,
344
+ }
345
+ : undefined;
346
+
347
+ if (collapsed) {
348
+ return (
349
+ <Card
350
+ className={cn(
351
+ 'group flex h-full w-14 shrink-0 snap-start flex-col items-center overflow-hidden rounded-xl border border-dashed transition-all duration-200 hover:shadow-md',
352
+ stickyOffset && 'sticky z-30',
353
+ config.collapsedClassName
354
+ )}
355
+ data-kanban-pinned-special={stickyOffset ? 'true' : undefined}
356
+ data-testid={`kanban-deadline-section-${config.section}-collapsed`}
357
+ style={stickyStyle}
358
+ >
359
+ <button
360
+ type="button"
361
+ className={cn(
362
+ 'flex h-full w-full flex-col items-center gap-3 rounded-xl px-1 py-3 transition-colors focus-visible:outline-none focus-visible:ring-2',
363
+ config.titleClassName
364
+ )}
365
+ title={expandLabel}
366
+ aria-label={expandLabel}
367
+ onClick={() => onCollapsedChange?.(config.section, false)}
368
+ >
369
+ <ChevronRight className="h-4 w-4 shrink-0" />
370
+ <Badge
371
+ variant="secondary"
372
+ className="h-5 min-w-5 justify-center px-1 text-[10px]"
373
+ >
374
+ {visibleTasks.length}
375
+ </Badge>
376
+ <span
377
+ className="max-h-48 truncate font-medium text-[11px]"
378
+ style={{ writingMode: 'vertical-rl' }}
379
+ >
380
+ {config.label}
381
+ </span>
382
+ </button>
383
+ </Card>
384
+ );
385
+ }
90
386
 
91
387
  return (
92
- <div
388
+ <Card
93
389
  className={cn(
94
- 'flex min-h-0 flex-1 flex-col overflow-hidden rounded-lg border shadow-xs',
390
+ 'flex h-full w-[var(--kanban-column-width)] shrink-0 snap-start flex-col overflow-hidden rounded-xl border border-dashed shadow-xs',
391
+ stickyOffset && 'sticky z-30',
95
392
  config.panelClassName
96
393
  )}
394
+ data-kanban-pinned-special={stickyOffset ? 'true' : undefined}
395
+ data-testid={`kanban-deadline-section-${config.section}`}
396
+ style={stickyStyle}
97
397
  >
98
- <div className="flex items-center justify-between gap-3 border-border/70 border-b px-3 py-2">
99
- <div className="flex min-w-0 items-center gap-2">
398
+ <div className="flex items-center justify-between gap-3 border-border/70 border-b p-3">
399
+ <div className="flex min-w-0 flex-1 items-center gap-2">
100
400
  <span
101
401
  className={cn(
102
402
  'inline-flex size-6 shrink-0 items-center justify-center rounded-md border bg-background/70',
@@ -113,45 +413,211 @@ function DeadlineSection({
113
413
  >
114
414
  {config.label}
115
415
  </h3>
416
+ <Badge
417
+ className="h-5 px-1.5 text-[10px]"
418
+ data-testid={`kanban-deadline-section-${config.section}-count`}
419
+ variant="outline"
420
+ >
421
+ {visibleTasks.length}
422
+ </Badge>
423
+ </div>
424
+ <div className="flex items-center gap-1">
425
+ <DropdownMenu>
426
+ <DropdownMenuTrigger asChild>
427
+ <Button
428
+ type="button"
429
+ variant="ghost"
430
+ size="xs"
431
+ className={cn(
432
+ 'relative h-7 w-7 p-0 hover:bg-muted/40',
433
+ config.titleClassName,
434
+ filterCount > 0 && 'bg-muted/40'
435
+ )}
436
+ title={labels.filter ?? 'Filters'}
437
+ aria-label={labels.filter ?? 'Filters'}
438
+ >
439
+ <Filter className="h-3.5 w-3.5" />
440
+ {filterCount > 0 && (
441
+ <span className="absolute -top-0.5 -right-0.5 flex h-3.5 min-w-3.5 items-center justify-center rounded-full bg-current px-0.5 font-medium text-[9px] text-background">
442
+ {filterCount}
443
+ </span>
444
+ )}
445
+ </Button>
446
+ </DropdownMenuTrigger>
447
+ <DropdownMenuContent align="end" className="w-64">
448
+ <DropdownMenuCheckboxItem
449
+ checked={includeDocuments}
450
+ onCheckedChange={(checked) =>
451
+ setIncludeDocuments(checked === true)
452
+ }
453
+ onSelect={(event) => event.preventDefault()}
454
+ >
455
+ <FileText className="mr-2 h-3.5 w-3.5 text-dynamic-blue" />
456
+ {labels.showDocuments ?? 'Show document-list tasks'}
457
+ </DropdownMenuCheckboxItem>
458
+ <DropdownMenuCheckboxItem
459
+ checked={includeExternal}
460
+ onCheckedChange={(checked) =>
461
+ setIncludeExternal(checked === true)
462
+ }
463
+ onSelect={(event) => event.preventDefault()}
464
+ >
465
+ <ExternalLink className="mr-2 h-3.5 w-3.5 text-dynamic-cyan" />
466
+ {labels.showExternalTasks ?? 'External tasks'}
467
+ </DropdownMenuCheckboxItem>
468
+ {filterCount > 0 && (
469
+ <>
470
+ <DropdownMenuSeparator />
471
+ <DropdownMenuItem
472
+ onClick={() => {
473
+ setIncludeDocuments(true);
474
+ setIncludeExternal(true);
475
+ }}
476
+ >
477
+ <RotateCcw className="mr-2 h-3.5 w-3.5" />
478
+ {labels.reset ?? 'Reset'}
479
+ </DropdownMenuItem>
480
+ </>
481
+ )}
482
+ </DropdownMenuContent>
483
+ </DropdownMenu>
484
+ <DropdownMenu>
485
+ <DropdownMenuTrigger asChild>
486
+ <Button
487
+ type="button"
488
+ variant="ghost"
489
+ size="xs"
490
+ className={cn(
491
+ 'h-7 w-7 p-0 hover:bg-muted/40',
492
+ config.titleClassName,
493
+ sortBy !== DEFAULT_DEADLINE_TASK_SORT_BY && 'bg-muted/40'
494
+ )}
495
+ title={labels.sort ?? 'Sort'}
496
+ aria-label={labels.sort ?? 'Sort'}
497
+ >
498
+ {sortBy === 'created-asc' ||
499
+ sortBy === 'due-asc' ||
500
+ sortBy === 'name-asc' ||
501
+ sortBy === 'source-asc' ? (
502
+ <ArrowUpAZ className="h-3.5 w-3.5" />
503
+ ) : (
504
+ <ArrowDownAZ className="h-3.5 w-3.5" />
505
+ )}
506
+ </Button>
507
+ </DropdownMenuTrigger>
508
+ <DropdownMenuContent align="end" className="w-56">
509
+ <DropdownMenuRadioGroup
510
+ value={sortBy}
511
+ onValueChange={(value) =>
512
+ setSortBy(value as DeadlineTaskSortBy)
513
+ }
514
+ >
515
+ <DropdownMenuRadioItem value="due-asc">
516
+ <CalendarClock className="mr-2 h-3.5 w-3.5" />
517
+ {labels.sortDueAsc ?? 'Soonest first'}
518
+ </DropdownMenuRadioItem>
519
+ <DropdownMenuRadioItem value="due-desc">
520
+ <CalendarClock className="mr-2 h-3.5 w-3.5" />
521
+ {labels.sortDueDesc ?? 'Latest first'}
522
+ </DropdownMenuRadioItem>
523
+ <DropdownMenuRadioItem value="created-desc">
524
+ <ArrowDownAZ className="mr-2 h-3.5 w-3.5" />
525
+ {labels.sortCreatedDesc ?? 'Newest first'}
526
+ </DropdownMenuRadioItem>
527
+ <DropdownMenuRadioItem value="created-asc">
528
+ <ArrowUpAZ className="mr-2 h-3.5 w-3.5" />
529
+ {labels.sortCreatedAsc ?? 'Oldest first'}
530
+ </DropdownMenuRadioItem>
531
+ <DropdownMenuRadioItem value="name-asc">
532
+ <ArrowUpAZ className="mr-2 h-3.5 w-3.5" />
533
+ {labels.sortNameAsc ?? 'Task name'}
534
+ </DropdownMenuRadioItem>
535
+ <DropdownMenuRadioItem value="source-asc">
536
+ <ArrowUpAZ className="mr-2 h-3.5 w-3.5" />
537
+ {labels.sortSourceAsc ?? 'Source board'}
538
+ </DropdownMenuRadioItem>
539
+ </DropdownMenuRadioGroup>
540
+ </DropdownMenuContent>
541
+ </DropdownMenu>
542
+ {onPinnedChange ? (
543
+ <Button
544
+ type="button"
545
+ variant="ghost"
546
+ size="xs"
547
+ className={cn(
548
+ 'h-7 w-7 p-0 hover:bg-muted/40',
549
+ config.titleClassName,
550
+ pinned && 'bg-muted/40'
551
+ )}
552
+ title={pinLabel}
553
+ aria-label={pinLabel}
554
+ onClick={() => onPinnedChange(config.section, !pinned)}
555
+ >
556
+ {pinned ? (
557
+ <PinOff className="h-3.5 w-3.5" />
558
+ ) : (
559
+ <Pin className="h-3.5 w-3.5" />
560
+ )}
561
+ </Button>
562
+ ) : null}
563
+ <Button
564
+ type="button"
565
+ variant="ghost"
566
+ size="xs"
567
+ className={cn(
568
+ 'h-7 w-7 p-0 hover:bg-muted/40',
569
+ config.titleClassName
570
+ )}
571
+ title={collapseLabel}
572
+ aria-label={collapseLabel}
573
+ onClick={() => onCollapsedChange?.(config.section, true)}
574
+ >
575
+ <ChevronLeft className="h-3.5 w-3.5" />
576
+ </Button>
116
577
  </div>
117
- <Badge className="h-5 px-1.5 text-[10px]" variant="outline">
118
- {tasks.length}
119
- </Badge>
120
578
  </div>
121
579
 
122
580
  <div className="scrollbar-thin min-h-0 flex-1 space-y-2 overflow-y-auto p-2">
123
- {tasks.map((task) => {
124
- const taskList = getTaskListForDeadlineTask(task, taskLists);
125
-
126
- return (
127
- <div
128
- key={task.id}
129
- className="shrink-0"
130
- data-testid={`kanban-deadline-task-card-${task.id}`}
131
- >
132
- <TaskCard
133
- availableLists={availableLists}
134
- boardId={boardId}
135
- bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
136
- dragDisabled
137
- isMultiSelectMode={isMultiSelectMode}
138
- isPersonalWorkspace={isPersonalWorkspace}
139
- isSelected={selectedTasks.has(task.id)}
140
- onClearSelection={onClearSelection}
141
- onSelect={onTaskSelect}
142
- onUpdate={onUpdate}
143
- optimisticUpdateInProgress={optimisticUpdateInProgress}
144
- selectedTasks={selectedTasks}
145
- sortableId={`deadline-${config.section}-${task.id}`}
146
- task={task}
147
- taskList={taskList}
148
- workspaceId={workspaceId}
149
- />
150
- </div>
151
- );
152
- })}
581
+ {loading && visibleTasks.length === 0 ? (
582
+ <DeadlineSectionSkeleton section={config.section} />
583
+ ) : (
584
+ visibleTasks.map((task) => {
585
+ const taskList = getTaskListForDeadlineTask(task, taskLists);
586
+
587
+ return (
588
+ <div
589
+ key={task.id}
590
+ className="shrink-0"
591
+ data-testid={`kanban-deadline-task-card-${task.id}`}
592
+ >
593
+ <TaskCard
594
+ availableLists={availableLists}
595
+ boardId={boardId}
596
+ bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
597
+ dragDisabled
598
+ isMultiSelectMode={isMultiSelectMode}
599
+ isPersonalWorkspace={isPersonalWorkspace}
600
+ canUseBoardAssignees={canUseBoardAssignees}
601
+ assigneeMemberSource={assigneeMemberSource}
602
+ isSelected={selectedTasks.has(task.id)}
603
+ onClearSelection={onClearSelection}
604
+ onSelect={onTaskSelect}
605
+ onUpdate={onUpdate}
606
+ optimisticUpdateInProgress={optimisticUpdateInProgress}
607
+ selectedTasks={selectedTasks}
608
+ deadlineContext={config.section}
609
+ deadlineNow={deadlineNow}
610
+ sortableId={`deadline-${config.section}-${task.id}`}
611
+ task={task}
612
+ taskList={taskList}
613
+ workspaceId={workspaceId}
614
+ />
615
+ </div>
616
+ );
617
+ })
618
+ )}
153
619
  </div>
154
- </div>
620
+ </Card>
155
621
  );
156
622
  }
157
623
 
@@ -161,61 +627,77 @@ export function KanbanDeadlinePanels({
161
627
  bulkUpdateCustomDueDate,
162
628
  isMultiSelectMode,
163
629
  isPersonalWorkspace,
630
+ canUseBoardAssignees,
631
+ assigneeMemberSource,
164
632
  labels,
165
633
  onClearSelection,
634
+ onSectionCollapsedChange,
635
+ onSectionPinnedChange,
166
636
  onTaskSelect,
167
637
  onUpdate,
168
638
  optimisticUpdateInProgress,
169
639
  sections,
640
+ loading,
641
+ collapsedSections,
642
+ pinnedSections,
643
+ stickyOffsets,
644
+ deadlineNow,
170
645
  selectedTasks,
171
646
  taskLists,
172
647
  workspaceId,
173
648
  }: KanbanDeadlinePanelsProps) {
174
- if (sections.overdue.length === 0 && sections.upcoming.length === 0) {
175
- return null;
176
- }
177
-
178
649
  const configs: DeadlineSectionConfig[] = [
179
650
  {
180
651
  icon: AlertTriangle,
181
652
  label: labels.overdue,
653
+ collapsedClassName: 'border-dynamic-red/35 bg-dynamic-red/5',
182
654
  panelClassName: 'border-dynamic-red/25 bg-dynamic-red/5',
183
655
  section: 'overdue',
184
- titleClassName: 'border-dynamic-red/25 text-dynamic-red',
656
+ titleClassName:
657
+ 'border-dynamic-red/25 text-dynamic-red hover:bg-dynamic-red/10 focus-visible:ring-dynamic-red/40',
185
658
  },
186
659
  {
187
660
  icon: CalendarClock,
188
661
  label: labels.upcoming,
662
+ collapsedClassName: 'border-dynamic-blue/35 bg-dynamic-blue/5',
189
663
  panelClassName: 'border-dynamic-blue/25 bg-dynamic-blue/5',
190
664
  section: 'upcoming',
191
- titleClassName: 'border-dynamic-blue/25 text-dynamic-blue',
665
+ titleClassName:
666
+ 'border-dynamic-blue/25 text-dynamic-blue hover:bg-dynamic-blue/10 focus-visible:ring-dynamic-blue/40',
192
667
  },
193
668
  ];
194
669
 
195
670
  return (
196
- <aside
197
- className="flex h-full w-[18rem] shrink-0 snap-start flex-col gap-3 md:w-80"
198
- data-testid="kanban-deadline-panels"
199
- >
671
+ <div className="contents" data-testid="kanban-deadline-panels">
200
672
  {configs.map((config) => (
201
673
  <DeadlineSection
202
674
  key={config.section}
203
675
  availableLists={availableLists}
204
676
  boardId={boardId}
205
677
  bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
678
+ collapsed={collapsedSections?.[config.section] === true}
206
679
  config={config}
680
+ deadlineNow={deadlineNow}
681
+ labels={labels}
682
+ loading={loading === true && sections[config.section].length === 0}
207
683
  isMultiSelectMode={isMultiSelectMode}
208
684
  isPersonalWorkspace={isPersonalWorkspace}
685
+ canUseBoardAssignees={canUseBoardAssignees}
686
+ assigneeMemberSource={assigneeMemberSource}
209
687
  onClearSelection={onClearSelection}
688
+ onCollapsedChange={onSectionCollapsedChange}
689
+ onPinnedChange={onSectionPinnedChange}
210
690
  onTaskSelect={onTaskSelect}
211
691
  onUpdate={onUpdate}
212
692
  optimisticUpdateInProgress={optimisticUpdateInProgress}
693
+ pinned={pinnedSections?.[config.section] === true}
213
694
  selectedTasks={selectedTasks}
695
+ stickyOffset={stickyOffsets?.[config.section]}
214
696
  taskLists={taskLists}
215
697
  tasks={sections[config.section]}
216
698
  workspaceId={workspaceId}
217
699
  />
218
700
  ))}
219
- </aside>
701
+ </div>
220
702
  );
221
703
  }