@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
@@ -9,9 +9,9 @@ import isBetween from 'dayjs/plugin/isBetween';
9
9
  import isSameOrAfter from 'dayjs/plugin/isSameOrAfter';
10
10
  import isSameOrBefore from 'dayjs/plugin/isSameOrBefore';
11
11
  import timezone from 'dayjs/plugin/timezone';
12
- import Image from 'next/image';
13
12
  import React, { useCallback, useMemo, useRef, useState } from 'react';
14
13
  import { MIN_COLUMN_WIDTH } from './config';
14
+ import { CalendarEventProviderIcon } from './event-provider-display';
15
15
  import { getLocationType, LocationTimeline } from './location-timeline';
16
16
  import { useCalendarSettings } from './settings/settings-context';
17
17
 
@@ -68,18 +68,10 @@ interface DragState {
68
68
  // 1. Extract EventContent component for shared rendering
69
69
  const EventContent = ({ event }: { event: CalendarEvent }) => (
70
70
  <>
71
- {typeof event.google_event_id === 'string' &&
72
- event.google_event_id.trim() !== '' && (
73
- <Image
74
- src="/media/google-calendar-icon.png"
75
- alt="Google Calendar"
76
- className="mr-1 inline-block h-[1.25em] w-[1.25em] align-middle opacity-80 dark:opacity-90"
77
- title="Synced from Google Calendar"
78
- data-testid="google-calendar-logo"
79
- width={18}
80
- height={18}
81
- />
82
- )}
71
+ <CalendarEventProviderIcon
72
+ event={event}
73
+ className="mr-1 h-[1.25em] w-[1.25em] opacity-80 dark:opacity-90"
74
+ />
83
75
  <span className="truncate">{event.title}</span>
84
76
  </>
85
77
  );
@@ -342,27 +334,6 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
342
334
  eventStart.isBefore(lastVisibleDate.add(1, 'day'), 'day') &&
343
335
  eventEnd.isAfter(firstVisibleDate, 'day');
344
336
 
345
- // Debug logging for multi-day events
346
- const eventDurationDays = eventEnd.diff(eventStart, 'day');
347
- if (eventDurationDays > 0) {
348
- console.log('Multi-day event processing:', {
349
- title: event.title,
350
- eventStart: eventStart.format('YYYY-MM-DD'),
351
- eventEnd: eventEnd.format('YYYY-MM-DD'),
352
- durationDays: eventDurationDays,
353
- isActuallyMultiDay: eventDurationDays > 1,
354
- firstVisibleDate: firstVisibleDate.format('YYYY-MM-DD'),
355
- lastVisibleDate: lastVisibleDate.format('YYYY-MM-DD'),
356
- eventOverlaps,
357
- wouldShowCutOffStart:
358
- eventDurationDays > 1 &&
359
- eventStart.isBefore(firstVisibleDate, 'day'),
360
- wouldShowCutOffEnd:
361
- eventDurationDays > 1 &&
362
- eventEnd.isAfter(lastVisibleDate.add(1, 'day'), 'day'),
363
- });
364
- }
365
-
366
337
  if (!eventOverlaps) {
367
338
  return; // Skip this event if it doesn't overlap with visible dates
368
339
  }
@@ -680,7 +651,13 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
680
651
  };
681
652
 
682
653
  // Calculate dynamic height based on visible events
683
- const barHeight = Math.max(1.9, eventLayout.maxVisibleEventsPerDay * 1.75);
654
+ const locationTopOffset =
655
+ locationSpans.length > 0 ? LOCATION_TIMELINE_HEIGHT_REM : 0;
656
+ const regularRowsHeight =
657
+ eventLayout.maxVisibleEventsPerDay > 0
658
+ ? eventLayout.maxVisibleEventsPerDay * 1.75
659
+ : 0;
660
+ const barHeight = Math.max(1.9, locationTopOffset + regularRowsHeight);
684
661
 
685
662
  // Enhanced mouse and touch handlers
686
663
  const handleEventMouseDown = (e: React.MouseEvent, eventSpan: EventSpan) => {
@@ -839,7 +816,7 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
839
816
  onClick={() => toggleDateExpansion(dateKey)}
840
817
  style={{
841
818
  position: 'absolute',
842
- top: `${MAX_EVENTS_DISPLAY * 1.7}rem`,
819
+ top: `${locationTopOffset + MAX_EVENTS_DISPLAY * 1.7}rem`,
843
820
  left: `${(dateIndex * 100) / visibleDates.length}%`,
844
821
  width: `${100 / visibleDates.length}%`,
845
822
  zIndex: 10,
@@ -858,7 +835,7 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
858
835
  onClick={() => toggleDateExpansion(dateKey)}
859
836
  style={{
860
837
  position: 'absolute',
861
- top: `${dateEvents.length * 1.7}rem`,
838
+ top: `${locationTopOffset + dateEvents.length * 1.7}rem`,
862
839
  left: `${(dateIndex * 100) / visibleDates.length}%`,
863
840
  width: `${100 / visibleDates.length}%`,
864
841
  zIndex: 10,
@@ -941,8 +918,18 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
941
918
  dragState.isDragging && dragState.draggedEvent?.id === event.id;
942
919
 
943
920
  // Calculate top offset based on location strip presence
944
- const topOffset =
945
- locationSpans.length > 0 ? LOCATION_TIMELINE_HEIGHT_REM : 0;
921
+ const topOffset = locationTopOffset;
922
+ const optimisticStatus = (
923
+ event as CalendarEvent & {
924
+ _optimisticStatus?:
925
+ | 'creating'
926
+ | 'updating'
927
+ | 'deleting'
928
+ | 'error';
929
+ }
930
+ )._optimisticStatus;
931
+ const isPendingMutation =
932
+ optimisticStatus === 'updating' || optimisticStatus === 'deleting';
946
933
 
947
934
  return (
948
935
  <div
@@ -959,6 +946,8 @@ export const AllDayEventBar = ({ dates }: { dates: Date[] }) => {
959
946
  bg,
960
947
  border,
961
948
  text,
949
+ isPendingMutation &&
950
+ 'opacity-60 outline outline-dashed outline-1 outline-primary/50',
962
951
  // Special styling for cut-off events
963
952
  (isCutOffStart || isCutOffEnd) && 'border-dashed'
964
953
  )}
@@ -880,6 +880,8 @@ export const CalendarCell = ({ date, hour }: CalendarCellProps) => {
880
880
  height: `${HOUR_HEIGHT}px`,
881
881
  }}
882
882
  onContextMenu={(e) => {
883
+ const target = e.target as HTMLElement | null;
884
+ if (target?.closest('[data-slot="context-menu-trigger"]')) return;
883
885
  e.preventDefault();
884
886
  }}
885
887
  onMouseEnter={() => setIsHovering(true)}
@@ -9,6 +9,7 @@ import { cn } from '@tuturuuu/utils/format';
9
9
  import { useCallback, useEffect, useRef, useState } from 'react';
10
10
  import { AgendaView } from './agenda-view';
11
11
  import { CalendarHeader } from './calendar-header';
12
+ import { CalendarLoadingSkeleton } from './calendar-loading-skeleton';
12
13
  import { CalendarViewWithTrail } from './calendar-view-with-trail';
13
14
  import { EventModal } from './event-modal';
14
15
  import { EventPreviewPopover } from './event-preview-popover';
@@ -102,6 +103,7 @@ export const CalendarContent = ({
102
103
  externalState,
103
104
  extras,
104
105
  overlay,
106
+ disableBuiltInEventUi,
105
107
  }: {
106
108
  t: any;
107
109
  locale: string;
@@ -118,10 +120,11 @@ export const CalendarContent = ({
118
120
  };
119
121
  extras?: React.ReactNode;
120
122
  overlay?: React.ReactNode;
123
+ disableBuiltInEventUi?: boolean;
121
124
  }) => {
122
125
  const { transition } = useViewTransition();
123
126
  const { settings } = useCalendarSettings();
124
- const { dates, setDates } = useCalendarSync();
127
+ const { dates, isLoading, setDates } = useCalendarSync();
125
128
 
126
129
  // Use ref to always have the latest settings without causing dependency cascades
127
130
  const settingsRef = useRef(settings);
@@ -191,7 +194,6 @@ export const CalendarContent = ({
191
194
  handleSetView('4-days');
192
195
  setDates(dates);
193
196
  });
194
- console.log('enable4DayView', dates);
195
197
  }, [date, transition, handleSetView, setDates]);
196
198
 
197
199
  const enableWeekView = useCallback(() => {
@@ -578,7 +580,7 @@ export const CalendarContent = ({
578
580
  return (
579
581
  <div
580
582
  className={cn(
581
- 'grid h-full w-full',
583
+ 'grid h-full min-h-0 w-full',
582
584
  view === 'month' || view === 'year'
583
585
  ? 'grid-rows-[auto_1fr]'
584
586
  : 'grid-rows-[auto_auto_1fr]'
@@ -621,7 +623,7 @@ export const CalendarContent = ({
621
623
 
622
624
  <div
623
625
  className={cn(
624
- 'scrollbar-none relative flex-1 overflow-auto rounded-lg focus:outline-none',
626
+ 'scrollbar-none relative min-h-0 flex-1 overflow-auto rounded-lg focus:outline-none',
625
627
  view === 'agenda' ||
626
628
  view === 'month' ||
627
629
  view === 'year' ||
@@ -632,7 +634,9 @@ export const CalendarContent = ({
632
634
  e.currentTarget.focus();
633
635
  }}
634
636
  >
635
- {view === 'month' && dates?.[0] ? (
637
+ {isLoading ? (
638
+ <CalendarLoadingSkeleton dates={dates} view={view} />
639
+ ) : view === 'month' && dates?.[0] ? (
636
640
  <MonthCalendar
637
641
  date={dates[0]}
638
642
  workspace={workspace}
@@ -665,7 +669,7 @@ export const CalendarContent = ({
665
669
  )}
666
670
  </div>
667
671
 
668
- {disabled
672
+ {disabled || disableBuiltInEventUi
669
673
  ? null
670
674
  : workspace && (
671
675
  <>
@@ -1,5 +1,7 @@
1
1
  import {
2
+ AlertTriangle,
2
3
  CalendarIcon,
4
+ Check,
3
5
  ChevronLeft,
4
6
  ChevronRight,
5
7
  Moon,
@@ -81,7 +83,7 @@ export function CalendarHeader({
81
83
  return newDate;
82
84
  });
83
85
 
84
- const { isLoading, isSyncing } = useCalendarSync();
86
+ const { syncStatus } = useCalendarSync();
85
87
  const selectToday = () => setDate(new Date());
86
88
  const isTodaySelected = () => dayjs(date).isSame(dayjs(), 'day');
87
89
  const isCurrentMonth = () =>
@@ -112,6 +114,14 @@ export function CalendarHeader({
112
114
  };
113
115
 
114
116
  const LunarIcon = showLunar ? MoonStar : Moon;
117
+ const statusLabel =
118
+ syncStatus.state === 'error'
119
+ ? t('failed_to_load_events')
120
+ : syncStatus.lastSyncTime
121
+ ? `${t('sync_completed')} ${dayjs(syncStatus.lastSyncTime)
122
+ .locale(locale)
123
+ .format('HH:mm')}`
124
+ : null;
115
125
 
116
126
  return (
117
127
  <div className="mb-2 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
@@ -121,8 +131,18 @@ export function CalendarHeader({
121
131
  </div>
122
132
  <div className="flex flex-col gap-2 md:flex-row md:items-center">
123
133
  <div className="flex items-center gap-2">
124
- {(isLoading || isSyncing) && (
125
- <div className="h-4 w-4 animate-spin rounded-full border-2 border-primary border-t-transparent" />
134
+ {statusLabel && (
135
+ <div
136
+ aria-live="polite"
137
+ className="hidden min-w-0 items-center gap-1.5 rounded-full border bg-background/80 px-2 py-1 text-muted-foreground text-xs shadow-xs sm:inline-flex"
138
+ >
139
+ {syncStatus.state === 'error' ? (
140
+ <AlertTriangle className="h-3.5 w-3.5 text-dynamic-red" />
141
+ ) : (
142
+ <Check className="h-3.5 w-3.5 text-dynamic-green" />
143
+ )}
144
+ <span className="truncate">{statusLabel}</span>
145
+ </div>
126
146
  )}
127
147
  <div className="flex flex-none items-center justify-center gap-2 md:justify-start">
128
148
  <Button
@@ -0,0 +1,135 @@
1
+ import { cn } from '@tuturuuu/utils/format';
2
+ import type { CalendarView } from '../../../../hooks/use-view-transition';
3
+ import { Skeleton } from '../../skeleton';
4
+ import { DAY_HEIGHT, HOUR_HEIGHT, MIN_COLUMN_WIDTH } from './config';
5
+
6
+ function TimedCalendarSkeleton({ columns }: { columns: number }) {
7
+ const eventPlaceholders = [
8
+ { column: 0, hour: 2.2, span: 1.4, width: 0.82 },
9
+ { column: Math.min(1, columns - 1), hour: 5.1, span: 1.1, width: 0.74 },
10
+ {
11
+ column: Math.max(0, columns - 2),
12
+ hour: 8.4,
13
+ span: 1.8,
14
+ width: 0.78,
15
+ },
16
+ ];
17
+
18
+ return (
19
+ <div
20
+ className="flex h-full overflow-hidden rounded-b-lg border-border border-b border-l bg-background/50 text-center dark:border-zinc-800"
21
+ style={{ minWidth: `${columns * MIN_COLUMN_WIDTH}px` }}
22
+ aria-hidden="true"
23
+ >
24
+ <div className="w-16 shrink-0 border-r bg-muted/20">
25
+ {Array.from({ length: 8 }).map((_, index) => (
26
+ <Skeleton key={index} className="mx-auto mt-7 h-3 w-9 rounded-sm" />
27
+ ))}
28
+ </div>
29
+ <div
30
+ className="relative grid flex-1"
31
+ style={{
32
+ gridTemplateColumns: `repeat(${columns}, minmax(${MIN_COLUMN_WIDTH}px, 1fr))`,
33
+ height: `${DAY_HEIGHT}px`,
34
+ }}
35
+ >
36
+ {Array.from({ length: columns }).map((_, index) => (
37
+ <div
38
+ key={index}
39
+ className="relative border-border/70 border-r last:border-r-0"
40
+ >
41
+ {Array.from({ length: 24 }).map((_, hour) => (
42
+ <div
43
+ key={hour}
44
+ className="border-border/50 border-b"
45
+ style={{ height: `${HOUR_HEIGHT}px` }}
46
+ />
47
+ ))}
48
+ </div>
49
+ ))}
50
+ {eventPlaceholders.map((placeholder, index) => (
51
+ <Skeleton
52
+ key={index}
53
+ className="absolute rounded-md"
54
+ style={{
55
+ left: `calc(${(placeholder.column * 100) / columns}% + 6px)`,
56
+ top: `${placeholder.hour * HOUR_HEIGHT}px`,
57
+ width: `calc(${(placeholder.width * 100) / columns}% - 12px)`,
58
+ height: `${placeholder.span * HOUR_HEIGHT}px`,
59
+ }}
60
+ />
61
+ ))}
62
+ </div>
63
+ </div>
64
+ );
65
+ }
66
+
67
+ function MonthCalendarSkeleton({ columns }: { columns: number }) {
68
+ return (
69
+ <div
70
+ className="grid h-full min-h-[28rem] gap-px overflow-hidden rounded-lg border bg-border"
71
+ style={{ gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))` }}
72
+ aria-hidden="true"
73
+ >
74
+ {Array.from({ length: columns * 5 }).map((_, index) => (
75
+ <div key={index} className="space-y-2 bg-background p-2">
76
+ <Skeleton className="h-3 w-8 rounded-sm" />
77
+ <Skeleton className="h-4 w-4/5 rounded-sm" />
78
+ <Skeleton className="h-4 w-3/5 rounded-sm" />
79
+ </div>
80
+ ))}
81
+ </div>
82
+ );
83
+ }
84
+
85
+ function AgendaCalendarSkeleton() {
86
+ return (
87
+ <div className="space-y-3 rounded-lg border bg-background p-4" aria-hidden>
88
+ {Array.from({ length: 6 }).map((_, index) => (
89
+ <div key={index} className="flex items-center gap-3">
90
+ <Skeleton className="h-10 w-14 rounded-md" />
91
+ <div className="min-w-0 flex-1 space-y-2">
92
+ <Skeleton className="h-4 w-2/5 rounded-sm" />
93
+ <Skeleton className="h-3 w-3/5 rounded-sm" />
94
+ </div>
95
+ </div>
96
+ ))}
97
+ </div>
98
+ );
99
+ }
100
+
101
+ export function CalendarLoadingSkeleton({
102
+ dates,
103
+ view,
104
+ }: {
105
+ dates: Date[];
106
+ view: CalendarView;
107
+ }) {
108
+ const columns = Math.max(1, dates.length || (view === 'day' ? 1 : 7));
109
+
110
+ if (view === 'month' || view === 'year') {
111
+ return <MonthCalendarSkeleton columns={view === 'year' ? 4 : 7} />;
112
+ }
113
+
114
+ if (view === 'agenda') return <AgendaCalendarSkeleton />;
115
+
116
+ return (
117
+ <div className="h-full min-h-0 space-y-2" aria-busy="true">
118
+ <div
119
+ className={cn(
120
+ 'grid rounded-lg border bg-background/70 p-2',
121
+ columns === 1 && 'max-w-lg'
122
+ )}
123
+ style={{
124
+ gridTemplateColumns: `4rem repeat(${columns}, minmax(${MIN_COLUMN_WIDTH}px, 1fr))`,
125
+ }}
126
+ >
127
+ <Skeleton className="h-5 w-5 self-center justify-self-center rounded-sm" />
128
+ {Array.from({ length: columns }).map((_, index) => (
129
+ <Skeleton key={index} className="mx-1 h-5 rounded-sm" />
130
+ ))}
131
+ </div>
132
+ <TimedCalendarSkeleton columns={columns} />
133
+ </div>
134
+ );
135
+ }