@tuturuuu/ui 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/biome.json +1 -1
  3. package/package.json +75 -73
  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/currency-input.test.tsx +43 -0
  29. package/src/components/ui/currency-input.tsx +1 -1
  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 +19 -0
  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/settings-dialog-shell.tsx +2 -1
  40. package/src/components/ui/custom/theme-toggle.tsx +1 -1
  41. package/src/components/ui/custom/workspace-access/workspace-access-default-role-card.tsx +60 -35
  42. package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
  43. package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
  44. package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
  45. package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
  46. package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
  47. package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
  48. package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
  49. package/src/components/ui/custom/workspace-select.tsx +8 -3
  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 +3 -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-card.tsx +21 -9
  69. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
  70. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
  71. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
  72. package/src/components/ui/finance/wallets/form.test.tsx +51 -3
  73. package/src/components/ui/finance/wallets/form.tsx +15 -4
  74. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  75. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
  76. package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
  77. package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
  78. package/src/components/ui/input-otp.tsx +1 -1
  79. package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
  80. package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
  81. package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
  82. package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
  83. package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
  84. package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
  85. package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
  86. package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
  87. package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
  88. package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
  89. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
  90. package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
  91. package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
  92. package/src/components/ui/money-input.test.tsx +64 -0
  93. package/src/components/ui/money-input.tsx +63 -0
  94. package/src/components/ui/navigation-menu.tsx +1 -1
  95. package/src/components/ui/pagination.tsx +1 -1
  96. package/src/components/ui/radio-group.tsx +1 -1
  97. package/src/components/ui/select.tsx +5 -1
  98. package/src/components/ui/sheet.tsx +1 -1
  99. package/src/components/ui/sidebar.tsx +1 -1
  100. package/src/components/ui/storefront/cart-popover.tsx +61 -0
  101. package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
  102. package/src/components/ui/storefront/cart-summary.tsx +104 -80
  103. package/src/components/ui/storefront/checkout-overlay.tsx +26 -0
  104. package/src/components/ui/storefront/hero-panel.tsx +2 -8
  105. package/src/components/ui/storefront/image-panel.tsx +6 -0
  106. package/src/components/ui/storefront/index.ts +11 -0
  107. package/src/components/ui/storefront/listing-card.tsx +84 -22
  108. package/src/components/ui/storefront/merch-sections.tsx +70 -0
  109. package/src/components/ui/storefront/product-detail.tsx +289 -0
  110. package/src/components/ui/storefront/product-dialog.tsx +72 -0
  111. package/src/components/ui/storefront/storefront-surface.test.tsx +221 -3
  112. package/src/components/ui/storefront/storefront-surface.tsx +288 -153
  113. package/src/components/ui/storefront/types.ts +27 -1
  114. package/src/components/ui/storefront/utils.ts +117 -27
  115. package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
  116. package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
  117. package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
  118. package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -0
  119. package/src/components/ui/text-editor/background-color-extension.ts +62 -0
  120. package/src/components/ui/text-editor/color-controls.tsx +284 -0
  121. package/src/components/ui/text-editor/content-migration.ts +41 -18
  122. package/src/components/ui/text-editor/editor.tsx +69 -14
  123. package/src/components/ui/text-editor/extensions.ts +9 -3
  124. package/src/components/ui/text-editor/highlight-extension.ts +22 -0
  125. package/src/components/ui/text-editor/image-extension.ts +40 -18
  126. package/src/components/ui/text-editor/tool-bar.tsx +9 -16
  127. package/src/components/ui/text-editor/video-extension.ts +11 -2
  128. package/src/components/ui/toast.tsx +1 -1
  129. package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
  130. package/src/components/ui/tu-do/boards/__tests__/workspace-projects-client-page.test.tsx +70 -1
  131. package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
  132. package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
  133. package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +127 -0
  134. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +113 -46
  135. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
  136. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
  137. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
  138. package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
  139. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
  140. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
  141. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +51 -9
  142. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
  143. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
  144. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +63 -0
  145. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +127 -38
  146. package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
  147. package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
  148. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
  149. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
  150. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
  151. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
  152. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
  153. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
  154. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
  155. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
  156. package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
  157. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +410 -4
  158. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +106 -14
  159. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
  160. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
  161. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
  162. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +186 -0
  163. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +59 -2
  164. package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
  165. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
  166. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
  167. package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
  168. package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
  169. package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
  170. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-display.ts +9 -0
  171. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
  172. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
  173. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
  174. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
  175. package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
  176. package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
  177. package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
  178. package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
  179. package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
  180. package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
  181. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
  182. package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
  183. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
  184. package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
  185. package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
  186. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +237 -3
  187. package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
  188. package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
  189. package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
  190. package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
  191. package/src/components/ui/tu-do/shared/board-header.tsx +465 -937
  192. package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
  193. package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
  194. package/src/components/ui/tu-do/shared/board-views.tsx +596 -82
  195. package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
  196. package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
  197. package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
  198. package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
  199. package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
  200. package/src/components/ui/tu-do/shared/task-dialog-presentation.test.ts +53 -0
  201. package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
  202. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
  203. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
  204. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -1
  205. package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
  206. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
  207. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
  208. package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
  209. package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
  210. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +44 -15
  211. package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
  212. package/src/declarations.d.ts +1 -0
  213. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
  214. package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
  215. package/src/hooks/__tests__/useBoardRealtime.test.tsx +2 -2
  216. package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -0
  217. package/src/hooks/use-calendar-sync.tsx +247 -243
  218. package/src/hooks/use-calendar.tsx +323 -138
  219. package/src/hooks/use-task-actions.ts +24 -0
  220. package/src/hooks/use-user-workspace-config.ts +75 -0
  221. package/src/hooks/use-workspace-currency.ts +8 -3
  222. package/src/hooks/useBoardRealtime.ts +6 -3
  223. package/src/hooks/useBoardRealtime.types.ts +11 -0
  224. package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
  225. package/src/hooks/useCursorTracking.ts +91 -27
  226. package/src/hooks/useTaskUserRealtime.ts +5 -3
@@ -85,12 +85,6 @@ export function TransactionCard({
85
85
  useFinanceConfidentialVisibility();
86
86
  const isTransfer = !!transaction.transfer;
87
87
 
88
- // Check if transaction is confidential
89
- const isConfidential =
90
- transaction.is_amount_confidential ||
91
- transaction.is_description_confidential ||
92
- transaction.is_category_confidential;
93
-
94
88
  // Get custom icon if available
95
89
  const CategoryIcon = useMemo(() => {
96
90
  if (transaction.category_icon) {
@@ -127,10 +121,17 @@ export function TransactionCard({
127
121
  return wallets.find((w) => w.id === transaction.transfer?.linked_wallet_id);
128
122
  }, [transaction.transfer?.linked_wallet_id, wallets]);
129
123
 
124
+ const linkedAmount = transaction.transfer?.linked_amount_redacted
125
+ ? undefined
126
+ : transaction.transfer?.linked_amount;
127
+ const linkedAmountIsConfidential = Boolean(
128
+ transaction.transfer?.linked_is_amount_confidential
129
+ );
130
130
  const transferDisplay = transaction.transfer
131
131
  ? transaction.transfer.is_origin
132
132
  ? {
133
133
  amount: transaction.amount,
134
+ amountIsConfidential: Boolean(transaction.is_amount_confidential),
134
135
  amountCurrency: transaction.wallet_currency,
135
136
  destinationIcon: linkedWallet?.icon,
136
137
  destinationImageSrc: linkedWallet?.image_src,
@@ -140,11 +141,15 @@ export function TransactionCard({
140
141
  originImageSrc: wallet?.image_src,
141
142
  originWalletId: transaction.wallet_id,
142
143
  originWalletName: transaction.wallet,
143
- secondaryAmount: transaction.transfer.linked_amount,
144
+ secondaryAmount: linkedAmount,
144
145
  secondaryCurrency: transaction.transfer.linked_wallet_currency,
145
146
  }
146
147
  : {
147
- amount: transaction.transfer.linked_amount ?? transaction.amount,
148
+ amount: linkedAmount ?? transaction.amount,
149
+ amountIsConfidential:
150
+ linkedAmount != null
151
+ ? linkedAmountIsConfidential
152
+ : Boolean(transaction.is_amount_confidential),
148
153
  amountCurrency:
149
154
  transaction.transfer.linked_wallet_currency ||
150
155
  transaction.wallet_currency,
@@ -161,9 +166,16 @@ export function TransactionCard({
161
166
  }
162
167
  : null;
163
168
  const displayAmount = transferDisplay?.amount ?? transaction.amount;
169
+ const displayAmountIsConfidential =
170
+ transferDisplay?.amountIsConfidential ??
171
+ Boolean(transaction.is_amount_confidential);
164
172
  const effectiveCurrency =
165
173
  transferDisplay?.amountCurrency || transaction.wallet_currency || currency;
166
174
  const isExpense = (displayAmount || 0) < 0;
175
+ const isConfidential =
176
+ displayAmountIsConfidential ||
177
+ transaction.is_description_confidential ||
178
+ transaction.is_category_confidential;
167
179
 
168
180
  // Currency conversion for foreign-currency transactions
169
181
  const isForeignCurrency =
@@ -434,7 +446,7 @@ export function TransactionCard({
434
446
  <div className="flex flex-col items-end">
435
447
  <ConfidentialAmount
436
448
  amount={displayAmount ?? null}
437
- isConfidential={transaction.is_amount_confidential || false}
449
+ isConfidential={displayAmountIsConfidential}
438
450
  currency={effectiveCurrency}
439
451
  className={cn(
440
452
  'font-bold text-sm tabular-nums transition-all duration-200 sm:text-xl',
@@ -481,10 +481,7 @@ export function TransactionEditDialog({
481
481
  return (
482
482
  <>
483
483
  <Dialog open={isOpen} onOpenChange={handleClose} modal={true}>
484
- <DialogContent
485
- showCloseButton={false}
486
- className="data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 inset-0! top-0! left-0! flex h-screen max-h-screen w-screen max-w-none! translate-x-0! translate-y-0! gap-0 rounded-none! border-0 p-0"
487
- >
484
+ <DialogContent showCloseButton={false} presentation="fullscreen">
488
485
  {/* Main content area */}
489
486
  <div className="flex min-w-0 flex-1 flex-col bg-background">
490
487
  {/* Header with gradient */}
@@ -15,6 +15,7 @@ interface TransactionsCreateSummaryProps {
15
15
  createDescription: string;
16
16
  createTitle: string;
17
17
  defaultOpen?: boolean;
18
+ defaultCurrency?: string;
18
19
  description: string;
19
20
  initialMode?: 'transaction' | 'transfer';
20
21
  permissionRequestUser?: FinancePermissionRequestUser | null;
@@ -32,6 +33,7 @@ export function TransactionsCreateSummary({
32
33
  createDescription,
33
34
  createTitle,
34
35
  defaultOpen = false,
36
+ defaultCurrency,
35
37
  description,
36
38
  initialMode = 'transaction',
37
39
  permissionRequestUser,
@@ -59,6 +61,7 @@ export function TransactionsCreateSummary({
59
61
  canCreateConfidentialTransactions
60
62
  }
61
63
  initialMode={initialMode}
64
+ defaultCurrency={defaultCurrency}
62
65
  timezone={timezone}
63
66
  permissionRequestUser={permissionRequestUser}
64
67
  />
@@ -7,6 +7,7 @@ import ExportDialogContent from '@tuturuuu/ui/finance/transactions/export-dialog
7
7
  import { TransactionsCreateSummary } from '@tuturuuu/ui/finance/transactions/transactions-create-summary';
8
8
  import { TransactionsInfinitePage } from '@tuturuuu/ui/finance/transactions/transactions-infinite-page';
9
9
  import { Separator } from '@tuturuuu/ui/separator';
10
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
10
11
  import {
11
12
  getPermissions,
12
13
  getWorkspace,
@@ -51,6 +52,7 @@ export default async function TransactionsPage({
51
52
  ]);
52
53
  if (!resolvedWorkspace || !resolvedPermissions) return notFound();
53
54
  const { containsPermission } = resolvedPermissions;
55
+ const workspaceCurrency = resolveSupportedCurrency(resolvedCurrency);
54
56
 
55
57
  const canViewTransactions = containsPermission('view_transactions');
56
58
  const canExportFinanceData = containsPermission('export_finance_data');
@@ -110,13 +112,14 @@ export default async function TransactionsPage({
110
112
  canChangeFinanceWallets={canChangeFinanceWallets}
111
113
  canSetFinanceWalletsOnCreate={canSetFinanceWalletsOnCreate}
112
114
  canCreateConfidentialTransactions={canCreateConfidentialTransactions}
115
+ defaultCurrency={workspaceCurrency}
113
116
  timezone={resolvedWorkspace.timezone}
114
117
  permissionRequestUser={permissionRequestUser}
115
118
  />
116
119
  <Separator className="my-4" />
117
120
  <TransactionsInfinitePage
118
121
  wsId={wsId}
119
- currency={resolvedCurrency ?? 'USD'}
122
+ currency={workspaceCurrency}
120
123
  timezone={resolvedWorkspace.timezone}
121
124
  canExport={canExportFinanceData}
122
125
  exportContent={
@@ -5,10 +5,27 @@ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import type { WalletFormValues } from './form';
6
6
  import { WalletForm } from './form';
7
7
 
8
+ const mocks = vi.hoisted(() => ({
9
+ createWallet: vi.fn(),
10
+ updateWallet: vi.fn(),
11
+ }));
12
+
8
13
  vi.mock('@tuturuuu/ui/hooks/use-workspace-currency', () => ({
9
- useWorkspaceCurrency: () => ({ currency: 'USD' }),
14
+ useWorkspaceCurrency: (_wsId: string, fallbackCurrency = 'USD') => ({
15
+ currency: fallbackCurrency,
16
+ }),
10
17
  }));
11
18
 
19
+ vi.mock('@tuturuuu/internal-api/finance', async (importOriginal) => {
20
+ const actual =
21
+ await importOriginal<typeof import('@tuturuuu/internal-api/finance')>();
22
+ return {
23
+ ...actual,
24
+ createWallet: mocks.createWallet,
25
+ updateWallet: mocks.updateWallet,
26
+ };
27
+ });
28
+
12
29
  vi.mock('next/navigation', () => ({
13
30
  useRouter: () => ({ refresh: vi.fn() }),
14
31
  }));
@@ -29,6 +46,7 @@ function renderWalletForm(
29
46
  options: {
30
47
  data?: ComponentProps<typeof WalletForm>['data'];
31
48
  defaultType?: WalletFormValues['type'];
49
+ defaultCurrency?: string;
32
50
  } = {}
33
51
  ) {
34
52
  const data =
@@ -41,13 +59,18 @@ function renderWalletForm(
41
59
  name: 'Primary',
42
60
  type: 'STANDARD',
43
61
  } as never);
44
- const { defaultType } = options;
62
+ const { defaultCurrency, defaultType } = options;
45
63
 
46
64
  const queryClient = new QueryClient();
47
65
 
48
66
  return render(
49
67
  <QueryClientProvider client={queryClient}>
50
- <WalletForm wsId="ws-1" data={data} defaultType={defaultType} />
68
+ <WalletForm
69
+ wsId="ws-1"
70
+ data={data}
71
+ defaultType={defaultType}
72
+ defaultCurrency={defaultCurrency}
73
+ />
51
74
  </QueryClientProvider>
52
75
  );
53
76
  }
@@ -69,6 +92,8 @@ function typeIntoCurrencyInput(input: HTMLInputElement, value: string) {
69
92
  describe('WalletForm', () => {
70
93
  beforeEach(() => {
71
94
  vi.clearAllMocks();
95
+ mocks.createWallet.mockResolvedValue({ message: 'success' });
96
+ mocks.updateWallet.mockResolvedValue({ message: 'success' });
72
97
  // biome-ignore lint/suspicious/noDocumentCookie: test resets the finance visibility cookie.
73
98
  document.cookie =
74
99
  'finance-confidential-mode=;expires=Thu, 01 Jan 1970 00:00:00 GMT;path=/';
@@ -118,6 +143,29 @@ describe('WalletForm', () => {
118
143
  );
119
144
  });
120
145
 
146
+ it('initializes new wallet currency from the supplied workspace default', async () => {
147
+ renderWalletForm({
148
+ data: undefined,
149
+ defaultCurrency: 'SGD',
150
+ });
151
+
152
+ fireEvent.change(screen.getByLabelText('wallet-data-table.wallet_name'), {
153
+ target: { value: 'Singapore Cash' },
154
+ });
155
+ fireEvent.click(screen.getByRole('button', { name: 'ws-wallets.create' }));
156
+
157
+ await waitFor(() =>
158
+ expect(mocks.createWallet).toHaveBeenCalledWith(
159
+ 'ws-1',
160
+ expect.objectContaining({
161
+ currency: 'SGD',
162
+ name: 'Singapore Cash',
163
+ type: 'STANDARD',
164
+ })
165
+ )
166
+ );
167
+ });
168
+
121
169
  it('keeps accepting large credit limits after locale grouping is inserted', () => {
122
170
  renderWalletForm({
123
171
  data: {
@@ -20,6 +20,7 @@ import { Input } from '@tuturuuu/ui/input';
20
20
  import { zodResolver } from '@tuturuuu/ui/resolvers';
21
21
  import {
22
22
  getCurrencyLocale,
23
+ resolveSupportedCurrency,
23
24
  SUPPORTED_CURRENCIES,
24
25
  } from '@tuturuuu/utils/currencies';
25
26
  import { useRouter } from 'next/navigation';
@@ -97,6 +98,7 @@ interface Props {
97
98
  wsId: string;
98
99
  data?: Wallet;
99
100
  defaultType?: WalletFormValues['type'];
101
+ defaultCurrency?: string;
100
102
  onFinish?: (data: WalletFormValues) => void;
101
103
  isPersonalWorkspace?: boolean;
102
104
  }
@@ -105,11 +107,20 @@ export function WalletForm({
105
107
  wsId,
106
108
  data,
107
109
  defaultType = 'STANDARD',
110
+ defaultCurrency,
108
111
  onFinish,
109
112
  isPersonalWorkspace,
110
113
  }: Props) {
111
114
  const t = useTranslations();
112
- const { currency: workspaceCurrency } = useWorkspaceCurrency(wsId);
115
+ const fallbackCurrency = resolveSupportedCurrency(defaultCurrency);
116
+ const { currency: workspaceCurrency } = useWorkspaceCurrency(
117
+ wsId,
118
+ fallbackCurrency
119
+ );
120
+ const resolvedWorkspaceCurrency = resolveSupportedCurrency(
121
+ workspaceCurrency,
122
+ fallbackCurrency
123
+ );
113
124
  const { isConfidential: areNumbersHidden } =
114
125
  useFinanceConfidentialVisibility();
115
126
  const queryClient = useQueryClient();
@@ -127,7 +138,7 @@ export function WalletForm({
127
138
  description: data?.description || '',
128
139
  balance: data?.balance || 0,
129
140
  type: data?.type || defaultType,
130
- currency: data?.currency || workspaceCurrency || 'USD',
141
+ currency: data?.currency || resolvedWorkspaceCurrency,
131
142
  icon: data?.icon || null,
132
143
  image_src: data?.image_src || null,
133
144
  limit: data?.limit ?? undefined,
@@ -193,7 +204,7 @@ export function WalletForm({
193
204
  ? ''
194
205
  : new Intl.NumberFormat(
195
206
  getCurrencyLocale(
196
- walletCurrency || workspaceCurrency || 'USD'
207
+ walletCurrency || resolvedWorkspaceCurrency
197
208
  ),
198
209
  {
199
210
  maximumFractionDigits: 2,
@@ -296,7 +307,7 @@ export function WalletForm({
296
307
  <WalletCreditFields
297
308
  form={form}
298
309
  loading={loading}
299
- currency={walletCurrency || workspaceCurrency || 'USD'}
310
+ currency={walletCurrency || resolvedWorkspaceCurrency}
300
311
  />
301
312
  )}
302
313
 
@@ -35,6 +35,7 @@ interface WalletDetailsActionsProps {
35
35
  canSetFinanceWalletsOnCreate?: boolean;
36
36
  canDeleteWallets: boolean;
37
37
  isPersonalWorkspace: boolean;
38
+ defaultCurrency?: string;
38
39
  timezone?: string | null;
39
40
  permissionRequestUser?: FinancePermissionRequestUser | null;
40
41
  }
@@ -51,6 +52,7 @@ export function WalletDetailsActions({
51
52
  canSetFinanceWalletsOnCreate,
52
53
  canDeleteWallets,
53
54
  isPersonalWorkspace,
55
+ defaultCurrency,
54
56
  timezone,
55
57
  permissionRequestUser,
56
58
  }: WalletDetailsActionsProps) {
@@ -110,6 +112,7 @@ export function WalletDetailsActions({
110
112
  <WalletForm
111
113
  wsId={wsId}
112
114
  data={wallet}
115
+ defaultCurrency={defaultCurrency}
113
116
  isPersonalWorkspace={isPersonalWorkspace}
114
117
  />
115
118
  }
@@ -210,6 +213,7 @@ export function WalletDetailsActions({
210
213
  canCreateConfidentialTransactions={
211
214
  canCreateConfidentialTransactions
212
215
  }
216
+ defaultCurrency={defaultCurrency}
213
217
  timezone={timezone}
214
218
  preferInitialWalletSelection={transactionAction !== 'payment'}
215
219
  refreshPageOnFinish
@@ -11,6 +11,7 @@ import type { FinancePermissionRequestUser } from '@tuturuuu/ui/finance/shared/f
11
11
  import { InfiniteTransactionsList } from '@tuturuuu/ui/finance/transactions/infinite-transactions-list';
12
12
  import { Separator } from '@tuturuuu/ui/separator';
13
13
  import { Skeleton } from '@tuturuuu/ui/skeleton';
14
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
14
15
  import type { ExchangeRate } from '@tuturuuu/utils/exchange-rates';
15
16
  import { getCurrencyLocale } from '@tuturuuu/utils/format';
16
17
  import {
@@ -117,8 +118,8 @@ export default async function WalletDetailsPage({
117
118
  notFound();
118
119
  }
119
120
 
120
- const currency = wallet.currency || resolvedDefaultCurrency || 'USD';
121
- const workspaceCurrency = resolvedDefaultCurrency || 'USD';
121
+ const workspaceCurrency = resolveSupportedCurrency(resolvedDefaultCurrency);
122
+ const currency = wallet.currency || workspaceCurrency;
122
123
  const initialAction = getInitialWalletAction(searchParams.action);
123
124
 
124
125
  // Fetch exchange rates for conversion display
@@ -153,6 +154,7 @@ export default async function WalletDetailsPage({
153
154
  canSetFinanceWalletsOnCreate={canSetFinanceWalletsOnCreate}
154
155
  canDeleteWallets={canDeleteWallets}
155
156
  isPersonalWorkspace={!!resolvedWorkspace.personal}
157
+ defaultCurrency={workspaceCurrency}
156
158
  timezone={resolvedWorkspace.timezone}
157
159
  permissionRequestUser={permissionRequestUser}
158
160
  />
@@ -228,6 +228,7 @@ export function WalletsDataTable({
228
228
  <WalletForm
229
229
  wsId={wsId}
230
230
  data={selectedWallet}
231
+ defaultCurrency={currency}
231
232
  onFinish={handleEditComplete}
232
233
  isPersonalWorkspace={isPersonalWorkspace}
233
234
  />
@@ -6,6 +6,7 @@ import { WalletTotalCheckDialog } from '@tuturuuu/ui/finance/wallets/checkpoints
6
6
  import { WalletForm } from '@tuturuuu/ui/finance/wallets/form';
7
7
  import { WalletsDataTable } from '@tuturuuu/ui/finance/wallets/wallets-data-table';
8
8
  import { Separator } from '@tuturuuu/ui/separator';
9
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
9
10
  import {
10
11
  getPermissions,
11
12
  getWorkspace,
@@ -51,6 +52,7 @@ export default async function WalletsPage({
51
52
  ]);
52
53
  if (!resolvedPermissions || !resolvedWorkspace) notFound();
53
54
  const { containsPermission } = resolvedPermissions;
55
+ const workspaceCurrency = resolveSupportedCurrency(resolvedCurrency);
54
56
 
55
57
  const canCreateWallets = containsPermission('create_wallets');
56
58
  const canUpdateWallets = containsPermission('update_wallets');
@@ -76,6 +78,7 @@ export default async function WalletsPage({
76
78
  <WalletForm
77
79
  wsId={wsId}
78
80
  defaultType={isCreditCardCreate ? 'CREDIT' : 'STANDARD'}
81
+ defaultCurrency={workspaceCurrency}
79
82
  />
80
83
  ) : undefined
81
84
  }
@@ -95,7 +98,7 @@ export default async function WalletsPage({
95
98
  />
96
99
  <WalletTotalCheckDialog
97
100
  wsId={wsId}
98
- currency={resolvedCurrency ?? 'USD'}
101
+ currency={workspaceCurrency}
99
102
  canUpdateWallets={canUpdateWallets}
100
103
  defaultOpen={searchParams.tool === 'all-wallet-check'}
101
104
  />
@@ -105,7 +108,7 @@ export default async function WalletsPage({
105
108
  wsId={wsId}
106
109
  canUpdateWallets={canUpdateWallets}
107
110
  canDeleteWallets={canDeleteWallets}
108
- currency={resolvedCurrency ?? 'USD'}
111
+ currency={workspaceCurrency}
109
112
  financePrefix={financePrefix}
110
113
  isPersonalWorkspace={!!resolvedWorkspace?.personal}
111
114
  query={searchParams.q}
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { MinusIcon } from '@tuturuuu/icons';
3
+ import { MinusIcon } from '@tuturuuu/icons/lucide-static';
4
4
  import { cn } from '@tuturuuu/utils/format';
5
5
  import { OTPInput, OTPInputContext, type SlotProps } from 'input-otp';
6
6
  import * as React from 'react';
@@ -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
  <>