@tuturuuu/ui 0.6.2 → 0.8.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 (108) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/biome.json +1 -1
  3. package/package.json +11 -11
  4. package/src/components/ui/calendar-app/components/calendar-connections.tsx +17 -13
  5. package/src/components/ui/calendar-app/components/connected-accounts-dialog.tsx +2 -5
  6. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +2 -5
  7. package/src/components/ui/calendar.test.tsx +24 -0
  8. package/src/components/ui/calendar.tsx +1 -0
  9. package/src/components/ui/currency-input.test.tsx +43 -0
  10. package/src/components/ui/currency-input.tsx +1 -1
  11. package/src/components/ui/custom/workspace-access/workspace-access-default-role-card.tsx +60 -35
  12. package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
  13. package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
  14. package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
  15. package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
  16. package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
  17. package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
  18. package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
  19. package/src/components/ui/date-time-picker.tsx +352 -234
  20. package/src/components/ui/finance/categories-tags-tabs.tsx +23 -1
  21. package/src/components/ui/finance/command/finance-command-actions.test.tsx +48 -0
  22. package/src/components/ui/finance/command/finance-command-actions.tsx +200 -0
  23. package/src/components/ui/finance/command/finance-command-provider.test.tsx +151 -0
  24. package/src/components/ui/finance/command/finance-command-provider.tsx +250 -0
  25. package/src/components/ui/finance/command/finance-command-results.tsx +262 -0
  26. package/src/components/ui/finance/invoices/pending-invoices-table.tsx +22 -9
  27. package/src/components/ui/finance/shared/quick-actions.tsx +39 -90
  28. package/src/components/ui/finance/tags/tag-manager.tsx +24 -5
  29. package/src/components/ui/finance/transactions/form-basic-tab.tsx +33 -49
  30. package/src/components/ui/finance/transactions/form-types.ts +5 -0
  31. package/src/components/ui/finance/transactions/form.test.tsx +105 -22
  32. package/src/components/ui/finance/transactions/form.tsx +116 -20
  33. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +13 -6
  34. package/src/components/ui/finance/transactions/transaction-card.tsx +21 -9
  35. package/src/components/ui/finance/transactions/transaction-edit-dialog.test.tsx +25 -1
  36. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +16 -3
  37. package/src/components/ui/finance/transactions/transactionId/transaction-details-client-page.tsx +3 -0
  38. package/src/components/ui/finance/transactions/transactionId/transaction-details-page.tsx +3 -0
  39. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +6 -0
  40. package/src/components/ui/finance/transactions/transactions-infinite-page.tsx +20 -2
  41. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -0
  42. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-history-dialog.tsx +7 -2
  43. package/src/components/ui/finance/wallets/checkpoints/wallet-total-check-dialog.tsx +7 -2
  44. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.test.tsx +38 -1
  45. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +5 -0
  46. package/src/components/ui/finance/wallets/walletId/wallet-details-page.test.tsx +18 -2
  47. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +3 -0
  48. package/src/components/ui/finance/wallets/wallets-page.tsx +3 -0
  49. package/src/components/ui/legacy/calendar/settings/google-calendar-settings.tsx +2 -9
  50. package/src/components/ui/money-input.test.tsx +64 -0
  51. package/src/components/ui/money-input.tsx +63 -0
  52. package/src/components/ui/optional-time-picker.tsx +95 -0
  53. package/src/components/ui/quick-command-center.test.tsx +90 -0
  54. package/src/components/ui/quick-command-center.tsx +190 -0
  55. package/src/components/ui/storefront/cart-summary.tsx +126 -50
  56. package/src/components/ui/storefront/checkout-overlay.tsx +27 -0
  57. package/src/components/ui/storefront/hero-panel.tsx +23 -20
  58. package/src/components/ui/storefront/image-panel.tsx +6 -0
  59. package/src/components/ui/storefront/index.ts +11 -0
  60. package/src/components/ui/storefront/listing-card.tsx +84 -22
  61. package/src/components/ui/storefront/product-detail.tsx +289 -0
  62. package/src/components/ui/storefront/product-dialog.tsx +72 -0
  63. package/src/components/ui/storefront/storefront-surface.test.tsx +132 -5
  64. package/src/components/ui/storefront/storefront-surface.tsx +371 -128
  65. package/src/components/ui/storefront/types.ts +25 -1
  66. package/src/components/ui/storefront/utils.ts +118 -13
  67. package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
  68. package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
  69. package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -0
  70. package/src/components/ui/text-editor/content-migration.ts +41 -18
  71. package/src/components/ui/text-editor/extensions.ts +1 -1
  72. package/src/components/ui/text-editor/image-extension.ts +40 -18
  73. package/src/components/ui/text-editor/video-extension.ts +11 -2
  74. package/src/components/ui/tu-do/boards/__tests__/workspace-projects-client-page.test.tsx +70 -1
  75. package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +127 -0
  76. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +1 -3
  77. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +13 -0
  78. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +63 -0
  79. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +46 -8
  80. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +13 -2
  81. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +3 -1
  82. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +164 -0
  83. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +56 -2
  84. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-display.ts +9 -0
  85. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
  86. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
  87. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
  88. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
  89. package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
  90. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +34 -1
  91. package/src/components/ui/tu-do/shared/board-header.tsx +39 -0
  92. package/src/components/ui/tu-do/shared/board-views.tsx +9 -7
  93. package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
  94. package/src/components/ui/tu-do/shared/task-dialog-presentation.test.ts +53 -0
  95. package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
  96. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
  97. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
  98. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -1
  99. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/task-api.test.ts +171 -0
  100. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/task-api.ts +200 -36
  101. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +21 -2
  102. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +42 -14
  103. package/src/hooks/__tests__/useBoardRealtime.test.tsx +2 -2
  104. package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -0
  105. package/src/hooks/useBoardRealtime.ts +6 -3
  106. package/src/hooks/useBoardRealtime.types.ts +11 -0
  107. package/src/hooks/useCursorTracking.ts +91 -27
  108. package/src/hooks/useTaskUserRealtime.ts +5 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0](https://github.com/tutur3u/platform/compare/ui-v0.7.0...ui-v0.8.0) (2026-06-17)
4
+
5
+
6
+ ### Features
7
+
8
+ * **inventory:** fix Polar checkout currency, add 2-way product sync, cache + redesign storefront ([de2f6fd](https://github.com/tutur3u/platform/commit/de2f6fd6e06ce5242a150a35d3989798f52b9ee9))
9
+ * **inventory:** per-variant SKUs + storefront cart, dialog & instant checkout ([9662b85](https://github.com/tutur3u/platform/commit/9662b8501bcab51033edde79b44991c8ba648a37))
10
+ * **inventory:** store commerce money in minor units and harden Polar sync ([3f7ee1d](https://github.com/tutur3u/platform/commit/3f7ee1da9335732854037e7e79fca9d5d2a381d0))
11
+ * **storefront-ui:** dedicated product page, richer cart, loading skeleton ([d4a67f8](https://github.com/tutur3u/platform/commit/d4a67f8e6097d51aab6ca983245529725fcaba5c))
12
+ * **storefront-ui:** polish checkout layout and order-status page ([ead94ed](https://github.com/tutur3u/platform/commit/ead94edaa2ac6e79f9e56fd9f48d7a098487f2de))
13
+ * **storefront:** dedicated checkout page with buyer details + order-placed success ([258014d](https://github.com/tutur3u/platform/commit/258014d642e7cf99164e92fec44cd32114ecf68d))
14
+ * **storefront:** make the storefront accent color visibly prominent ([f2b5289](https://github.com/tutur3u/platform/commit/f2b5289506fd8e289f4bfb2bb6755cbc10ea2430))
15
+ * **storefront:** polish checkout cart experience ([cfdccfa](https://github.com/tutur3u/platform/commit/cfdccfa089703d9dde4357eeef8e52c00ccf6d4b))
16
+ * **tasks:** add per-board default list for new tasks ([2d1d308](https://github.com/tutur3u/platform/commit/2d1d3082422bdd4813accb258fee79b322ce647b))
17
+ * **web:** consolidate workspace roles into members and redesign access UI ([4a16407](https://github.com/tutur3u/platform/commit/4a164070e1fe020a834ce4fe77ff4ae371e4e366))
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **ci:** format storefront files and harden upstash scan wrapper ([4c82444](https://github.com/tutur3u/platform/commit/4c824446725dc280edde3e54f946bf386a52952d))
23
+ * **ci:** sort tailwind classes in storefront product-detail (biome lint) ([809c3b9](https://github.com/tutur3u/platform/commit/809c3b9b1e514fb218b58941c9542c75bd723f2d))
24
+ * **deps:** keep lodash on latest reviewed artifact ([19909b3](https://github.com/tutur3u/platform/commit/19909b334581d3b58cdcd19e9b2fde553f7ad60a))
25
+ * **deps:** pin reviewed lodash artifact ([dfcf585](https://github.com/tutur3u/platform/commit/dfcf585fab9cc0b425cac5d60c5bccc997340be5))
26
+ * **editor:** clear stale upload handlers ([ed34408](https://github.com/tutur3u/platform/commit/ed34408e9e48ee13bf7e69c2a8ba222d75c67253))
27
+ * **epm:** validate summary editor content ([bd87c77](https://github.com/tutur3u/platform/commit/bd87c770690c75a125fe2a727f3ad39aa925b5fd))
28
+ * **finance:** harden transaction enrichment ([eddd93b](https://github.com/tutur3u/platform/commit/eddd93bd11fb451a7fa5da2e4ab2892fad931ab5))
29
+ * **inventory:** block unsafe storefront section links ([870ce76](https://github.com/tutur3u/platform/commit/870ce760237f43ca2a3d1221359c4a06d1bc7653))
30
+ * **storefront-ui:** prevent horizontal overflow and cap cart height ([5825c49](https://github.com/tutur3u/platform/commit/5825c49fb9b77c859cda4ec6bbb6ba382925e519))
31
+ * **storefront-ui:** show an empty-cart state instead of "no listings" in the cart ([00bdb40](https://github.com/tutur3u/platform/commit/00bdb40eb344b17c3727fda1ea0c042508c63901))
32
+ * **tasks:** authorize board page before workspace ([9b1f740](https://github.com/tutur3u/platform/commit/9b1f74096a4a889b7733b6ffe37d4d16f442d974))
33
+ * **tasks:** cap timeline rendering work ([f2722ee](https://github.com/tutur3u/platform/commit/f2722ee0c3778153e1b014280bc1513305c5be7a))
34
+ * **tasks:** gate boards data access ([6daf363](https://github.com/tutur3u/platform/commit/6daf363b1c73593a233371a6cf079a3a4a015daa))
35
+ * **tasks:** ignore stale drag rollbacks ([c60131f](https://github.com/tutur3u/platform/commit/c60131f9f0930c81d3d2ccc6f2f9e918968d4b77))
36
+ * **tasks:** refine document task dialog presentation ([ca10735](https://github.com/tutur3u/platform/commit/ca10735f086a24be065eded03b8bc6e9382e2c5f))
37
+ * **tasks:** secure cursor realtime channels ([a0ec120](https://github.com/tutur3u/platform/commit/a0ec120d912b7998fe43c90b675a09d0d3798dfe))
38
+ * **tasks:** secure realtime task channels ([6d98d16](https://github.com/tutur3u/platform/commit/6d98d16baa9ecf68bdd47ce3ce6dc1ff2e2bca84))
39
+ * **tasks:** secure realtime task channels ([03dc6d6](https://github.com/tutur3u/platform/commit/03dc6d66666d1d3ae422f91cb94285367a8c1071))
40
+ * **tasks:** stop external lane retry loops ([713c327](https://github.com/tutur3u/platform/commit/713c327a613b018e72e376c03324517c6ee673c3))
41
+ * **tu-do:** center compact task edit dialog instead of anchoring to bottom ([67f38aa](https://github.com/tutur3u/platform/commit/67f38aaab1eab4b5340dfe049eed9c1e224c83b1))
42
+ * **tu-do:** preserve newlines and dim compact description preview ([ee01f40](https://github.com/tutur3u/platform/commit/ee01f40ab5ae800bf8aa1c59f19eb4f2bd69c256))
43
+ * **ui:** keep locale decimals in currency input ([3cd2420](https://github.com/tutur3u/platform/commit/3cd242026c3afe51eedabc3ec070093d0a94523f))
44
+ * **web:** stop new-workspace setup from trapping users on "Preparing Workspace" ([3f7ee1d](https://github.com/tutur3u/platform/commit/3f7ee1da9335732854037e7e79fca9d5d2a381d0))
45
+
46
+
47
+ ### Performance Improvements
48
+
49
+ * **storefront-ui:** lazy-load product images, eager-load above-the-fold ([6da600e](https://github.com/tutur3u/platform/commit/6da600eec0ce964ac61ae7890cf4574ada6aef3e))
50
+
51
+ ## [0.7.0](https://github.com/tutur3u/platform/compare/ui-v0.6.2...ui-v0.7.0) (2026-06-15)
52
+
53
+
54
+ ### Features
55
+
56
+ * **finance:** add command center shortcuts ([d0197f7](https://github.com/tutur3u/platform/commit/d0197f705227cdf8171704b40119b66b1140be80))
57
+ * **inventory:** field tooltips, tabbed dialogs, row-click edit, storefront polish ([e27d7bb](https://github.com/tutur3u/platform/commit/e27d7bbd75a76b8e6d9395faaa48ad6b465dca4b))
58
+ * **inventory:** upgrade storefront commerce ([ac43942](https://github.com/tutur3u/platform/commit/ac439426ec6d7abc25efbf7ef88468e32be3a46e))
59
+ * **ui:** add optional time picker ([d4cf439](https://github.com/tutur3u/platform/commit/d4cf4398c8efd8ca5267b770e07f1015a59d43fd))
60
+ * **ui:** redesign optional time picker ([cadbf12](https://github.com/tutur3u/platform/commit/cadbf124a9048d85164082eec2b857feb78b1070))
61
+
62
+
63
+ ### Bug Fixes
64
+
65
+ * **calendar:** preserve Google OAuth refresh tokens ([4358623](https://github.com/tutur3u/platform/commit/4358623a2dbde76fd84af59bb18e540994a5d091))
66
+ * **finance:** stop pending invoices from intermittently failing to load ([c4a51b8](https://github.com/tutur3u/platform/commit/c4a51b89e3cad8130b8016864cf25f4da6db6fc1))
67
+ * **tasks:** persist large task descriptions in chunks ([457744a](https://github.com/tutur3u/platform/commit/457744aa051d06baccc5df5aa4d4cb509534ea8b))
68
+
3
69
  ## [0.6.2](https://github.com/tutur3u/platform/compare/ui-v0.6.1...ui-v0.6.2) (2026-06-13)
4
70
 
5
71
 
package/biome.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "root": false,
3
- "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json",
3
+ "$schema": "https://biomejs.dev/schemas/2.5.0/schema.json",
4
4
  "extends": "//"
5
5
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tuturuuu/ui",
3
- "version": "0.6.2",
3
+ "version": "0.8.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -83,12 +83,12 @@
83
83
  "@tiptap/react": "3.26.1",
84
84
  "@tiptap/starter-kit": "3.26.1",
85
85
  "@tuturuuu/ai": "0.2.2",
86
- "@tuturuuu/apis": "0.4.1",
86
+ "@tuturuuu/apis": "0.5.0",
87
87
  "@tuturuuu/hooks": "0.0.2",
88
88
  "@tuturuuu/icons": "0.0.6",
89
- "@tuturuuu/internal-api": "0.7.1",
90
- "@tuturuuu/supabase": "0.3.3",
91
- "@tuturuuu/utils": "0.6.1",
89
+ "@tuturuuu/internal-api": "0.9.0",
90
+ "@tuturuuu/supabase": "0.3.4",
91
+ "@tuturuuu/utils": "0.8.0",
92
92
  "@types/debug": "^4.1.13",
93
93
  "browser-image-compression": "^2.0.2",
94
94
  "class-variance-authority": "^0.7.1",
@@ -105,7 +105,7 @@
105
105
  "html2canvas-pro": "^2.0.2",
106
106
  "input-otp": "^1.4.2",
107
107
  "katex": "^0.17.0",
108
- "lodash": "^4.18.1",
108
+ "lodash": "4.18.1",
109
109
  "moment": "^2.30.1",
110
110
  "motion": "^12.40.0",
111
111
  "next": "^16.2.9",
@@ -119,7 +119,7 @@
119
119
  "react-day-picker": "^10.0.1",
120
120
  "react-dom": "^19.2.7",
121
121
  "react-dropzone": "^15.0.0",
122
- "react-hook-form": "^7.77.0",
122
+ "react-hook-form": "^7.79.0",
123
123
  "react-markdown": "^10.1.0",
124
124
  "react-papaparse": "^4.4.0",
125
125
  "react-pdf": "^10.4.1",
@@ -135,19 +135,19 @@
135
135
  "tiptap-extension-resize-image": "^1.4.3",
136
136
  "use-debounce": "^10.1.1",
137
137
  "vaul": "^1.1.2",
138
- "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz",
138
+ "xlsx": "file:vendor/xlsx-0.20.3.tgz",
139
139
  "y-protocols": "^1.0.7",
140
140
  "yjs": "^13.6.31",
141
141
  "zod": "^4.4.3"
142
142
  },
143
143
  "devDependencies": {
144
- "@tailwindcss/postcss": "^4.3.0",
144
+ "@tailwindcss/postcss": "^4.3.1",
145
145
  "@tailwindcss/typography": "^0.5.20",
146
146
  "@tanstack/react-query": "^5.101.0",
147
147
  "@tanstack/react-table": "^8.21.3",
148
148
  "@testing-library/jest-dom": "^6.9.1",
149
149
  "@testing-library/react": "^16.3.2",
150
- "@tuturuuu/types": "0.8.0",
150
+ "@tuturuuu/types": "0.10.0",
151
151
  "@tuturuuu/typescript-config": "0.1.1",
152
152
  "@types/html2canvas": "^1.0.0",
153
153
  "@types/lodash": "^4.17.24",
@@ -162,7 +162,7 @@
162
162
  "jsdom": "^29.1.1",
163
163
  "postcss": "^8.5.14",
164
164
  "postcss-load-config": "^6.0.1",
165
- "tailwindcss": "^4.3.0",
165
+ "tailwindcss": "^4.3.1",
166
166
  "typescript": "^6.0.3",
167
167
  "uuid": "^14.0.0",
168
168
  "vitest": "^4.1.8"
@@ -15,6 +15,10 @@ import {
15
15
  Trash2,
16
16
  Upload,
17
17
  } from '@tuturuuu/icons';
18
+ import {
19
+ getGoogleCalendarAuthUrl,
20
+ InternalApiError,
21
+ } from '@tuturuuu/internal-api';
18
22
  import { useTranslations } from 'next-intl';
19
23
  import { useMemo, useState } from 'react';
20
24
  import { useCalendarSync } from '../../../../hooks/use-calendar-sync';
@@ -292,21 +296,21 @@ export default function CalendarConnections({
292
296
  const googleAuthMutation = useMutation<AuthResponse, Error, void>({
293
297
  mutationKey: ['calendar', 'google-auth', wsId],
294
298
  mutationFn: async () => {
295
- const response = await fetch(`/api/v1/calendar/auth?wsId=${wsId}`, {
296
- method: 'GET',
297
- });
299
+ try {
300
+ return await getGoogleCalendarAuthUrl(wsId);
301
+ } catch (error) {
302
+ if (error instanceof InternalApiError) {
303
+ throw new Error(
304
+ error.status === 401
305
+ ? 'unauthorized'
306
+ : error.status === 403
307
+ ? 'forbidden'
308
+ : 'unknown_error'
309
+ );
310
+ }
298
311
 
299
- if (!response.ok) {
300
- throw new Error(
301
- response.status === 401
302
- ? 'unauthorized'
303
- : response.status === 403
304
- ? 'forbidden'
305
- : 'unknown_error'
306
- );
312
+ throw error;
307
313
  }
308
-
309
- return (await response.json()) as AuthResponse;
310
314
  },
311
315
  onSuccess: (data) => {
312
316
  const { authUrl } = data;
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
4
4
  import { AlertCircle, Loader2, Plus, Trash2, User } from '@tuturuuu/icons';
5
+ import { getGoogleCalendarAuthUrl } from '@tuturuuu/internal-api';
5
6
  import Image from 'next/image';
6
7
  import { useTranslations } from 'next-intl';
7
8
  import React, { useState } from 'react';
@@ -137,11 +138,7 @@ export function ConnectedAccountsDialog({
137
138
  // Google auth mutation
138
139
  const googleAuthMutation = useMutation<AuthResponse, Error, void>({
139
140
  mutationKey: ['calendar', 'google-auth', wsId],
140
- mutationFn: async () => {
141
- const response = await fetch(`/api/v1/calendar/auth?wsId=${wsId}`);
142
- if (!response.ok) throw new Error('Failed to get auth URL');
143
- return response.json();
144
- },
141
+ mutationFn: () => getGoogleCalendarAuthUrl(wsId),
145
142
  onSuccess: (data) => {
146
143
  if (data.authUrl) {
147
144
  window.location.href = data.authUrl;
@@ -1,6 +1,7 @@
1
1
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
2
  import {
3
3
  type CalendarSourceOption,
4
+ getGoogleCalendarAuthUrl,
4
5
  getWorkspaceCalendarDefaultSource,
5
6
  updateWorkspaceCalendarDefaultSource,
6
7
  } from '@tuturuuu/internal-api';
@@ -392,11 +393,7 @@ export function useCalendarConnectionsManager(wsId: string) {
392
393
  // Google auth mutation
393
394
  const googleAuthMutation = useMutation<AuthResponse, Error, void>({
394
395
  mutationKey: ['calendar', 'google-auth', wsId],
395
- mutationFn: async () => {
396
- const response = await fetch(`/api/v1/calendar/auth?wsId=${wsId}`);
397
- if (!response.ok) throw new Error('Failed to get auth URL');
398
- return response.json();
399
- },
396
+ mutationFn: () => getGoogleCalendarAuthUrl(wsId),
400
397
  onSuccess: (data) => {
401
398
  if (data.authUrl) window.location.href = data.authUrl;
402
399
  else toast.error(t('auth_url_invalid'));
@@ -0,0 +1,24 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { Calendar } from './calendar';
4
+
5
+ vi.mock('react-day-picker', () => ({
6
+ DayPicker: ({ fixedWeeks }: { fixedWeeks?: boolean }) => (
7
+ <div data-fixed-weeks={String(fixedWeeks)} data-testid="day-picker" />
8
+ ),
9
+ }));
10
+
11
+ vi.mock('../../hooks/use-calendar-preferences', () => ({
12
+ useCalendarPreferences: () => ({ weekStartsOn: 1 }),
13
+ }));
14
+
15
+ describe('Calendar', () => {
16
+ it('renders DayPicker with fixed weeks to avoid layout shift', () => {
17
+ render(<Calendar mode="single" />);
18
+
19
+ expect(screen.getByTestId('day-picker')).toHaveAttribute(
20
+ 'data-fixed-weeks',
21
+ 'true'
22
+ );
23
+ });
24
+ });
@@ -311,6 +311,7 @@ function Calendar({
311
311
  onMonthChange={setMonth}
312
312
  defaultMonth={initialMonth}
313
313
  showOutsideDays={true}
314
+ fixedWeeks={true}
314
315
  weekStartsOn={props.weekStartsOn ?? preferences.weekStartsOn}
315
316
  className={cn('', className)}
316
317
  classNames={normalizeClassNames({
@@ -0,0 +1,43 @@
1
+ import { fireEvent, render, screen } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+ import { CurrencyInput } from './currency-input';
4
+
5
+ describe('CurrencyInput', () => {
6
+ it('keeps locale decimal separators while editing', () => {
7
+ const onChange = vi.fn();
8
+
9
+ render(
10
+ <CurrencyInput
11
+ aria-label="Amount"
12
+ hideHelpers
13
+ locale="vi-VN"
14
+ maximumFractionDigits={2}
15
+ onChange={onChange}
16
+ value={undefined}
17
+ />
18
+ );
19
+
20
+ const input = screen.getByLabelText('Amount') as HTMLInputElement;
21
+
22
+ fireEvent.focus(input);
23
+ fireEvent.change(input, {
24
+ target: {
25
+ selectionStart: 3,
26
+ value: '1,2',
27
+ },
28
+ });
29
+
30
+ expect(input).toHaveValue('1,2');
31
+ expect(onChange).toHaveBeenLastCalledWith(1.2);
32
+
33
+ fireEvent.change(input, {
34
+ target: {
35
+ selectionStart: input.value.length + 1,
36
+ value: `${input.value}3`,
37
+ },
38
+ });
39
+
40
+ expect(input).toHaveValue('1,23');
41
+ expect(onChange).toHaveBeenLastCalledWith(1.23);
42
+ });
43
+ });
@@ -197,7 +197,7 @@ export const CurrencyInput = forwardRef<HTMLInputElement, CurrencyInputProps>(
197
197
  // Build final formatted string
198
198
  let formatted = formattedInteger;
199
199
  if (hasDecimal) {
200
- formatted += `.${decimalPart}`;
200
+ formatted += `${localeSeparators.decimal}${decimalPart}`;
201
201
  }
202
202
 
203
203
  // Calculate new cursor position
@@ -1,15 +1,8 @@
1
1
  'use client';
2
2
 
3
- import { Pencil } from '@tuturuuu/icons';
3
+ import { KeyRound, Pencil, ShieldUser } from '@tuturuuu/icons';
4
4
  import type { WorkspaceDefaultPermissionMemberType } from '@tuturuuu/types';
5
5
  import { Button } from '@tuturuuu/ui/button';
6
- import {
7
- Card,
8
- CardContent,
9
- CardDescription,
10
- CardHeader,
11
- CardTitle,
12
- } from '@tuturuuu/ui/card';
13
6
  import { Skeleton } from '@tuturuuu/ui/skeleton';
14
7
  import { useTranslations } from 'next-intl';
15
8
  import type { WorkspaceAccessRole } from './types';
@@ -37,45 +30,77 @@ export function WorkspaceAccessDefaultRoleCard({
37
30
  }) {
38
31
  const t = useTranslations() as (key: string) => string;
39
32
  const isGuest = memberType === 'GUEST';
33
+ const enabled = enabledPermissionCount(role);
34
+ const pct =
35
+ permissionCount > 0 ? Math.round((enabled / permissionCount) * 100) : 0;
36
+ const accent = isGuest
37
+ ? 'border-dynamic-blue/30 bg-dynamic-blue/10 text-dynamic-blue'
38
+ : 'border-dynamic-green/30 bg-dynamic-green/10 text-dynamic-green';
39
+ const barColor = isGuest ? 'bg-dynamic-blue' : 'bg-dynamic-green';
40
40
 
41
41
  return (
42
- <Card className="shadow-none">
43
- <CardHeader>
44
- <div className="flex flex-wrap items-start justify-between gap-3">
45
- <div className="space-y-1">
46
- <CardTitle className="text-base">
42
+ <div className="rounded-xl border border-border bg-background p-5">
43
+ <div className="flex flex-wrap items-start justify-between gap-3">
44
+ <div className="flex min-w-0 gap-3">
45
+ <div
46
+ className={`flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border ${accent}`}
47
+ >
48
+ {isGuest ? (
49
+ <KeyRound className="h-5 w-5" />
50
+ ) : (
51
+ <ShieldUser className="h-5 w-5" />
52
+ )}
53
+ </div>
54
+ <div className="min-w-0">
55
+ <div className="font-semibold text-base">
47
56
  {isGuest
48
57
  ? t('ws-roles.guest_defaults')
49
58
  : t('ws-roles.member_defaults')}
50
- </CardTitle>
51
- <CardDescription>
59
+ </div>
60
+ <p className="mt-0.5 max-w-md text-muted-foreground text-sm">
52
61
  {isGuest
53
62
  ? t('ws-roles.guest_defaults_description')
54
63
  : t('ws-roles.member_defaults_description')}
55
- </CardDescription>
64
+ </p>
56
65
  </div>
57
- {canManageRoles ? (
58
- <Button
59
- variant="outline"
60
- size="sm"
61
- onClick={() => onEdit(memberType)}
62
- >
63
- <Pencil className="mr-2 h-4 w-4" />
64
- {t('common.edit')}
65
- </Button>
66
- ) : null}
67
66
  </div>
68
- </CardHeader>
69
- <CardContent className="space-y-3">
67
+ {canManageRoles ? (
68
+ <Button
69
+ variant="outline"
70
+ size="sm"
71
+ onClick={() => onEdit(memberType)}
72
+ >
73
+ <Pencil className="mr-2 h-4 w-4" />
74
+ {t('common.edit')}
75
+ </Button>
76
+ ) : null}
77
+ </div>
78
+
79
+ <div className="mt-4 space-y-3">
70
80
  {isLoading ? (
71
81
  <Skeleton className="h-24 rounded-lg" />
72
82
  ) : (
73
83
  <>
74
- <div className="font-semibold text-2xl tabular-nums">
75
- {enabledPermissionCount(role)}
76
- <span className="ml-1 font-normal text-base text-muted-foreground">
77
- / {permissionCount}
78
- </span>
84
+ <div>
85
+ <div className="flex items-baseline justify-between gap-1.5">
86
+ <div className="flex items-baseline gap-1.5">
87
+ <span className="font-bold text-2xl tabular-nums">
88
+ {enabled}
89
+ </span>
90
+ <span className="text-base text-muted-foreground">
91
+ / {permissionCount}
92
+ </span>
93
+ </div>
94
+ <span className="text-muted-foreground text-sm">
95
+ {t('ws-roles.permissions')}
96
+ </span>
97
+ </div>
98
+ <div className="mt-2 h-1.5 max-w-md overflow-hidden rounded-full bg-foreground/10">
99
+ <div
100
+ className={`h-full rounded-full ${barColor}`}
101
+ style={{ width: `${pct}%` }}
102
+ />
103
+ </div>
79
104
  </div>
80
105
  <div className="flex flex-wrap gap-2">
81
106
  <WorkspaceAccessPermissionPreview
@@ -86,7 +111,7 @@ export function WorkspaceAccessDefaultRoleCard({
86
111
  </div>
87
112
  </>
88
113
  )}
89
- </CardContent>
90
- </Card>
114
+ </div>
115
+ </div>
91
116
  );
92
117
  }