@tuturuuu/ui 0.30.2 → 0.31.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 (91) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/biome.json +1 -1
  3. package/package.json +95 -49
  4. package/src/components/ui/calendar-app/calendar-client-page.test.tsx +57 -0
  5. package/src/components/ui/calendar-app/calendar-client-page.tsx +13 -4
  6. package/src/components/ui/calendar-app/calendar-page-shell.tsx +7 -1
  7. package/src/components/ui/calendar-app/components/calendar-connections-compact.tsx +47 -39
  8. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +14 -0
  9. package/src/components/ui/calendar-app/components/calendar-connections-unified.tsx +4 -0
  10. package/src/components/ui/calendar-app/components/calendar-sync-attention-button.tsx +47 -0
  11. package/src/components/ui/calendar-app/components/calendar-sync-recovery.test.tsx +112 -0
  12. package/src/components/ui/calendar-app/components/calendar-sync-recovery.tsx +206 -0
  13. package/src/components/ui/calendar-app/components/calendar-types.ts +1 -1
  14. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +86 -90
  15. package/src/components/ui/custom/language-dropdown-item.tsx +6 -14
  16. package/src/components/ui/custom/nav-link.tsx +7 -7
  17. package/src/components/ui/custom/nav.tsx +15 -63
  18. package/src/components/ui/custom/notification-card.tsx +361 -0
  19. package/src/components/ui/custom/notification-list.tsx +178 -0
  20. package/src/components/ui/custom/notification-popover-client.tsx +12 -780
  21. package/src/components/ui/custom/notification-runtime.tsx +23 -0
  22. package/src/components/ui/custom/onboarding-settings-panel.tsx +8 -55
  23. package/src/components/ui/custom/satellite-account-switcher-menu.tsx +212 -0
  24. package/src/components/ui/custom/satellite-brand.tsx +122 -0
  25. package/src/components/ui/custom/satellite-content.tsx +148 -0
  26. package/src/components/ui/custom/satellite-footer-actions.tsx +350 -0
  27. package/src/components/ui/custom/satellite-language-item.tsx +26 -0
  28. package/src/components/ui/custom/satellite-navigation.tsx +86 -0
  29. package/src/components/ui/custom/satellite-notification-popover.tsx +272 -0
  30. package/src/components/ui/custom/satellite-onboarding-settings-panel.tsx +80 -0
  31. package/src/components/ui/custom/satellite-settings-dialog-shell.tsx +621 -0
  32. package/src/components/ui/custom/satellite-shell-utils.ts +163 -0
  33. package/src/components/ui/custom/satellite-shell.test.tsx +169 -0
  34. package/src/components/ui/custom/satellite-shell.tsx +50 -0
  35. package/src/components/ui/custom/satellite-theme-dropdown-items.tsx +92 -0
  36. package/src/components/ui/custom/satellite-user-menu-items.tsx +190 -0
  37. package/src/components/ui/custom/satellite-user-menu.tsx +93 -0
  38. package/src/components/ui/custom/satellite-workspace-section.tsx +46 -0
  39. package/src/components/ui/custom/settings/keyboard-shortcuts-settings.tsx +16 -0
  40. package/src/components/ui/custom/settings-dialog-shell.tsx +21 -600
  41. package/src/components/ui/custom/sidebar-context.tsx +5 -14
  42. package/src/components/ui/custom/sidebar-footer-actions.tsx +19 -322
  43. package/src/components/ui/custom/structure.tsx +14 -0
  44. package/src/components/ui/custom/system-language-dropdown-item.tsx +7 -14
  45. package/src/components/ui/custom/theme-dropdown-items.tsx +2 -85
  46. package/src/components/ui/custom/use-satellite-shell.tsx +165 -0
  47. package/src/components/ui/legacy/calendar/agenda-view.tsx +139 -47
  48. package/src/components/ui/legacy/calendar/calendar-content.test.tsx +116 -0
  49. package/src/components/ui/legacy/calendar/calendar-content.tsx +38 -74
  50. package/src/components/ui/legacy/calendar/calendar-event-tone.ts +20 -0
  51. package/src/components/ui/legacy/calendar/calendar-meeting-link.test.ts +27 -0
  52. package/src/components/ui/legacy/calendar/calendar-meeting-link.ts +34 -0
  53. package/src/components/ui/legacy/calendar/calendar-period.ts +28 -0
  54. package/src/components/ui/legacy/calendar/calendar-shortcuts.test.tsx +107 -0
  55. package/src/components/ui/legacy/calendar/calendar-views.test.tsx +169 -0
  56. package/src/components/ui/legacy/calendar/event-modal-header.tsx +52 -0
  57. package/src/components/ui/legacy/calendar/event-modal.tsx +107 -156
  58. package/src/components/ui/legacy/calendar/month-calendar.tsx +242 -933
  59. package/src/components/ui/legacy/calendar/save-calendar-event-drafts.test.ts +25 -0
  60. package/src/components/ui/legacy/calendar/save-calendar-event-drafts.ts +32 -0
  61. package/src/components/ui/legacy/calendar/smart-calendar.tsx +3 -0
  62. package/src/components/ui/legacy/calendar/use-calendar-view-shortcuts.ts +54 -0
  63. package/src/components/ui/legacy/calendar/use-event-draft-session.test.tsx +59 -0
  64. package/src/components/ui/legacy/calendar/use-event-draft-session.ts +20 -0
  65. package/src/components/ui/legacy/calendar/year-calendar.tsx +34 -16
  66. package/src/components/ui/pwa-status.test.tsx +53 -0
  67. package/src/components/ui/pwa-status.tsx +113 -0
  68. package/src/components/ui/report-problem-dialog-content.tsx +350 -0
  69. package/src/components/ui/report-problem-dialog.tsx +6 -975
  70. package/src/components/ui/report-problem-model.ts +133 -0
  71. package/src/components/ui/report-problem-mutation.ts +122 -0
  72. package/src/components/ui/save-notification.test.tsx +36 -0
  73. package/src/components/ui/save-notification.tsx +17 -0
  74. package/src/components/ui/use-report-problem.ts +488 -0
  75. package/src/declarations.d.ts +2 -0
  76. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +46 -4
  77. package/src/hooks/__tests__/use-calendar-sync.test.tsx +24 -0
  78. package/src/hooks/calendar-date-utils.ts +11 -0
  79. package/src/hooks/calendar-event-lookup.test.ts +77 -0
  80. package/src/hooks/calendar-event-lookup.ts +47 -0
  81. package/src/hooks/calendar-range-cache.ts +52 -0
  82. package/src/hooks/pwa-install-prompt.ts +44 -0
  83. package/src/hooks/use-calendar-sync.tsx +23 -100
  84. package/src/hooks/use-calendar.tsx +21 -77
  85. package/src/hooks/use-notifications.ts +9 -24
  86. package/src/hooks/use-open-initial-calendar-event.ts +27 -0
  87. package/src/hooks/use-settings-dialog-shortcut.test.tsx +41 -0
  88. package/src/hooks/use-settings-dialog-shortcut.ts +12 -14
  89. package/src/hooks/use-workspace-presence.ts +0 -18
  90. package/src/lib/tasks-app-url.test.ts +25 -0
  91. package/src/lib/tasks-app-url.ts +8 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.0](https://github.com/tutur3u/platform/compare/ui-v0.30.2...ui-v0.31.0) (2026-09-07)
4
+
5
+
6
+ ### Features
7
+
8
+ * **calendar:** add installable apps and bounded PWA caching ([f4b2a1c](https://github.com/tutur3u/platform/commit/f4b2a1cf7943783fddc45f537df73589f19594f6))
9
+ * **calendar:** add installable Tasks and Calendar with bounded caching ([#5231](https://github.com/tutur3u/platform/issues/5231)) ([7078824](https://github.com/tutur3u/platform/commit/70788244874d811da3e1ce3a9af2c9389739a8c3))
10
+ * **colab:** launch multiplayer prompt workshops on Cloudflare ([7a4a3f9](https://github.com/tutur3u/platform/commit/7a4a3f9b4a12a0d7439cdb296398a21c0cd8939f))
11
+ * **colab:** make workshops a login-first workspace ([89613ca](https://github.com/tutur3u/platform/commit/89613ca10de53fe39412c58342ee453cd0db4006))
12
+ * **colab:** reuse the shared Tuturuuu structure shell ([11ca1fa](https://github.com/tutur3u/platform/commit/11ca1fa35bba8a64fdb9b56972b5c0f11c87339f))
13
+ * **meet:** integrate instant rooms with Calendar ([9631fd1](https://github.com/tutur3u/platform/commit/9631fd1373615f3e8be55dd028d54b441e78a023))
14
+ * **meet:** integrate instant rooms with Calendar ([#5248](https://github.com/tutur3u/platform/issues/5248)) ([5f3e77d](https://github.com/tutur3u/platform/commit/5f3e77df20e636fe270cdc7d1b1d88817a3f00bd))
15
+ * **planning:** integrate tasks and calendar workflows ([454bb45](https://github.com/tutur3u/platform/commit/454bb4589cdc85967a75a1cb04e01dd004223cbd))
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **calendar:** align view controls and stabilize event pagination ([0497af1](https://github.com/tutur3u/platform/commit/0497af126102a59d1d12af6615505a98a2ccb6a7))
21
+ * **calendar:** contain month cell content on mobile ([a3518bb](https://github.com/tutur3u/platform/commit/a3518bbbe90009f87f6a7f38078027996bdcb3a3))
22
+ * **calendar:** expose sync failures and repair completion tracking ([a5601a5](https://github.com/tutur3u/platform/commit/a5601a5f0fd33741392a6f6e16dad337ba4f0a35))
23
+ * **calendar:** fail closed on linked event access ([9071075](https://github.com/tutur3u/platform/commit/90710750398ff0143f2a71f2b19c8631469018e9))
24
+ * **calendar:** handle partial sync in every client action ([b820bbc](https://github.com/tutur3u/platform/commit/b820bbcdb725e38c13342eed96062b2f4a0afacb))
25
+ * **calendar:** harden PWA lifecycle and cache retention ([14565c0](https://github.com/tutur3u/platform/commit/14565c0106e74b947e152fd8bb787de7f70bc54d))
26
+ * **calendar:** import sync helper from calendar entry point ([753dd22](https://github.com/tutur3u/platform/commit/753dd22ab9a480144ee94384aa9983d58787d311))
27
+ * **calendar:** initialize draft timezone helper ([e1b81c7](https://github.com/tutur3u/platform/commit/e1b81c75b8aef6d9ded2c162511f0c32b03b7db1))
28
+ * **calendar:** make sync failures visible and recoverable ([#5227](https://github.com/tutur3u/platform/issues/5227)) ([465c9f8](https://github.com/tutur3u/platform/commit/465c9f82c5464a54675fa9d9f814c630902916e6))
29
+ * **calendar:** preserve concurrent connection updates ([8307cbb](https://github.com/tutur3u/platform/commit/8307cbbc5d0ccb943eee77a259ab76147c1c698c))
30
+ * **calendar:** recover failed syncs and redesign calendar views ([7d22948](https://github.com/tutur3u/platform/commit/7d22948095a29d236f698d42dde362fc38311dd0))
31
+ * **calendar:** recover failed syncs and redesign calendar views ([#5230](https://github.com/tutur3u/platform/issues/5230)) ([e52605d](https://github.com/tutur3u/platform/commit/e52605d671f921b1564746afcdedf2478deee78e))
32
+ * **calendar:** respect week preferences and classify DNS failures ([ff88780](https://github.com/tutur3u/platform/commit/ff88780dc3d25edb46b5e15ee323d8eee3ad26db))
33
+ * **calendar:** restore provider assets in tasks and format sync time ([0359132](https://github.com/tutur3u/platform/commit/0359132f96947aaeeb02bb776af3d2a24794e0b2))
34
+ * **calendar:** show sync recovery with sidebar navigation ([033e04b](https://github.com/tutur3u/platform/commit/033e04bb44d8b1ca466ad0638e9525ca96c84202))
35
+ * **colab:** complete shared profile and footer controls ([4cc8959](https://github.com/tutur3u/platform/commit/4cc895951d9181f86972362ca864e4aa2bba9ed2))
36
+ * **colab:** preserve Next.js account menu parity ([1910015](https://github.com/tutur3u/platform/commit/19100151cb43220277d0970047fc8903d942348e))
37
+ * **meet:** make Calendar scheduling atomic ([aa3a4cc](https://github.com/tutur3u/platform/commit/aa3a4cc46ccdfccfa7648923bcd8207a7a91467f))
38
+ * **meet:** make Calendar scheduling atomic ([#5249](https://github.com/tutur3u/platform/issues/5249)) ([a2914ca](https://github.com/tutur3u/platform/commit/a2914cabde82b4f9a5549801807f73e9d2c728b0))
39
+ * **planning:** repair embedded APIs and simplify navigation ([5e82d77](https://github.com/tutur3u/platform/commit/5e82d77e183c5603d04cbe76bf3bba8bf3a94eca))
40
+ * **tasks:** preserve calendar drafts and recover failed task creation ([#5237](https://github.com/tutur3u/platform/issues/5237)) ([2b9acbf](https://github.com/tutur3u/platform/commit/2b9acbfcb19edff9680f4eb2c5c5b18a0e56ec8b))
41
+ * **tasks:** preserve task and calendar drafts until saves finish ([34b4902](https://github.com/tutur3u/platform/commit/34b4902b0382a3aa9e10249a0d83205c9a530878))
42
+ * **tasks:** recover partial saves and display actionable errors ([c4887c9](https://github.com/tutur3u/platform/commit/c4887c98377fd8493f3e6ae4219763d8872e2a5b))
43
+
3
44
  ## [0.30.2](https://github.com/tutur3u/platform/compare/ui-v0.30.1...ui-v0.30.2) (2026-08-31)
4
45
 
5
46
 
package/biome.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "root": false,
3
- "$schema": "https://biomejs.dev/schemas/2.5.11/schema.json",
3
+ "$schema": "https://biomejs.dev/schemas/2.5.12/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.30.2",
3
+ "version": "0.31.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,40 +60,40 @@
60
60
  "@tanstack/react-query": "^5.102.8",
61
61
  "@tanstack/react-table": "^9.2.3",
62
62
  "@tanstack/react-virtual": "^3.14.10",
63
- "@tiptap/core": "3.30.5",
64
- "@tiptap/extension-collaboration": "3.30.5",
65
- "@tiptap/extension-collaboration-caret": "3.30.5",
66
- "@tiptap/extension-color": "3.30.5",
67
- "@tiptap/extension-details": "3.30.5",
68
- "@tiptap/extension-drag-handle": "3.30.5",
69
- "@tiptap/extension-drag-handle-react": "3.30.5",
70
- "@tiptap/extension-highlight": "3.30.5",
71
- "@tiptap/extension-horizontal-rule": "3.30.5",
72
- "@tiptap/extension-image": "3.30.5",
73
- "@tiptap/extension-link": "3.30.5",
74
- "@tiptap/extension-list": "3.30.5",
75
- "@tiptap/extension-node-range": "3.30.5",
76
- "@tiptap/extension-placeholder": "3.30.5",
77
- "@tiptap/extension-strike": "3.30.5",
78
- "@tiptap/extension-subscript": "3.30.5",
79
- "@tiptap/extension-superscript": "3.30.5",
80
- "@tiptap/extension-table": "3.30.5",
81
- "@tiptap/extension-table-cell": "3.30.5",
82
- "@tiptap/extension-table-header": "3.30.5",
83
- "@tiptap/extension-table-row": "3.30.5",
84
- "@tiptap/extension-text-align": "3.30.5",
85
- "@tiptap/extension-text-style": "3.30.5",
86
- "@tiptap/extension-youtube": "3.30.5",
87
- "@tiptap/pm": "3.30.5",
88
- "@tiptap/react": "3.30.5",
89
- "@tiptap/starter-kit": "3.30.5",
90
- "@tuturuuu/ai": "0.8.3",
63
+ "@tiptap/core": "3.31.2",
64
+ "@tiptap/extension-collaboration": "3.31.2",
65
+ "@tiptap/extension-collaboration-caret": "3.31.2",
66
+ "@tiptap/extension-color": "3.31.2",
67
+ "@tiptap/extension-details": "3.31.2",
68
+ "@tiptap/extension-drag-handle": "3.31.2",
69
+ "@tiptap/extension-drag-handle-react": "3.31.2",
70
+ "@tiptap/extension-highlight": "3.31.2",
71
+ "@tiptap/extension-horizontal-rule": "3.31.2",
72
+ "@tiptap/extension-image": "3.31.2",
73
+ "@tiptap/extension-link": "3.31.2",
74
+ "@tiptap/extension-list": "3.31.2",
75
+ "@tiptap/extension-node-range": "3.31.2",
76
+ "@tiptap/extension-placeholder": "3.31.2",
77
+ "@tiptap/extension-strike": "3.31.2",
78
+ "@tiptap/extension-subscript": "3.31.2",
79
+ "@tiptap/extension-superscript": "3.31.2",
80
+ "@tiptap/extension-table": "3.31.2",
81
+ "@tiptap/extension-table-cell": "3.31.2",
82
+ "@tiptap/extension-table-header": "3.31.2",
83
+ "@tiptap/extension-table-row": "3.31.2",
84
+ "@tiptap/extension-text-align": "3.31.2",
85
+ "@tiptap/extension-text-style": "3.31.2",
86
+ "@tiptap/extension-youtube": "3.31.2",
87
+ "@tiptap/pm": "3.31.2",
88
+ "@tiptap/react": "3.31.2",
89
+ "@tiptap/starter-kit": "3.31.2",
90
+ "@tuturuuu/ai": "0.9.0",
91
91
  "@tuturuuu/apis": "0.11.3",
92
92
  "@tuturuuu/hooks": "0.1.1",
93
93
  "@tuturuuu/icons": "0.1.0",
94
- "@tuturuuu/internal-api": "0.33.0",
94
+ "@tuturuuu/internal-api": "0.34.0",
95
95
  "@tuturuuu/supabase": "0.5.1",
96
- "@tuturuuu/utils": "0.27.0",
96
+ "@tuturuuu/utils": "0.28.0",
97
97
  "@types/debug": "^4.1.13",
98
98
  "browser-image-compression": "^2.0.2",
99
99
  "class-variance-authority": "^0.7.1",
@@ -105,28 +105,28 @@
105
105
  "debug": "^4.4.3",
106
106
  "embla-carousel-react": "^8.6.0",
107
107
  "eventemitter3": "^5.0.4",
108
- "framer-motion": "^13.1.1",
108
+ "framer-motion": "^13.2.0",
109
109
  "gsap": "^3.15.0",
110
110
  "html-entities": "^2.6.0",
111
- "html2canvas-pro": "^2.4.0",
111
+ "html2canvas-pro": "^2.4.1",
112
112
  "input-otp": "^1.5.0",
113
- "katex": "^0.18.4",
113
+ "katex": "^0.18.5",
114
114
  "lodash": "4.18.1",
115
115
  "moment": "^2.30.1",
116
- "motion": "^13.1.1",
117
- "next": "^16.3.3",
118
- "next-intl": "^4.14.1",
116
+ "motion": "^13.2.0",
117
+ "next": "^16.3.4",
118
+ "next-intl": "^4.14.2",
119
119
  "next-themes": "^0.4.6",
120
120
  "nuqs": "^2.10.1",
121
121
  "prosemirror-state": "^1.4.4",
122
122
  "prosemirror-view": "1.42.3",
123
123
  "qrcode.react": "^4.2.0",
124
124
  "react": "^19.2.8",
125
- "react-colorful": "^5.8.0",
125
+ "react-colorful": "^5.8.1",
126
126
  "react-day-picker": "^10.0.1",
127
127
  "react-dom": "^19.2.8",
128
128
  "react-dropzone": "^20.1.1",
129
- "react-hook-form": "^7.86.0",
129
+ "react-hook-form": "^7.87.0",
130
130
  "react-markdown": "^10.1.0",
131
131
  "react-papaparse": "^4.4.0",
132
132
  "react-pdf": "^10.5.0",
@@ -145,34 +145,34 @@
145
145
  "vaul": "^1.1.2",
146
146
  "y-protocols": "^1.0.7",
147
147
  "yjs": "^13.6.32",
148
- "zod": "^4.4.3"
148
+ "zod": "^4.5.4"
149
149
  },
150
150
  "devDependencies": {
151
151
  "@tailwindcss/postcss": "^4.3.3",
152
152
  "@tailwindcss/typography": "^0.5.20",
153
153
  "@tanstack/react-query": "^5.102.8",
154
- "@tanstack/react-table": "^9.2.3",
154
+ "@tanstack/react-table": "^9.2.4",
155
155
  "@testing-library/jest-dom": "^7.0.1",
156
156
  "@testing-library/react": "^16.3.3",
157
- "@tuturuuu/types": "0.28.0",
157
+ "@tuturuuu/types": "0.29.0",
158
158
  "@tuturuuu/typescript-config": "0.1.2",
159
159
  "@types/html2canvas": "^1.0.0",
160
160
  "@types/lodash": "^4.17.25",
161
- "@types/node": "^26.4.0",
161
+ "@types/node": "^26.4.1",
162
162
  "@types/react": "^19.2.18",
163
- "@types/react-dom": "^19.2.5",
163
+ "@types/react-dom": "^19.2.7",
164
164
  "@types/react-resizable": "^4.0.0",
165
165
  "@types/react-syntax-highlighter": "^15.5.13",
166
166
  "@types/uuid": "^11.0.0",
167
167
  "@vitejs/plugin-react": "^6.1.1",
168
168
  "autoprefixer": "^10.5.4",
169
169
  "jsdom": "^30.0.1",
170
- "postcss": "^8.5.26",
170
+ "postcss": "^8.5.28",
171
171
  "postcss-load-config": "^6.0.1",
172
172
  "tailwindcss": "^4.3.3",
173
173
  "typescript": "7.0.2",
174
174
  "uuid": "^14.0.2",
175
- "vitest": "^4.1.11"
175
+ "vitest": "^5.0.0"
176
176
  },
177
177
  "peerDependencies": {
178
178
  "@tanstack/react-query": "^5.101.2",
@@ -335,7 +335,8 @@
335
335
  },
336
336
  "./custom/notification-popover-client": {
337
337
  "types": "./src/components/ui/custom/notification-popover-client.tsx",
338
- "import": "./src/components/ui/custom/notification-popover-client.tsx"
338
+ "import": "./src/components/ui/custom/notification-popover-client.tsx",
339
+ "default": "./src/components/ui/custom/notification-popover-client.tsx"
339
340
  },
340
341
  "./custom/sidebar-footer-actions": {
341
342
  "types": "./src/components/ui/custom/sidebar-footer-actions.tsx",
@@ -447,7 +448,52 @@
447
448
  "./text-editor/text-replacements": {
448
449
  "types": "./src/components/ui/text-editor/text-replacements.ts",
449
450
  "import": "./src/components/ui/text-editor/text-replacements.ts"
450
- }
451
+ },
452
+ "./custom/satellite-brand": {
453
+ "types": "./src/components/ui/custom/satellite-brand.tsx",
454
+ "import": "./src/components/ui/custom/satellite-brand.tsx"
455
+ },
456
+ "./custom/satellite-navigation": {
457
+ "types": "./src/components/ui/custom/satellite-navigation.tsx",
458
+ "import": "./src/components/ui/custom/satellite-navigation.tsx"
459
+ },
460
+ "./custom/satellite-content": {
461
+ "types": "./src/components/ui/custom/satellite-content.tsx",
462
+ "import": "./src/components/ui/custom/satellite-content.tsx"
463
+ },
464
+ "./custom/satellite-workspace-section": {
465
+ "types": "./src/components/ui/custom/satellite-workspace-section.tsx",
466
+ "import": "./src/components/ui/custom/satellite-workspace-section.tsx"
467
+ },
468
+ "./custom/satellite-shell-utils": {
469
+ "types": "./src/components/ui/custom/satellite-shell-utils.ts",
470
+ "import": "./src/components/ui/custom/satellite-shell-utils.ts"
471
+ },
472
+ "./custom/satellite-shell": {
473
+ "types": "./src/components/ui/custom/satellite-shell.tsx",
474
+ "import": "./src/components/ui/custom/satellite-shell.tsx"
475
+ },
476
+ "./custom/use-satellite-shell": {
477
+ "types": "./src/components/ui/custom/use-satellite-shell.tsx",
478
+ "import": "./src/components/ui/custom/use-satellite-shell.tsx"
479
+ },
480
+ "./custom/satellite-user-menu": {
481
+ "types": "./src/components/ui/custom/satellite-user-menu.tsx",
482
+ "default": "./src/components/ui/custom/satellite-user-menu.tsx"
483
+ },
484
+ "./custom/satellite-footer-actions": {
485
+ "types": "./src/components/ui/custom/satellite-footer-actions.tsx",
486
+ "default": "./src/components/ui/custom/satellite-footer-actions.tsx"
487
+ },
488
+ "./custom/satellite-user-menu-items": "./src/components/ui/custom/satellite-user-menu-items.tsx",
489
+ "./custom/satellite-account-switcher-menu": "./src/components/ui/custom/satellite-account-switcher-menu.tsx",
490
+ "./custom/satellite-theme-dropdown-items": "./src/components/ui/custom/satellite-theme-dropdown-items.tsx",
491
+ "./custom/satellite-language-item": "./src/components/ui/custom/satellite-language-item.tsx",
492
+ "./report-problem-dialog-content": "./src/components/ui/report-problem-dialog-content.tsx",
493
+ "./custom/satellite-settings-dialog-shell": "./src/components/ui/custom/satellite-settings-dialog-shell.tsx",
494
+ "./custom/satellite-onboarding-settings-panel": "./src/components/ui/custom/satellite-onboarding-settings-panel.tsx",
495
+ "./custom/satellite-notification-popover": "./src/components/ui/custom/satellite-notification-popover.tsx",
496
+ "./custom/notification-runtime": "./src/components/ui/custom/notification-runtime.tsx"
451
497
  },
452
- "packageManager": "bun@1.4.0"
498
+ "packageManager": "bun@1.4.1"
453
499
  }
@@ -0,0 +1,57 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import type { Workspace } from '@tuturuuu/types';
3
+ import type { ReactNode } from 'react';
4
+ import { describe, expect, it, vi } from 'vitest';
5
+
6
+ vi.mock('next-intl', () => ({
7
+ useLocale: () => 'en',
8
+ useTranslations: () => (key: string) => key,
9
+ }));
10
+ vi.mock('./hooks', () => ({
11
+ useCalendarSettings: () => ({
12
+ initialSettings: {},
13
+ needsCalendarGate: false,
14
+ }),
15
+ }));
16
+ vi.mock('./components/require-workspace-timezone-dialog', () => ({
17
+ RequireWorkspaceTimezoneDialog: () => null,
18
+ }));
19
+ vi.mock('@tuturuuu/ui/legacy/calendar/smart-calendar', () => ({
20
+ SmartCalendar: ({ extras }: { extras: ReactNode }) => <div>{extras}</div>,
21
+ }));
22
+ vi.mock('./components/calendar-connections-unified', () => ({
23
+ default: ({ variant }: { variant: string }) => (
24
+ <div data-testid="sync-entry">{variant}</div>
25
+ ),
26
+ }));
27
+
28
+ import { CalendarClientPage } from './calendar-client-page';
29
+
30
+ const workspace = { id: 'workspace' } as Workspace;
31
+ const HeaderActions = () => <span>Calendar actions</span>;
32
+
33
+ describe('calendar sync warning placement', () => {
34
+ it('keeps a header recovery entry when Calendar uses its sidebar for calendar selection', () => {
35
+ render(
36
+ <CalendarClientPage
37
+ workspace={workspace}
38
+ HeaderActions={HeaderActions}
39
+ enableSmartScheduling={false}
40
+ showConnectionsManager={false}
41
+ />
42
+ );
43
+ expect(screen.getByTestId('sync-entry').textContent).toBe('status');
44
+ expect(screen.getByText('Calendar actions')).toBeTruthy();
45
+ });
46
+ it('lets the normal calendar picker own recovery in the embedded Tasks calendar', () => {
47
+ render(
48
+ <CalendarClientPage
49
+ workspace={workspace}
50
+ HeaderActions={HeaderActions}
51
+ enableSmartScheduling={false}
52
+ showConnectionsManager
53
+ />
54
+ );
55
+ expect(screen.queryByTestId('sync-entry')).toBeNull();
56
+ });
57
+ });
@@ -14,6 +14,7 @@ import {
14
14
  type SetStateAction,
15
15
  useState,
16
16
  } from 'react';
17
+ import CalendarConnectionsUnified from './components/calendar-connections-unified';
17
18
  import { RequireWorkspaceTimezoneDialog } from './components/require-workspace-timezone-dialog';
18
19
  import { useCalendarSettings } from './hooks';
19
20
 
@@ -28,6 +29,7 @@ export type CalendarHeaderActionsComponent =
28
29
  interface CalendarClientPageProps {
29
30
  HeaderActions: CalendarHeaderActionsComponent;
30
31
  experimentalGoogleToken?: WorkspaceCalendarGoogleTokenClient | null;
32
+ initialEventId?: string;
31
33
  workspace: Workspace;
32
34
  enableSmartScheduling: boolean;
33
35
  externalState?: {
@@ -42,6 +44,7 @@ interface CalendarClientPageProps {
42
44
 
43
45
  export function CalendarClientPage({
44
46
  experimentalGoogleToken,
47
+ initialEventId,
45
48
  workspace,
46
49
  enableSmartScheduling,
47
50
  externalState,
@@ -59,10 +62,15 @@ export function CalendarClientPage({
59
62
  const needsCalendarGate = !calendarGateCompleted && settingsNeedGate;
60
63
 
61
64
  const extras = (
62
- <HeaderActions
63
- workspaceId={workspace.id}
64
- enableSmartScheduling={enableSmartScheduling}
65
- />
65
+ <>
66
+ {showConnectionsManager === false && (
67
+ <CalendarConnectionsUnified wsId={workspace.id} variant="status" />
68
+ )}
69
+ <HeaderActions
70
+ workspaceId={workspace.id}
71
+ enableSmartScheduling={enableSmartScheduling}
72
+ />
73
+ </>
66
74
  );
67
75
 
68
76
  return (
@@ -84,6 +92,7 @@ export function CalendarClientPage({
84
92
  ? experimentalGoogleToken
85
93
  : null
86
94
  }
95
+ initialEventId={initialEventId}
87
96
  extras={extras}
88
97
  externalState={externalState}
89
98
  initialSettings={initialSettings}
@@ -4,6 +4,7 @@ import type {
4
4
  WorkspaceCalendarGoogleTokenClient,
5
5
  } from '@tuturuuu/types';
6
6
  import { CalendarSyncProvider } from '@tuturuuu/ui/hooks/use-calendar-sync';
7
+ import { cn } from '@tuturuuu/utils/format';
7
8
  import type { ComponentType } from 'react';
8
9
  import type { CalendarView } from '../../../hooks/use-view-transition';
9
10
  import type { ExtendedWorkspaceTask } from '../time-tracker/types';
@@ -26,6 +27,7 @@ interface CalendarPageShellProps {
26
27
  calendarConnections: CalendarConnection[] | null;
27
28
  enableSmartScheduling: boolean;
28
29
  experimentalGoogleToken?: WorkspaceCalendarGoogleTokenClient | null;
30
+ initialEventId?: string;
29
31
  HeaderActions: CalendarHeaderActionsComponent;
30
32
  locale: string;
31
33
  smartSchedulingTasks?: ExtendedWorkspaceTask[];
@@ -41,12 +43,14 @@ interface CalendarPageShellProps {
41
43
  };
42
44
  manageCalendarSyncProvider?: boolean;
43
45
  showConnectionsManager?: boolean;
46
+ className?: string;
44
47
  }
45
48
 
46
49
  export function CalendarPageShell({
47
50
  calendarConnections,
48
51
  enableSmartScheduling,
49
52
  experimentalGoogleToken,
53
+ initialEventId,
50
54
  HeaderActions,
51
55
  locale,
52
56
  smartSchedulingTasks = [],
@@ -56,11 +60,13 @@ export function CalendarPageShell({
56
60
  externalState,
57
61
  manageCalendarSyncProvider = true,
58
62
  showConnectionsManager = true,
63
+ className,
59
64
  }: CalendarPageShellProps) {
60
65
  const content = (
61
- <div className="flex h-[calc(100vh-2rem)]">
66
+ <div className={cn('flex h-[calc(100dvh-2rem)] min-w-0', className)}>
62
67
  <CalendarClientPage
63
68
  experimentalGoogleToken={experimentalGoogleToken}
69
+ initialEventId={initialEventId}
64
70
  workspace={workspace}
65
71
  enableSmartScheduling={enableSmartScheduling}
66
72
  externalState={externalState}
@@ -1,7 +1,7 @@
1
1
  import {
2
+ AlertTriangle,
2
3
  Calendar,
3
4
  ChevronDown,
4
- ExternalLink,
5
5
  Eye,
6
6
  EyeOff,
7
7
  Loader2,
@@ -10,6 +10,7 @@ import {
10
10
  Settings,
11
11
  } from '@tuturuuu/icons';
12
12
  import Image from 'next/image';
13
+ import { useState } from 'react';
13
14
  import { Badge } from '../../badge';
14
15
  import { Button } from '../../button';
15
16
  import {
@@ -28,6 +29,10 @@ import {
28
29
  import { Popover, PopoverContent, PopoverTrigger } from '../../popover';
29
30
  import { Separator } from '../../separator';
30
31
  import { CalendarConnectionsSettingsContent } from './calendar-connections-settings-content';
32
+ import {
33
+ CalendarSyncRecovery,
34
+ needsCalendarSyncAttention,
35
+ } from './calendar-sync-recovery';
31
36
  import type { CalendarConnectionsManagerState } from './use-calendar-connections-manager';
32
37
 
33
38
  export function CalendarConnectionsCompact({
@@ -35,6 +40,8 @@ export function CalendarConnectionsCompact({
35
40
  }: {
36
41
  state: CalendarConnectionsManagerState;
37
42
  }) {
43
+ const [open, setOpen] = useState(false);
44
+ const needsAttention = needsCalendarSyncAttention(state);
38
45
  const {
39
46
  accounts,
40
47
  calendarConnections,
@@ -52,7 +59,6 @@ export function CalendarConnectionsCompact({
52
59
  syncHealth,
53
60
  syncMutation,
54
61
  syncStatusStyles,
55
- syncToTuturuuu,
56
62
  t,
57
63
  togglingIds,
58
64
  togglingTuturuuuIds,
@@ -62,9 +68,9 @@ export function CalendarConnectionsCompact({
62
68
  } = state;
63
69
 
64
70
  return (
65
- <div className="flex items-center gap-2">
71
+ <div className="flex flex-wrap items-center gap-2">
66
72
  {/* Quick calendar visibility toggle */}
67
- <Popover>
73
+ <Popover open={open} onOpenChange={setOpen}>
68
74
  <PopoverTrigger asChild>
69
75
  <Button variant="outline" size="sm" className="gap-2">
70
76
  <Calendar className="h-4 w-4" />
@@ -353,6 +359,7 @@ export function CalendarConnectionsCompact({
353
359
  )}
354
360
  </div>
355
361
 
362
+ <CalendarSyncRecovery state={state} />
356
363
  <Separator />
357
364
 
358
365
  {/* Sync actions */}
@@ -365,50 +372,51 @@ export function CalendarConnectionsCompact({
365
372
  )}
366
373
  {syncHealth?.state === 'degraded'
367
374
  ? t('degraded') || 'Degraded'
368
- : syncHealth?.state === 'healthy'
369
- ? t('healthy') || 'Healthy'
370
- : syncHealth?.state === 'disconnected'
371
- ? t('connect_accounts') || 'Connect accounts'
372
- : t('syncing_calendars') || 'Syncing calendars'}
373
- </div>
374
- <div className="flex gap-1">
375
- <Button
376
- variant="ghost"
377
- size="icon"
378
- className="h-7 w-7"
379
- onClick={() => syncMutation.mutate()}
380
- disabled={manualSyncDisabled}
381
- title={t('sync_now') || 'Sync now'}
382
- >
383
- <ExternalLink
384
- className={`h-3.5 w-3.5 ${
385
- manualSyncDisabled ? 'animate-pulse' : ''
386
- }`}
387
- />
388
- </Button>
389
- <Button
390
- variant="ghost"
391
- size="icon"
392
- className="h-7 w-7"
393
- onClick={() => syncToTuturuuu()}
394
- disabled={manualSyncDisabled}
395
- title={t('sync_from_google')}
396
- >
397
- <RefreshCw
398
- className={`h-3.5 w-3.5 ${manualSyncDisabled ? 'animate-spin' : ''}`}
399
- />
400
- </Button>
375
+ : syncHealth?.state === 'paused'
376
+ ? t('sync_recovery.paused')
377
+ : syncHealth?.state === 'healthy'
378
+ ? t('healthy') || 'Healthy'
379
+ : syncHealth?.state === 'disconnected'
380
+ ? t('connect_accounts') || 'Connect accounts'
381
+ : syncHealth?.state === 'syncing'
382
+ ? t('syncing_calendars')
383
+ : t('sync_recovery.unavailable')}
401
384
  </div>
385
+ <Button
386
+ variant="outline"
387
+ size="sm"
388
+ onClick={() => syncMutation.mutate()}
389
+ disabled={manualSyncDisabled}
390
+ >
391
+ <RefreshCw
392
+ className={
393
+ syncMutation.isPending ? 'size-4 animate-spin' : 'size-4'
394
+ }
395
+ />
396
+ {t('sync_now')}
397
+ </Button>
402
398
  </div>
403
399
  {syncHealth?.lastSuccessAt && (
404
400
  <p className="text-muted-foreground text-xs">
405
- {t('last_synced_at') || 'Last synced'}:{' '}
406
- {new Date(syncHealth.lastSuccessAt).toLocaleString()}
401
+ {t('last_synced_at', {
402
+ time: new Date(syncHealth.lastSuccessAt).toLocaleString(),
403
+ })}
407
404
  </p>
408
405
  )}
409
406
  </div>
410
407
  </PopoverContent>
411
408
  </Popover>
409
+ {needsAttention && (
410
+ <Button
411
+ variant="outline"
412
+ size="sm"
413
+ className="gap-1.5 border-dynamic-orange/40 text-dynamic-orange"
414
+ onClick={() => setOpen(true)}
415
+ >
416
+ <AlertTriangle className="size-4 shrink-0" />
417
+ <span>{t('sync_recovery.attention')}</span>
418
+ </Button>
419
+ )}
412
420
  </div>
413
421
  );
414
422
  }
@@ -47,6 +47,7 @@ import {
47
47
  import { Separator } from '../../separator';
48
48
  import { Switch } from '../../switch';
49
49
  import { Tabs, TabsContent, TabsList, TabsTrigger } from '../../tabs';
50
+ import { CalendarSyncRecovery } from './calendar-sync-recovery';
50
51
  import { CalendarSyncSettingsPanel } from './calendar-sync-settings-panel';
51
52
  import type { CalendarConnectionsManagerState } from './use-calendar-connections-manager';
52
53
 
@@ -104,6 +105,7 @@ export function CalendarConnectionsSettingsContent({
104
105
 
105
106
  return (
106
107
  <div className={className ? `space-y-4 ${className}` : 'space-y-4'}>
108
+ <CalendarSyncRecovery state={state} />
107
109
  <Tabs defaultValue="calendars" className="py-4">
108
110
  <TabsList className="grid w-full grid-cols-3">
109
111
  <TabsTrigger value="calendars">
@@ -401,6 +403,12 @@ export function CalendarConnectionsSettingsContent({
401
403
  {t('reconnect_required') || 'Reconnect'}
402
404
  </Badge>
403
405
  )}
406
+ {providerAccountStatuses[account.id]?.state ===
407
+ 'temporarily_unavailable' && (
408
+ <Badge variant="secondary" className="text-xs">
409
+ {t('sync_recovery.attention')}
410
+ </Badge>
411
+ )}
404
412
  <Badge variant="outline" className="text-xs capitalize">
405
413
  {account.provider}
406
414
  </Badge>
@@ -434,6 +442,12 @@ export function CalendarConnectionsSettingsContent({
434
442
  </Button>
435
443
  </div>
436
444
  )}
445
+ {providerAccountStatuses[account.id]?.state ===
446
+ 'temporarily_unavailable' && (
447
+ <p className="rounded-md border p-2 text-muted-foreground text-xs">
448
+ {t('sync_recovery.provider_unavailable')}
449
+ </p>
450
+ )}
437
451
  {(calendarsByAccount[account.id] || []).length > 0 ? (
438
452
  (calendarsByAccount[account.id] || []).map((cal) => {
439
453
  const syncControlsDisabled =
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { CalendarConnectionsCompact } from './calendar-connections-compact';
4
4
  import { CalendarConnectionsSettingsContent } from './calendar-connections-settings-content';
5
+ import { CalendarSyncAttentionButton } from './calendar-sync-attention-button';
5
6
  import {
6
7
  type CalendarConnectionsUnifiedVariant,
7
8
  useCalendarConnectionsManager,
@@ -21,6 +22,9 @@ export default function CalendarConnectionsUnified({
21
22
  }) {
22
23
  const state = useCalendarConnectionsManager(wsId);
23
24
 
25
+ if (variant === 'status')
26
+ return <CalendarSyncAttentionButton state={state} />;
27
+
24
28
  if (variant === 'settings') {
25
29
  return (
26
30
  <CalendarConnectionsSettingsContent state={state} className={className} />