@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
@@ -23,9 +23,9 @@ import {
23
23
  toWorkspaceSlug,
24
24
  } from '@tuturuuu/utils/constants';
25
25
  import { cn } from '@tuturuuu/utils/format';
26
- import { getInitials } from '@tuturuuu/utils/name-helper';
27
26
  import { workspaceHandleSchema } from '@tuturuuu/utils/workspace-handle';
28
27
  import { WORKSPACE_LIMIT_ERROR_CODE } from '@tuturuuu/utils/workspace-limits';
28
+ import Image from 'next/image';
29
29
  import { usePathname, useRouter } from 'next/navigation';
30
30
  import { useTranslations } from 'next-intl';
31
31
  import type { ReactNode } from 'react';
@@ -98,6 +98,7 @@ function WorkspaceIcon({
98
98
  avatarUrl,
99
99
  fallbackLogoUrl
100
100
  );
101
+ const shouldSkipFallbackOptimization = /^https?:\/\//u.test(fallbackLogoUrl);
101
102
 
102
103
  return (
103
104
  <Avatar
@@ -124,11 +125,15 @@ function WorkspaceIcon({
124
125
  resolvedAvatarUrl ? 'rounded-xs' : 'rounded-sm'
125
126
  )}
126
127
  >
127
- <AvatarImage
128
+ <Image
129
+ alt=""
130
+ aria-hidden="true"
128
131
  className="h-full w-full object-cover"
132
+ height={20}
129
133
  src={fallbackLogoUrl}
134
+ unoptimized={shouldSkipFallbackOptimization}
135
+ width={20}
130
136
  />
131
- {name ? getInitials(name) : '?'}
132
137
  </AvatarFallback>
133
138
  </Avatar>
134
139
  );
@@ -0,0 +1,52 @@
1
+ import '@testing-library/jest-dom';
2
+ import { render, screen } from '@testing-library/react';
3
+ import type { ReactNode } from 'react';
4
+ import { describe, expect, it } from 'vitest';
5
+ import { Dialog, DialogContent, DialogTitle } from './dialog';
6
+
7
+ function renderOpenDialog(children: ReactNode) {
8
+ return render(<Dialog open>{children}</Dialog>);
9
+ }
10
+
11
+ describe('DialogContent presentation', () => {
12
+ it('keeps the default centered dialog animation classes', () => {
13
+ renderOpenDialog(
14
+ <DialogContent>
15
+ <DialogTitle>Default dialog</DialogTitle>
16
+ </DialogContent>
17
+ );
18
+
19
+ const dialog = screen.getByRole('dialog', { name: 'Default dialog' });
20
+ const className = dialog.getAttribute('class') ?? '';
21
+
22
+ expect(className).toContain('top-[50%]');
23
+ expect(className).toContain('left-[50%]');
24
+ expect(className).toContain('data-[state=open]:animate-in');
25
+ expect(className).toContain('data-[state=open]:fade-in-0');
26
+ expect(className).toContain('data-[state=open]:zoom-in-95');
27
+ expect(dialog).toHaveClass('rounded-lg');
28
+ });
29
+
30
+ it('renders fullscreen content as an opaque non-animated viewport surface', () => {
31
+ renderOpenDialog(
32
+ <DialogContent presentation="fullscreen">
33
+ <DialogTitle>Fullscreen dialog</DialogTitle>
34
+ </DialogContent>
35
+ );
36
+
37
+ const dialog = screen.getByRole('dialog', { name: 'Fullscreen dialog' });
38
+ const className = dialog.getAttribute('class') ?? '';
39
+
40
+ expect(dialog).toHaveClass('fixed');
41
+ expect(dialog).toHaveClass('inset-0');
42
+ expect(dialog).toHaveClass('h-dvh');
43
+ expect(dialog).toHaveClass('max-h-dvh');
44
+ expect(dialog).toHaveClass('w-screen');
45
+ expect(dialog).toHaveClass('max-w-none');
46
+ expect(dialog).toHaveClass('bg-background');
47
+ expect(dialog).toHaveClass('rounded-none');
48
+ expect(dialog).toHaveClass('border-0');
49
+ expect(dialog).toHaveClass('shadow-none');
50
+ expect(className).not.toMatch(/animate-in|fade-in|zoom-in|slide-in/);
51
+ });
52
+ });
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import * as DialogPrimitive from '@radix-ui/react-dialog';
4
- import { XIcon } from '@tuturuuu/icons';
4
+ import { XIcon } from '@tuturuuu/icons/lucide-static';
5
5
  import { cn } from '@tuturuuu/utils/format';
6
6
  import type * as React from 'react';
7
7
 
@@ -51,9 +51,11 @@ function DialogContent({
51
51
  children,
52
52
  role = 'dialog',
53
53
  showCloseButton = true,
54
+ presentation = 'default',
54
55
  ...props
55
56
  }: React.ComponentProps<typeof DialogPrimitive.Content> & {
56
57
  showCloseButton?: boolean;
58
+ presentation?: 'default' | 'fullscreen';
57
59
  }) {
58
60
  return (
59
61
  <DialogPortal data-slot="dialog-portal">
@@ -62,7 +64,9 @@ function DialogContent({
62
64
  data-slot="dialog-content"
63
65
  role={role}
64
66
  className={cn(
65
- 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg',
67
+ presentation === 'fullscreen'
68
+ ? 'fixed inset-0 z-50 flex h-dvh max-h-dvh w-screen max-w-none gap-0 overflow-hidden rounded-none border-0 bg-background p-0 shadow-none'
69
+ : 'data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 data-[state=closed]:animate-out data-[state=open]:animate-in sm:max-w-lg',
66
70
  className
67
71
  )}
68
72
  onClick={(e) => e.stopPropagation()}
@@ -1,7 +1,11 @@
1
1
  'use client';
2
2
 
3
3
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
4
- import { CheckIcon, ChevronRightIcon, CircleIcon } from '@tuturuuu/icons';
4
+ import {
5
+ CheckIcon,
6
+ ChevronRightIcon,
7
+ CircleIcon,
8
+ } from '@tuturuuu/icons/lucide-static';
5
9
  import { cn } from '@tuturuuu/utils/format';
6
10
  import type * as React from 'react';
7
11
 
@@ -9,6 +9,10 @@ import type {
9
9
  InterestCalculationType,
10
10
  } from '@tuturuuu/types/primitives/DebtLoan';
11
11
  import type { Wallet } from '@tuturuuu/types/primitives/Wallet';
12
+ import {
13
+ resolveSupportedCurrency,
14
+ SUPPORTED_CURRENCIES,
15
+ } from '@tuturuuu/utils/currencies';
12
16
  import { useTranslations } from 'next-intl';
13
17
  import { useCallback, useState } from 'react';
14
18
  import { useForm } from 'react-hook-form';
@@ -36,6 +40,7 @@ interface Props {
36
40
  data?: DebtLoan;
37
41
  wallets?: Wallet[];
38
42
  defaultType?: DebtLoanType;
43
+ defaultCurrency?: string;
39
44
  onFinish?: (data: DebtLoanFormData) => void;
40
45
  onCancel?: () => void;
41
46
  }
@@ -45,12 +50,14 @@ export function DebtLoanForm({
45
50
  data,
46
51
  wallets = [],
47
52
  defaultType = 'debt',
53
+ defaultCurrency,
48
54
  onFinish,
49
55
  onCancel,
50
56
  }: Props) {
51
57
  const t = useTranslations('ws-debt-loan');
52
58
  const [loading, setLoading] = useState(false);
53
59
  const formSchema = createDebtLoanFormSchema(t);
60
+ const workspaceCurrency = resolveSupportedCurrency(defaultCurrency);
54
61
 
55
62
  const form = useForm<DebtLoanFormValues>({
56
63
  resolver: zodResolver(formSchema),
@@ -60,7 +67,7 @@ export function DebtLoanForm({
60
67
  counterparty: data?.counterparty || '',
61
68
  type: (data?.type || defaultType) as DebtLoanType,
62
69
  principal_amount: data?.principal_amount || 0,
63
- currency: data?.currency || 'VND',
70
+ currency: data?.currency || workspaceCurrency,
64
71
  interest_rate: data?.interest_rate ?? undefined,
65
72
  interest_type: (data?.interest_type || undefined) as
66
73
  | InterestCalculationType
@@ -226,10 +233,10 @@ export function DebtLoanForm({
226
233
  <SelectField
227
234
  id="currency"
228
235
  placeholder={t('select_currency')}
229
- options={[
230
- { value: 'VND', label: 'VND' },
231
- { value: 'USD', label: 'USD' },
232
- ]}
236
+ options={SUPPORTED_CURRENCIES.map((currency) => ({
237
+ value: currency.code,
238
+ label: `${currency.code} - ${currency.name}`,
239
+ }))}
233
240
  classNames={{ selectTrigger: 'w-full' }}
234
241
  value={field.value}
235
242
  onValueChange={field.onChange}
@@ -8,6 +8,7 @@ import {
8
8
  TrendingUp,
9
9
  } from '@tuturuuu/icons';
10
10
  import type { DebtLoanSummary } from '@tuturuuu/types/primitives/DebtLoan';
11
+ import { getCurrencyLocale } from '@tuturuuu/utils/currencies';
11
12
  import { cn } from '@tuturuuu/utils/format';
12
13
  import { useTranslations } from 'next-intl';
13
14
  import { useFinanceHref } from '../finance-route-context';
@@ -22,8 +23,8 @@ interface Props {
22
23
 
23
24
  export function DebtLoanSummaryCards({
24
25
  summary,
25
- currency = 'VND',
26
- locale = 'vi-VN',
26
+ currency = 'USD',
27
+ locale = getCurrencyLocale(currency),
27
28
  wsId,
28
29
  }: Props) {
29
30
  const t = useTranslations('ws-debt-loan');
@@ -5,6 +5,8 @@ import { DebtsPage } from './debts-page';
5
5
 
6
6
  const mocks = vi.hoisted(() => ({
7
7
  debtLoanForm: vi.fn(),
8
+ debtLoanList: vi.fn(),
9
+ debtLoanSummaryCards: vi.fn(),
8
10
  getDebtLoanSummary: vi.fn(),
9
11
  listDebtLoans: vi.fn(),
10
12
  listWallets: vi.fn(),
@@ -31,15 +33,22 @@ vi.mock('./debt-loan-form', () => ({
31
33
  }));
32
34
 
33
35
  vi.mock('./debt-loan-list', () => ({
34
- DebtLoanList: () => null,
36
+ DebtLoanList: (props: unknown) => {
37
+ mocks.debtLoanList(props);
38
+ return null;
39
+ },
35
40
  }));
36
41
 
37
42
  vi.mock('./debt-loan-summary', () => ({
38
- DebtLoanSummaryCards: () => null,
43
+ DebtLoanSummaryCards: (props: unknown) => {
44
+ mocks.debtLoanSummaryCards(props);
45
+ return null;
46
+ },
39
47
  }));
40
48
 
41
49
  function renderDebtsPage(
42
- searchParams: { create?: string; type?: string } = {}
50
+ searchParams: { create?: string; type?: string } = {},
51
+ props: { currency?: string } = {}
43
52
  ) {
44
53
  const queryClient = new QueryClient({
45
54
  defaultOptions: {
@@ -51,7 +60,11 @@ function renderDebtsPage(
51
60
 
52
61
  return render(
53
62
  <QueryClientProvider client={queryClient}>
54
- <DebtsPage wsId="ws-1" searchParams={searchParams} />
63
+ <DebtsPage
64
+ wsId="ws-1"
65
+ searchParams={searchParams}
66
+ currency={props.currency}
67
+ />
55
68
  </QueryClientProvider>
56
69
  );
57
70
  }
@@ -83,15 +96,51 @@ describe('debts page', () => {
83
96
  });
84
97
 
85
98
  it('opens the loan create dialog from query state', async () => {
86
- renderDebtsPage({ create: 'loan' });
99
+ renderDebtsPage({ create: 'loan' }, { currency: 'SGD' });
87
100
 
88
101
  await waitFor(() => {
89
102
  expect(mocks.debtLoanForm).toHaveBeenCalledWith(
90
103
  expect.objectContaining({
104
+ defaultCurrency: 'SGD',
91
105
  defaultType: 'loan',
92
106
  wsId: 'ws-1',
93
107
  })
94
108
  );
95
109
  });
96
110
  });
111
+
112
+ it('passes workspace currency to summary without overriding list row currencies', async () => {
113
+ mocks.listDebtLoans.mockResolvedValueOnce([
114
+ {
115
+ currency: 'USD',
116
+ id: 'debt-1',
117
+ name: 'USD Debt',
118
+ status: 'active',
119
+ type: 'debt',
120
+ },
121
+ ]);
122
+
123
+ renderDebtsPage({}, { currency: 'SGD' });
124
+
125
+ await waitFor(() => {
126
+ expect(mocks.debtLoanSummaryCards).toHaveBeenCalledWith(
127
+ expect.objectContaining({
128
+ currency: 'SGD',
129
+ locale: 'en-SG',
130
+ })
131
+ );
132
+ expect(mocks.debtLoanList).toHaveBeenCalledWith(
133
+ expect.not.objectContaining({
134
+ currency: expect.anything(),
135
+ })
136
+ );
137
+ expect(mocks.debtLoanList).toHaveBeenCalledWith(
138
+ expect.objectContaining({
139
+ debtLoans: expect.arrayContaining([
140
+ expect.objectContaining({ currency: 'USD' }),
141
+ ]),
142
+ })
143
+ );
144
+ });
145
+ });
97
146
  });
@@ -13,6 +13,10 @@ import type {
13
13
  DebtLoanWithBalance,
14
14
  } from '@tuturuuu/types/primitives/DebtLoan';
15
15
  import type { Wallet } from '@tuturuuu/types/primitives/Wallet';
16
+ import {
17
+ getCurrencyLocale,
18
+ resolveSupportedCurrency,
19
+ } from '@tuturuuu/utils/currencies';
16
20
  import { useTranslations } from 'next-intl';
17
21
  import { useEffect, useState } from 'react';
18
22
  import { Button } from '../../button';
@@ -37,11 +41,14 @@ interface Props {
37
41
  create?: string;
38
42
  type?: string;
39
43
  };
44
+ currency?: string;
40
45
  }
41
46
 
42
- export function DebtsPage({ wsId, searchParams }: Props) {
47
+ export function DebtsPage({ wsId, searchParams, currency }: Props) {
43
48
  const t = useTranslations('ws-debt-loan');
44
49
  const queryClient = useQueryClient();
50
+ const workspaceCurrency = resolveSupportedCurrency(currency);
51
+ const workspaceCurrencyLocale = getCurrencyLocale(workspaceCurrency);
45
52
  const requestedCreateType =
46
53
  searchParams?.create === 'loan'
47
54
  ? 'loan'
@@ -135,7 +142,12 @@ export function DebtsPage({ wsId, searchParams }: Props) {
135
142
  ))}
136
143
  </div>
137
144
  ) : summary ? (
138
- <DebtLoanSummaryCards summary={summary} wsId={wsId} />
145
+ <DebtLoanSummaryCards
146
+ summary={summary}
147
+ wsId={wsId}
148
+ currency={workspaceCurrency}
149
+ locale={workspaceCurrencyLocale}
150
+ />
139
151
  ) : null}
140
152
 
141
153
  <Separator />
@@ -220,6 +232,7 @@ export function DebtsPage({ wsId, searchParams }: Props) {
220
232
  wsId={wsId}
221
233
  wallets={wallets}
222
234
  defaultType={defaultCreateType}
235
+ defaultCurrency={workspaceCurrency}
223
236
  onFinish={handleFormFinish}
224
237
  onCancel={() => setIsCreateDialogOpen(false)}
225
238
  />
@@ -6,7 +6,6 @@ import {
6
6
  ChevronDown,
7
7
  Loader2,
8
8
  } from '@tuturuuu/icons';
9
- import type { Database } from '@tuturuuu/types';
10
9
  import {
11
10
  Card,
12
11
  CardContent,
@@ -29,6 +28,7 @@ import {
29
28
  getSubscriptionCoverageInvoiceForGroup,
30
29
  isSubscriptionMonthPaidForGroup,
31
30
  parseLocalCalendarDate,
31
+ type WorkspaceUserGroup,
32
32
  } from '../utils';
33
33
 
34
34
  type LatestInvoice = {
@@ -38,9 +38,7 @@ type LatestInvoice = {
38
38
  };
39
39
 
40
40
  type UserGroupItem = {
41
- workspace_user_groups:
42
- | Database['public']['Tables']['workspace_user_groups']['Row']
43
- | null;
41
+ workspace_user_groups: WorkspaceUserGroup | null;
44
42
  };
45
43
 
46
44
  function hasSchedule(group: UserGroupItem['workspace_user_groups']): boolean {
@@ -84,7 +82,7 @@ function GroupRow({
84
82
  onToggle,
85
83
  t,
86
84
  }: {
87
- group: Database['public']['Tables']['workspace_user_groups']['Row'];
85
+ group: WorkspaceUserGroup;
88
86
  isSelected: boolean;
89
87
  isMonthPaid: boolean;
90
88
  latestInvoice: LatestInvoice | undefined;
@@ -1,5 +1,6 @@
1
1
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
2
  import { render, waitFor } from '@testing-library/react';
3
+ import type { ComponentProps } from 'react';
3
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
5
  import NewInvoicePage from './new-invoice-page';
5
6
 
@@ -62,10 +63,12 @@ function createQueryClient() {
62
63
  });
63
64
  }
64
65
 
65
- function renderPage() {
66
+ function renderPage(
67
+ props: Partial<ComponentProps<typeof NewInvoicePage>> = {}
68
+ ) {
66
69
  return render(
67
70
  <QueryClientProvider client={createQueryClient()}>
68
- <NewInvoicePage wsId="ws-1" />
71
+ <NewInvoicePage wsId="ws-1" {...props} />
69
72
  </QueryClientProvider>
70
73
  );
71
74
  }
@@ -80,7 +83,7 @@ describe('NewInvoicePage', () => {
80
83
  fetchMock = vi.fn().mockResolvedValue({
81
84
  ok: true,
82
85
  json: async () => ({
83
- DEFAULT_CURRENCY: 'VND',
86
+ DEFAULT_CURRENCY: 'SGD',
84
87
  DEFAULT_SUBSCRIPTION_CATEGORY_ID: 'category-1',
85
88
  default_wallet_id: 'wallet-1',
86
89
  }),
@@ -107,7 +110,7 @@ describe('NewInvoicePage', () => {
107
110
  await waitFor(() =>
108
111
  expect(invoiceMocks.StandardInvoice).toHaveBeenLastCalledWith(
109
112
  expect.objectContaining({
110
- defaultCurrency: 'VND',
113
+ defaultCurrency: 'SGD',
111
114
  defaultWalletId: 'wallet-1',
112
115
  })
113
116
  )
@@ -128,10 +131,27 @@ describe('NewInvoicePage', () => {
128
131
  expect(invoiceMocks.SubscriptionInvoice).toHaveBeenLastCalledWith(
129
132
  expect.objectContaining({
130
133
  defaultCategoryId: 'category-1',
131
- defaultCurrency: 'VND',
134
+ defaultCurrency: 'SGD',
132
135
  defaultWalletId: 'wallet-1',
133
136
  })
134
137
  )
135
138
  );
136
139
  });
140
+
141
+ it('uses the server-provided currency when batched config read is denied', async () => {
142
+ fetchMock.mockResolvedValueOnce({
143
+ ok: false,
144
+ status: 403,
145
+ });
146
+
147
+ renderPage({ defaultCurrency: 'VND' });
148
+
149
+ await waitFor(() =>
150
+ expect(invoiceMocks.StandardInvoice).toHaveBeenLastCalledWith(
151
+ expect.objectContaining({
152
+ defaultCurrency: 'VND',
153
+ })
154
+ )
155
+ );
156
+ });
137
157
  });
@@ -12,6 +12,7 @@ import { Separator } from '@tuturuuu/ui/separator';
12
12
  import { Skeleton } from '@tuturuuu/ui/skeleton';
13
13
  import { Switch } from '@tuturuuu/ui/switch';
14
14
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '@tuturuuu/ui/tabs';
15
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
15
16
  import { useTranslations } from 'next-intl';
16
17
  import { useQueryState } from 'nuqs';
17
18
  import { useState } from 'react';
@@ -38,6 +39,7 @@ interface Props {
38
39
  canReadInvoiceProducts?: boolean;
39
40
  canReadInvoiceProductStock?: boolean;
40
41
  canReadGroupLinkedProducts?: boolean;
42
+ defaultCurrency?: string;
41
43
  permissionRequestUser?: FinancePermissionRequestUser | null;
42
44
  }
43
45
 
@@ -49,6 +51,7 @@ export default function NewInvoicePage({
49
51
  canReadInvoiceProducts = true,
50
52
  canReadInvoiceProductStock = true,
51
53
  canReadGroupLinkedProducts = true,
54
+ defaultCurrency: workspaceDefaultCurrency,
52
55
  permissionRequestUser,
53
56
  }: Props) {
54
57
  const t = useTranslations();
@@ -74,8 +77,10 @@ export default function NewInvoicePage({
74
77
  const defaultWalletId = defaultConfigs.default_wallet_id ?? undefined;
75
78
  const defaultCategoryId =
76
79
  defaultConfigs.DEFAULT_SUBSCRIPTION_CATEGORY_ID ?? undefined;
77
- const defaultCurrency =
78
- defaultConfigs.DEFAULT_CURRENCY === 'VND' ? 'VND' : 'USD';
80
+ const defaultCurrency = resolveSupportedCurrency(
81
+ defaultConfigs.DEFAULT_CURRENCY,
82
+ resolveSupportedCurrency(workspaceDefaultCurrency)
83
+ );
79
84
 
80
85
  const [
81
86
  createMultipleInvoices,
@@ -6,6 +6,7 @@ import { Button } from '@tuturuuu/ui/button';
6
6
  import { Card, CardContent, CardHeader, CardTitle } from '@tuturuuu/ui/card';
7
7
  import { Separator } from '@tuturuuu/ui/separator';
8
8
  import { toast } from '@tuturuuu/ui/sonner';
9
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
9
10
  import { shouldLockFinanceWalletSelectionOnCreate } from '@tuturuuu/utils/finance';
10
11
  import { useRouter } from 'next/navigation';
11
12
  import { useTranslations } from 'next-intl';
@@ -56,7 +57,7 @@ interface Props {
56
57
  printAfterCreate?: boolean;
57
58
  downloadImageAfterCreate?: boolean;
58
59
  defaultWalletId?: string;
59
- defaultCurrency?: 'VND' | 'USD';
60
+ defaultCurrency?: string;
60
61
  canChangeFinanceWallets?: boolean;
61
62
  canSetFinanceWalletsOnCreate?: boolean;
62
63
  canReadInvoiceProducts?: boolean;
@@ -70,7 +71,7 @@ export function StandardInvoice({
70
71
  printAfterCreate = false,
71
72
  downloadImageAfterCreate = false,
72
73
  defaultWalletId,
73
- defaultCurrency = 'USD',
74
+ defaultCurrency: rawDefaultCurrency = 'USD',
74
75
  canChangeFinanceWallets = true,
75
76
  canSetFinanceWalletsOnCreate = true,
76
77
  canReadInvoiceProducts = true,
@@ -79,6 +80,7 @@ export function StandardInvoice({
79
80
  }: Props) {
80
81
  const t = useTranslations();
81
82
  const router = useRouter();
83
+ const defaultCurrency = resolveSupportedCurrency(rawDefaultCurrency);
82
84
  const { isConfidential: areNumbersHidden } =
83
85
  useFinanceConfidentialVisibility();
84
86
 
@@ -6,6 +6,7 @@ import { Button } from '@tuturuuu/ui/button';
6
6
  import { Card, CardContent, CardHeader, CardTitle } from '@tuturuuu/ui/card';
7
7
  import { Separator } from '@tuturuuu/ui/separator';
8
8
  import { toast } from '@tuturuuu/ui/sonner';
9
+ import { resolveSupportedCurrency } from '@tuturuuu/utils/currencies';
9
10
  import { shouldLockFinanceWalletSelectionOnCreate } from '@tuturuuu/utils/finance';
10
11
  import { useRouter } from 'next/navigation';
11
12
  import { useLocale, useTranslations } from 'next-intl';
@@ -81,7 +82,7 @@ interface Props {
81
82
  downloadImageAfterCreate?: boolean;
82
83
  defaultWalletId?: string;
83
84
  defaultCategoryId?: string;
84
- defaultCurrency?: 'VND' | 'USD';
85
+ defaultCurrency?: string;
85
86
  canChangeFinanceWallets?: boolean;
86
87
  canSetFinanceWalletsOnCreate?: boolean;
87
88
  canReadInvoiceProducts?: boolean;
@@ -98,7 +99,7 @@ export function SubscriptionInvoice({
98
99
  downloadImageAfterCreate = false,
99
100
  defaultWalletId,
100
101
  defaultCategoryId,
101
- defaultCurrency = 'USD',
102
+ defaultCurrency: rawDefaultCurrency = 'USD',
102
103
  canChangeFinanceWallets = true,
103
104
  canSetFinanceWalletsOnCreate = true,
104
105
  canReadInvoiceProducts = true,
@@ -108,6 +109,7 @@ export function SubscriptionInvoice({
108
109
  }: Props) {
109
110
  const t = useTranslations();
110
111
  const locale = useLocale();
112
+ const defaultCurrency = resolveSupportedCurrency(rawDefaultCurrency);
111
113
  const { isConfidential: areNumbersHidden } =
112
114
  useFinanceConfidentialVisibility();
113
115
  const router = useRouter();
@@ -14,7 +14,9 @@ type AttendanceStats = {
14
14
  };
15
15
 
16
16
  export type WorkspaceUserGroup =
17
- Database['public']['Tables']['workspace_user_groups']['Row'];
17
+ Database['public']['Tables']['workspace_user_groups']['Row'] & {
18
+ sessions?: string[] | null;
19
+ };
18
20
 
19
21
  export type UserGroup = {
20
22
  workspace_user_groups: WorkspaceUserGroup | null;
@@ -31,6 +31,7 @@ interface FormContentDialogProps {
31
31
  setNewContentType: Dispatch<SetStateAction<NewContentType | undefined>>;
32
32
  newContent: NewContent;
33
33
  setNewContent: Dispatch<SetStateAction<NewContent>>;
34
+ defaultCurrency?: string;
34
35
  newTagColor: string;
35
36
  setNewTagColor: Dispatch<SetStateAction<string>>;
36
37
  }
@@ -53,6 +54,7 @@ export function FormContentDialog({
53
54
  setNewContentType,
54
55
  newContent,
55
56
  setNewContent,
57
+ defaultCurrency,
56
58
  newTagColor,
57
59
  setNewTagColor,
58
60
  }: FormContentDialogProps) {
@@ -101,6 +103,7 @@ export function FormContentDialog({
101
103
  <WalletForm
102
104
  wsId={wsId}
103
105
  data={newContent as WalletType}
106
+ defaultCurrency={defaultCurrency}
104
107
  onFinish={() => {
105
108
  setNewContent(undefined);
106
109
  setNewContentType(undefined);
@@ -15,6 +15,8 @@ export type Transaction = DbTransaction & {
15
15
  linked_wallet_name: string;
16
16
  linked_wallet_currency?: string;
17
17
  linked_amount?: number;
18
+ linked_amount_redacted?: boolean;
19
+ linked_is_amount_confidential?: boolean;
18
20
  is_origin: boolean;
19
21
  };
20
22
  };
@@ -53,6 +55,7 @@ export interface TransactionFormProps {
53
55
  canUpdateConfidentialTransactions?: boolean;
54
56
  canChangeFinanceWallets?: boolean;
55
57
  canSetFinanceWalletsOnCreate?: boolean;
58
+ defaultCurrency?: string;
56
59
  initialMode?: TransactionFormInitialMode;
57
60
  initialTransaction?: TransactionFormInitialTransaction;
58
61
  initialTransfer?: TransactionFormInitialTransfer;
@@ -138,6 +138,7 @@ export function TransactionForm({
138
138
  canUpdateConfidentialTransactions,
139
139
  canChangeFinanceWallets = true,
140
140
  canSetFinanceWalletsOnCreate = true,
141
+ defaultCurrency,
141
142
  initialMode = 'transaction',
142
143
  initialTransaction,
143
144
  initialTransfer,
@@ -1005,6 +1006,7 @@ export function TransactionForm({
1005
1006
  setNewContentType={setNewContentType}
1006
1007
  newContent={newContent}
1007
1008
  setNewContent={setNewContent}
1009
+ defaultCurrency={defaultCurrency}
1008
1010
  newTagColor={newTagColor}
1009
1011
  setNewTagColor={setNewTagColor}
1010
1012
  />
@@ -1067,6 +1067,7 @@ export function InfiniteTransactionsList({
1067
1067
  canUpdateConfidentialTransactions={
1068
1068
  canUpdateConfidentialTransactions
1069
1069
  }
1070
+ defaultCurrency={currency}
1070
1071
  timezone={resolvedTimezone}
1071
1072
  refreshPageOnFinish={!!walletId}
1072
1073
  permissionRequestUser={permissionRequestUser}
@@ -1102,6 +1103,7 @@ export function InfiniteTransactionsList({
1102
1103
  canCreateConfidentialTransactions={
1103
1104
  canCreateConfidentialTransactions
1104
1105
  }
1106
+ defaultCurrency={currency}
1105
1107
  timezone={resolvedTimezone}
1106
1108
  preferInitialWalletSelection={!!walletId}
1107
1109
  refreshPageOnFinish={!!walletId}
@@ -497,7 +497,7 @@ export function CategoryBreakdownDialog({
497
497
 
498
498
  return (
499
499
  <Dialog open={open} onOpenChange={onOpenChange}>
500
- <DialogContent className="data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-bottom-2 data-[state=open]:slide-in-from-bottom-2 inset-0! top-0! left-0! flex h-screen max-h-screen w-screen max-w-none! translate-x-0! translate-y-0! flex-col gap-0 overflow-hidden rounded-none! border-0 p-0">
500
+ <DialogContent presentation="fullscreen" className="flex-col">
501
501
  {/* Header */}
502
502
  <DialogHeader className="shrink-0 border-b px-6 py-5">
503
503
  <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">