@tuturuuu/ui 0.32.0 → 0.33.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 (78) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/biome.json +1 -1
  3. package/package.json +53 -51
  4. package/src/components/ui/calendar-app/components/calendar-sync-attention-button.tsx +58 -24
  5. package/src/components/ui/calendar-app/components/calendar-sync-recovery.test.tsx +57 -0
  6. package/src/components/ui/calendar-app/components/calendar-sync-recovery.tsx +28 -14
  7. package/src/components/ui/calendar-app/components/refresh-calendar-sync-status.test.tsx +321 -0
  8. package/src/components/ui/calendar-app/components/refresh-calendar-sync-status.ts +182 -0
  9. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +12 -14
  10. package/src/components/ui/chat/ai-message-parts.test.tsx +47 -0
  11. package/src/components/ui/chat/ai-message-parts.tsx +23 -16
  12. package/src/components/ui/chat/ai-message-render-utils.ts +19 -1
  13. package/src/components/ui/commercial-comparison-style.ts +104 -0
  14. package/src/components/ui/commercial-comparison-table.tsx +159 -0
  15. package/src/components/ui/commercial-comparison-toolbar.tsx +205 -0
  16. package/src/components/ui/commercial-comparison.test.tsx +86 -0
  17. package/src/components/ui/commercial-comparison.tsx +224 -0
  18. package/src/components/ui/comparison-value.test.tsx +32 -0
  19. package/src/components/ui/comparison-value.tsx +72 -0
  20. package/src/components/ui/custom/__tests__/report-preview-pagination-progress.test.ts +129 -0
  21. package/src/components/ui/custom/__tests__/report-preview-viewport.test.tsx +56 -0
  22. package/src/components/ui/custom/education/modules/resources/pdf-viewer.tsx +13 -2
  23. package/src/components/ui/custom/report-preview-pagination.ts +5 -3
  24. package/src/components/ui/custom/report-preview-viewport.tsx +40 -0
  25. package/src/components/ui/custom/report-preview.tsx +2 -3
  26. package/src/components/ui/finance/categories-tags-tabs.tsx +3 -0
  27. package/src/components/ui/finance/invoices/columns.tsx +2 -0
  28. package/src/components/ui/finance/invoices/components/invoice-checkout-summary.tsx +6 -3
  29. package/src/components/ui/finance/invoices/components/invoice-data-state.test.tsx +27 -0
  30. package/src/components/ui/finance/invoices/components/invoice-data-state.tsx +34 -0
  31. package/src/components/ui/finance/invoices/hooks/use-invoice-customer-search.test.tsx +89 -0
  32. package/src/components/ui/finance/invoices/hooks/use-invoice-customer-search.ts +117 -0
  33. package/src/components/ui/finance/invoices/hooks.ts +5 -110
  34. package/src/components/ui/finance/invoices/internal-api.test.ts +122 -0
  35. package/src/components/ui/finance/invoices/internal-api.ts +19 -1
  36. package/src/components/ui/finance/invoices/invoice-discount.test.tsx +107 -0
  37. package/src/components/ui/finance/invoices/invoice-discount.ts +12 -0
  38. package/src/components/ui/finance/invoices/invoice-history-filters.tsx +121 -0
  39. package/src/components/ui/finance/invoices/invoice-history-pagination.tsx +81 -0
  40. package/src/components/ui/finance/invoices/invoice-history-row.tsx +159 -0
  41. package/src/components/ui/finance/invoices/invoice-history.test.tsx +170 -0
  42. package/src/components/ui/finance/invoices/invoice-history.tsx +300 -0
  43. package/src/components/ui/finance/invoices/invoice-page.tsx +18 -2
  44. package/src/components/ui/finance/invoices/invoiceId/full-invoice-template.tsx +3 -7
  45. package/src/components/ui/finance/invoices/invoiceId/invoice-details-page.test.tsx +16 -0
  46. package/src/components/ui/finance/invoices/invoiceId/invoice-details-page.tsx +9 -0
  47. package/src/components/ui/finance/invoices/invoices-table.tsx +3 -0
  48. package/src/components/ui/finance/invoices/promotion-form.tsx +5 -4
  49. package/src/components/ui/finance/invoices/query-invalidation.ts +1 -0
  50. package/src/components/ui/finance/invoices/row-actions.tsx +35 -4
  51. package/src/components/ui/finance/invoices/standard-invoice.tsx +27 -16
  52. package/src/components/ui/finance/invoices/subscription-invoice-props.ts +20 -0
  53. package/src/components/ui/finance/invoices/subscription-invoice.test.tsx +71 -4
  54. package/src/components/ui/finance/invoices/subscription-invoice.tsx +44 -45
  55. package/src/components/ui/finance/shared/charts/category-breakdown-chart-body.tsx +73 -80
  56. package/src/components/ui/finance/shared/charts/category-breakdown-chart-controls.tsx +3 -1
  57. package/src/components/ui/finance/shared/charts/category-breakdown-chart-legend.test.tsx +30 -0
  58. package/src/components/ui/finance/shared/charts/category-breakdown-chart-legend.tsx +31 -24
  59. package/src/components/ui/finance/shared/charts/category-breakdown-chart-tooltip.tsx +9 -6
  60. package/src/components/ui/finance/shared/charts/category-breakdown-chart-types.ts +2 -0
  61. package/src/components/ui/finance/shared/charts/category-breakdown-chart-utils.test.ts +59 -0
  62. package/src/components/ui/finance/shared/charts/category-breakdown-chart-utils.ts +51 -37
  63. package/src/components/ui/finance/shared/charts/category-breakdown-chart.tsx +37 -7
  64. package/src/components/ui/public-workspace-prices.tsx +13 -0
  65. package/src/components/ui/pwa-status.test.tsx +13 -2
  66. package/src/components/ui/pwa-status.tsx +15 -3
  67. package/src/components/ui/text-editor/editor-readonly-transition.test.tsx +86 -0
  68. package/src/components/ui/text-editor/editor.tsx +11 -3
  69. package/src/components/ui/text-editor/task-item-checkbox-view.test.tsx +188 -65
  70. package/src/components/ui/text-editor/task-item-checkbox-view.tsx +178 -164
  71. package/src/components/ui/text-editor/task-item-status-picker.tsx +130 -0
  72. package/src/hooks/__tests__/use-calendar-sync.test.tsx +20 -1
  73. package/src/hooks/__tests__/use-notifications-subscription.test.tsx +99 -180
  74. package/src/hooks/calendar-provider-sync.ts +33 -0
  75. package/src/hooks/notification-subscription.ts +74 -0
  76. package/src/hooks/use-calendar-sync.tsx +11 -14
  77. package/src/hooks/use-calendar.tsx +2 -2
  78. package/src/hooks/use-notifications.ts +5 -141
@@ -1,16 +1,14 @@
1
1
  import {
2
- type QueryClient,
3
2
  useInfiniteQuery,
4
3
  useMutation,
5
4
  useQuery,
6
5
  useQueryClient,
7
6
  } from '@tanstack/react-query';
8
- import {
9
- createRealtimeClient,
10
- type RealtimeChannel,
11
- type SupabaseClient,
12
- } from '@tuturuuu/supabase/next/realtime-browser';
13
- import { useEffect } from 'react';
7
+
8
+ export {
9
+ __resetNotificationSubscriptionRegistryForTests,
10
+ useNotificationSubscription,
11
+ } from './notification-subscription';
14
12
  export type NotificationType =
15
13
  | 'task_assigned'
16
14
  | 'task_updated'
@@ -78,98 +76,8 @@ interface UseNotificationsOptions {
78
76
  readOnly?: boolean;
79
77
  type?: NotificationType;
80
78
  }
81
- interface NotificationSubscriptionEntry {
82
- channel: RealtimeChannel;
83
- queryClientRefs: Map<QueryClient, number>;
84
- subscriberCount: number;
85
- supabase: SupabaseClient;
86
- }
87
79
  export const UNREAD_COUNT_STALE_TIME_MS = 5 * 60 * 1000;
88
80
  export const UNREAD_COUNT_FALLBACK_INTERVAL_MS = 15 * 60 * 1000;
89
- const notificationSubscriptionRegistry = new Map<
90
- string,
91
- NotificationSubscriptionEntry
92
- >();
93
- function invalidateNotificationQueries(
94
- queryClientRefs: Map<QueryClient, number>
95
- ) {
96
- for (const queryClient of queryClientRefs.keys()) {
97
- queryClient.invalidateQueries({ queryKey: ['notifications'] });
98
- queryClient.invalidateQueries({
99
- queryKey: ['notifications', 'unread-count'],
100
- });
101
- }
102
- }
103
- function addNotificationQueryClientRef(
104
- entry: NotificationSubscriptionEntry,
105
- queryClient: QueryClient
106
- ) {
107
- entry.subscriberCount += 1;
108
- entry.queryClientRefs.set(
109
- queryClient,
110
- (entry.queryClientRefs.get(queryClient) ?? 0) + 1
111
- );
112
- }
113
- function releaseNotificationQueryClientRef(
114
- entry: NotificationSubscriptionEntry,
115
- queryClient: QueryClient
116
- ) {
117
- entry.subscriberCount -= 1;
118
- const currentRefCount = entry.queryClientRefs.get(queryClient) ?? 0;
119
- if (currentRefCount <= 1) {
120
- entry.queryClientRefs.delete(queryClient);
121
- } else {
122
- entry.queryClientRefs.set(queryClient, currentRefCount - 1);
123
- }
124
- }
125
- function createNotificationSubscriptionEntry(
126
- userId: string,
127
- queryClient: QueryClient
128
- ) {
129
- const queryClientRefs = new Map<QueryClient, number>([[queryClient, 1]]);
130
- const supabase = createRealtimeClient();
131
- const invalidateQueries = () =>
132
- invalidateNotificationQueries(queryClientRefs);
133
- const channel = supabase
134
- .channel(`notifications-${userId}`)
135
- .on(
136
- 'postgres_changes',
137
- {
138
- event: 'INSERT',
139
- schema: 'public',
140
- table: 'notifications',
141
- filter: `user_id=eq.${userId}`,
142
- },
143
- invalidateQueries
144
- )
145
- .on(
146
- 'postgres_changes',
147
- {
148
- event: 'UPDATE',
149
- schema: 'public',
150
- table: 'notifications',
151
- filter: `user_id=eq.${userId}`,
152
- },
153
- invalidateQueries
154
- )
155
- .on(
156
- 'postgres_changes',
157
- {
158
- event: 'DELETE',
159
- schema: 'public',
160
- table: 'notifications',
161
- filter: `user_id=eq.${userId}`,
162
- },
163
- invalidateQueries
164
- )
165
- .subscribe();
166
- return {
167
- channel,
168
- queryClientRefs,
169
- subscriberCount: 1,
170
- supabase,
171
- } satisfies NotificationSubscriptionEntry;
172
- }
173
81
  export function dedupeNotifications(
174
82
  notifications: Notification[]
175
83
  ): Notification[] {
@@ -665,47 +573,3 @@ export function useDeleteNotification() {
665
573
  },
666
574
  });
667
575
  }
668
-
669
- /**
670
- * Hook to subscribe to realtime notification updates
671
- * Subscribes to all notifications for the user, regardless of workspace
672
- */
673
- export function useNotificationSubscription(
674
- _wsId: string | null,
675
- userId: string
676
- ) {
677
- const queryClient = useQueryClient();
678
-
679
- useEffect(() => {
680
- if (!userId) return;
681
-
682
- const registryKey = userId;
683
- let entry = notificationSubscriptionRegistry.get(registryKey);
684
-
685
- if (entry) {
686
- addNotificationQueryClientRef(entry, queryClient);
687
- } else {
688
- entry = createNotificationSubscriptionEntry(userId, queryClient);
689
- notificationSubscriptionRegistry.set(registryKey, entry);
690
- }
691
-
692
- return () => {
693
- if (!entry) return;
694
-
695
- releaseNotificationQueryClientRef(entry, queryClient);
696
-
697
- if (entry.subscriberCount <= 0) {
698
- notificationSubscriptionRegistry.delete(registryKey);
699
- entry.supabase.removeChannel(entry.channel);
700
- }
701
- };
702
- }, [userId, queryClient]);
703
- }
704
-
705
- export function __resetNotificationSubscriptionRegistryForTests() {
706
- for (const entry of notificationSubscriptionRegistry.values()) {
707
- entry.supabase.removeChannel(entry.channel);
708
- }
709
-
710
- notificationSubscriptionRegistry.clear();
711
- }