@tuturuuu/ui 0.14.0 → 0.17.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.
- package/CHANGELOG.md +96 -0
- package/biome.json +1 -1
- package/package.json +63 -62
- package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +12 -0
- package/src/components/ui/custom/__tests__/sidebar-context.test.tsx +93 -6
- package/src/components/ui/custom/loading-statistic-card.tsx +18 -0
- package/src/components/ui/custom/notification-popover-client.tsx +5 -1
- package/src/components/ui/custom/settings/keyboard-shortcuts-settings.test.tsx +34 -0
- package/src/components/ui/custom/settings/keyboard-shortcuts-settings.tsx +106 -0
- package/src/components/ui/custom/settings/sidebar-settings.tsx +18 -6
- package/src/components/ui/custom/settings-dialog-shell.tsx +18 -12
- package/src/components/ui/custom/sidebar-context.tsx +58 -3
- package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +5 -3
- package/src/components/ui/custom/statistic-card.tsx +104 -0
- package/src/components/ui/custom/structure.test.tsx +29 -0
- package/src/components/ui/custom/structure.tsx +103 -88
- package/src/components/ui/finance/invoices/civil-month.ts +224 -0
- package/src/components/ui/finance/invoices/components/subscription-invoice-proposal.test.tsx +49 -0
- package/src/components/ui/finance/invoices/components/subscription-invoice-proposal.tsx +81 -0
- package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +8 -2
- package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +90 -2
- package/src/components/ui/finance/invoices/new-invoice-page.tsx +40 -4
- package/src/components/ui/finance/invoices/pending-columns.test.ts +26 -0
- package/src/components/ui/finance/invoices/pending-columns.tsx +45 -14
- package/src/components/ui/finance/invoices/standard-invoice.test.ts +31 -0
- package/src/components/ui/finance/invoices/standard-invoice.tsx +33 -16
- package/src/components/ui/finance/invoices/subscription-invoice.test.tsx +331 -0
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +53 -28
- package/src/components/ui/finance/invoices/utils.test.ts +84 -0
- package/src/components/ui/finance/invoices/utils.ts +56 -134
- package/src/components/ui/resizable.test.ts +15 -0
- package/src/components/ui/resizable.tsx +28 -6
- package/src/components/ui/storefront/browse-panel.tsx +161 -0
- package/src/components/ui/storefront/bundle-selection-dialog.tsx +1 -1
- package/src/components/ui/storefront/cart-popover.tsx +1 -1
- package/src/components/ui/storefront/hero-panel.tsx +35 -30
- package/src/components/ui/storefront/listing-card.tsx +10 -5
- package/src/components/ui/storefront/merch-sections.tsx +7 -5
- package/src/components/ui/storefront/product-detail.tsx +79 -6
- package/src/components/ui/storefront/product-dialog.tsx +5 -1
- package/src/components/ui/storefront/storefront-surface.test.tsx +72 -0
- package/src/components/ui/storefront/storefront-surface.tsx +67 -63
- package/src/components/ui/storefront/types.ts +28 -0
- package/src/components/ui/storefront/utils.ts +4 -5
- package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +59 -1
- package/src/components/ui/tu-do/boards/boardId/board-column.tsx +37 -8
- package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-move.test.tsx +62 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +48 -0
- package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +32 -2
- package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +41 -3
- package/src/components/ui/tu-do/boards/boardId/menus/task-due-date-menu.tsx +31 -10
- package/src/components/ui/tu-do/boards/boardId/menus/task-priority-menu.tsx +7 -5
- package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +7 -5
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-archive-visibility.test.ts +44 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-archive-visibility.ts +17 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +2 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +7 -0
- package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +44 -30
- package/src/components/ui/tu-do/my-tasks/__tests__/use-task-context-actions.test.ts +54 -0
- package/src/components/ui/tu-do/my-tasks/task-list-with-completion.tsx +43 -1
- package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +33 -15
- package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +3 -3
- package/src/components/ui/tu-do/progress/task-progress-page.tsx +60 -26
- package/src/components/ui/tu-do/progress/task-progress-panels.tsx +12 -10
- package/src/components/ui/tu-do/projects/components/project-grid-card.tsx +22 -6
- package/src/components/ui/tu-do/projects/components/project-list-item.tsx +24 -8
- package/src/components/ui/tu-do/projects/projectId/components/__tests__/tasks-tab-layout.test.tsx +6 -0
- package/src/components/ui/tu-do/projects/projectId/components/tasks-tab.tsx +3 -1
- package/src/components/ui/tu-do/projects/projectId/task-project-detail-page-client.tsx +11 -3
- package/src/components/ui/tu-do/projects/projectId/task-project-detail.tsx +2 -0
- package/src/components/ui/tu-do/projects/projectId/types.ts +1 -0
- package/src/components/ui/tu-do/projects/task-projects-client.tsx +45 -1
- package/src/components/ui/tu-do/projects/types.ts +4 -1
- package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +10 -0
- package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +7 -1
- package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +87 -2
- package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +27 -3
- package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +41 -3
- package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +54 -0
- package/src/components/ui/tu-do/shared/board-header.tsx +20 -0
- package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +4 -4
- package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +2 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +7 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-mutations.test.tsx +231 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +139 -68
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.test.tsx +68 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +17 -0
- package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +45 -23
- package/src/components/ui/tu-do/shared/task-row-actions-menu.test.tsx +138 -0
- package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +15 -9
- package/src/components/ui/tu-do/templates/client.tsx +97 -78
- package/src/components/ui/tu-do/templates/marketplace/client.tsx +171 -135
- package/src/components/ui/tu-do/templates/task-templates-hub.tsx +3 -0
- package/src/components/ui/tu-do/templates/templateId/client.tsx +24 -9
- package/src/components/ui/tu-do/templates/templateId/task-template-detail-page-client.tsx +8 -2
- package/src/globals.css +1 -0
- package/src/hooks/__tests__/use-notifications-subscription.test.tsx +34 -0
- package/src/hooks/__tests__/use-task-actions.test.tsx +14 -39
- package/src/hooks/task-actions-personal-external.ts +12 -6
- package/src/hooks/use-notifications.ts +14 -2
- package/vitest.config.ts +4 -0
|
@@ -286,12 +286,9 @@ describe('useTaskActions', () => {
|
|
|
286
286
|
});
|
|
287
287
|
});
|
|
288
288
|
|
|
289
|
-
it('moves an external task to
|
|
289
|
+
it('moves an external task to done through terminal personal placement when checked', async () => {
|
|
290
290
|
const completedAt = '2026-05-07T03:00:00.000Z';
|
|
291
291
|
queryClient.setQueryData(['tasks', 'board-1'], [mockExternalTask]);
|
|
292
|
-
mockListWorkspaceTaskLists.mockResolvedValueOnce({
|
|
293
|
-
lists: mockSourceLists,
|
|
294
|
-
});
|
|
295
292
|
mockUpsertCurrentUserTaskPersonalPlacement.mockResolvedValueOnce({
|
|
296
293
|
task: {
|
|
297
294
|
...mockExternalTask,
|
|
@@ -299,14 +296,10 @@ describe('useTaskActions', () => {
|
|
|
299
296
|
personal_list_id: 'completion-list',
|
|
300
297
|
personal_sort_key: 1_500_000,
|
|
301
298
|
sort_key: 1_500_000,
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
mockUpdateWorkspaceTask.mockResolvedValueOnce({
|
|
305
|
-
task: {
|
|
306
|
-
...mockExternalTask,
|
|
307
|
-
list_id: 'source-done-list',
|
|
299
|
+
source_list_id: 'source-done-list',
|
|
300
|
+
source_list_status: 'done',
|
|
308
301
|
completed_at: completedAt,
|
|
309
|
-
closed_at:
|
|
302
|
+
closed_at: completedAt,
|
|
310
303
|
},
|
|
311
304
|
});
|
|
312
305
|
|
|
@@ -329,10 +322,7 @@ describe('useTaskActions', () => {
|
|
|
329
322
|
await result.current.handleArchiveToggle();
|
|
330
323
|
});
|
|
331
324
|
|
|
332
|
-
expect(mockListWorkspaceTaskLists).
|
|
333
|
-
'source-ws',
|
|
334
|
-
'source-board'
|
|
335
|
-
);
|
|
325
|
+
expect(mockListWorkspaceTaskLists).not.toHaveBeenCalled();
|
|
336
326
|
expect(mockUpsertCurrentUserTaskPersonalPlacement).toHaveBeenCalledWith(
|
|
337
327
|
'task-1',
|
|
338
328
|
expect.objectContaining({
|
|
@@ -340,15 +330,10 @@ describe('useTaskActions', () => {
|
|
|
340
330
|
personal_list_id: 'completion-list',
|
|
341
331
|
previous_task_id: null,
|
|
342
332
|
next_task_id: null,
|
|
333
|
+
terminal_status: 'done',
|
|
343
334
|
})
|
|
344
335
|
);
|
|
345
|
-
expect(mockUpdateWorkspaceTask).
|
|
346
|
-
'source-ws',
|
|
347
|
-
'task-1',
|
|
348
|
-
{
|
|
349
|
-
list_id: 'source-done-list',
|
|
350
|
-
}
|
|
351
|
-
);
|
|
336
|
+
expect(mockUpdateWorkspaceTask).not.toHaveBeenCalled();
|
|
352
337
|
expect(queryClient.getQueryData<Task[]>(['tasks', 'board-1'])).toEqual([
|
|
353
338
|
expect.objectContaining({
|
|
354
339
|
id: 'task-1',
|
|
@@ -356,6 +341,7 @@ describe('useTaskActions', () => {
|
|
|
356
341
|
personal_list_id: 'completion-list',
|
|
357
342
|
source_list_id: 'source-done-list',
|
|
358
343
|
completed_at: completedAt,
|
|
344
|
+
closed_at: completedAt,
|
|
359
345
|
_localMutationAt: expect.any(Number),
|
|
360
346
|
}),
|
|
361
347
|
]);
|
|
@@ -733,12 +719,9 @@ describe('useTaskActions', () => {
|
|
|
733
719
|
expect(setMenuOpen).toHaveBeenCalledWith(false);
|
|
734
720
|
});
|
|
735
721
|
|
|
736
|
-
it('moves an external task to
|
|
722
|
+
it('moves an external task to closed through terminal personal placement', async () => {
|
|
737
723
|
const closedAt = '2026-05-07T03:30:00.000Z';
|
|
738
724
|
queryClient.setQueryData(['tasks', 'board-1'], [mockExternalTask]);
|
|
739
|
-
mockListWorkspaceTaskLists.mockResolvedValueOnce({
|
|
740
|
-
lists: mockSourceLists,
|
|
741
|
-
});
|
|
742
725
|
mockUpsertCurrentUserTaskPersonalPlacement.mockResolvedValueOnce({
|
|
743
726
|
task: {
|
|
744
727
|
...mockExternalTask,
|
|
@@ -746,12 +729,8 @@ describe('useTaskActions', () => {
|
|
|
746
729
|
personal_list_id: 'closed-list',
|
|
747
730
|
personal_sort_key: 1_700_000,
|
|
748
731
|
sort_key: 1_700_000,
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
mockUpdateWorkspaceTask.mockResolvedValueOnce({
|
|
752
|
-
task: {
|
|
753
|
-
...mockExternalTask,
|
|
754
|
-
list_id: 'source-closed-list',
|
|
732
|
+
source_list_id: 'source-closed-list',
|
|
733
|
+
source_list_status: 'closed',
|
|
755
734
|
completed_at: null,
|
|
756
735
|
closed_at: closedAt,
|
|
757
736
|
},
|
|
@@ -781,15 +760,11 @@ describe('useTaskActions', () => {
|
|
|
781
760
|
expect.objectContaining({
|
|
782
761
|
personal_board_id: 'board-1',
|
|
783
762
|
personal_list_id: 'closed-list',
|
|
763
|
+
terminal_status: 'closed',
|
|
784
764
|
})
|
|
785
765
|
);
|
|
786
|
-
expect(
|
|
787
|
-
|
|
788
|
-
'task-1',
|
|
789
|
-
{
|
|
790
|
-
list_id: 'source-closed-list',
|
|
791
|
-
}
|
|
792
|
-
);
|
|
766
|
+
expect(mockListWorkspaceTaskLists).not.toHaveBeenCalled();
|
|
767
|
+
expect(mockUpdateWorkspaceTask).not.toHaveBeenCalled();
|
|
793
768
|
expect(queryClient.getQueryData<Task[]>(['tasks', 'board-1'])).toEqual([
|
|
794
769
|
expect.objectContaining({
|
|
795
770
|
id: 'task-1',
|
|
@@ -152,16 +152,21 @@ export async function moveExternalTaskToPersonalList({
|
|
|
152
152
|
const now = new Date().toISOString();
|
|
153
153
|
let sourceTargetList: TaskList | null = null;
|
|
154
154
|
const desiredSourceStatus = sourceStatus ?? targetList.status;
|
|
155
|
+
const terminalStatus =
|
|
156
|
+
targetList.status === 'done' || targetList.status === 'closed'
|
|
157
|
+
? targetList.status
|
|
158
|
+
: undefined;
|
|
155
159
|
const hasKnownSourceStatusChange = Boolean(
|
|
156
160
|
task.source_list_status && task.source_list_status !== desiredSourceStatus
|
|
157
161
|
);
|
|
158
162
|
const shouldInspectSourceList = Boolean(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
task.
|
|
163
|
-
|
|
164
|
-
|
|
163
|
+
!terminalStatus &&
|
|
164
|
+
(sourceStatus ||
|
|
165
|
+
hasKnownSourceStatusChange ||
|
|
166
|
+
(!task.source_list_status &&
|
|
167
|
+
task.source_list_id &&
|
|
168
|
+
sourceWorkspaceId &&
|
|
169
|
+
sourceBoardId))
|
|
165
170
|
);
|
|
166
171
|
|
|
167
172
|
try {
|
|
@@ -235,6 +240,7 @@ export async function moveExternalTaskToPersonalList({
|
|
|
235
240
|
personal_list_id: targetList.id,
|
|
236
241
|
previous_task_id: order.previous_task_id,
|
|
237
242
|
next_task_id: order.next_task_id,
|
|
243
|
+
terminal_status: terminalStatus,
|
|
238
244
|
}
|
|
239
245
|
);
|
|
240
246
|
|
|
@@ -90,6 +90,8 @@ interface NotificationSubscriptionEntry {
|
|
|
90
90
|
supabase: SupabaseClient;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
export const UNREAD_COUNT_FALLBACK_INTERVAL_MS = 5 * 60 * 1000;
|
|
94
|
+
|
|
93
95
|
const notificationSubscriptionRegistry = new Map<
|
|
94
96
|
string,
|
|
95
97
|
NotificationSubscriptionEntry
|
|
@@ -265,11 +267,13 @@ export function useInfiniteNotifications({
|
|
|
265
267
|
unreadOnly = false,
|
|
266
268
|
readOnly = false,
|
|
267
269
|
pageSize = 20,
|
|
270
|
+
enabled = true,
|
|
268
271
|
}: {
|
|
269
272
|
wsId?: string;
|
|
270
273
|
unreadOnly?: boolean;
|
|
271
274
|
readOnly?: boolean;
|
|
272
275
|
pageSize?: number;
|
|
276
|
+
enabled?: boolean;
|
|
273
277
|
}) {
|
|
274
278
|
return useInfiniteQuery({
|
|
275
279
|
queryKey: [
|
|
@@ -299,6 +303,7 @@ export function useInfiniteNotifications({
|
|
|
299
303
|
} satisfies NotificationsPage;
|
|
300
304
|
},
|
|
301
305
|
initialPageParam: 0,
|
|
306
|
+
enabled,
|
|
302
307
|
getNextPageParam: (lastPage) => {
|
|
303
308
|
const nextOffset = lastPage.offset + lastPage.limit;
|
|
304
309
|
return nextOffset < lastPage.count ? nextOffset : undefined;
|
|
@@ -312,7 +317,7 @@ export function useInfiniteNotifications({
|
|
|
312
317
|
* Hook to get unread notification count.
|
|
313
318
|
* If wsId is provided, scopes to that workspace. Otherwise returns total unread count.
|
|
314
319
|
*/
|
|
315
|
-
export function useUnreadCount(wsId?: string) {
|
|
320
|
+
export function useUnreadCount(wsId?: string, options?: { enabled?: boolean }) {
|
|
316
321
|
return useQuery({
|
|
317
322
|
queryKey: ['notifications', 'unread-count', wsId || 'all'],
|
|
318
323
|
queryFn: async () => {
|
|
@@ -328,7 +333,14 @@ export function useUnreadCount(wsId?: string) {
|
|
|
328
333
|
const data = await response.json();
|
|
329
334
|
return data.count as number;
|
|
330
335
|
},
|
|
331
|
-
|
|
336
|
+
enabled: options?.enabled ?? true,
|
|
337
|
+
staleTime: 60_000,
|
|
338
|
+
// Realtime invalidation is the primary update path. Keep a low-frequency
|
|
339
|
+
// refresh as a safety net for disconnected or suspended browser sessions.
|
|
340
|
+
refetchInterval: UNREAD_COUNT_FALLBACK_INTERVAL_MS,
|
|
341
|
+
refetchIntervalInBackground: false,
|
|
342
|
+
refetchOnReconnect: true,
|
|
343
|
+
refetchOnWindowFocus: true,
|
|
332
344
|
});
|
|
333
345
|
}
|
|
334
346
|
|
package/vitest.config.ts
CHANGED
|
@@ -29,6 +29,10 @@ export default defineConfig({
|
|
|
29
29
|
find: '@tuturuuu/internal-api',
|
|
30
30
|
replacement: resolve(__dirname, '../internal-api/src/index.ts'),
|
|
31
31
|
},
|
|
32
|
+
{
|
|
33
|
+
find: /^@tuturuuu\/types\/primitives\/(.+)$/,
|
|
34
|
+
replacement: `${resolve(__dirname, '../types/src/primitives')}/$1.ts`,
|
|
35
|
+
},
|
|
32
36
|
{
|
|
33
37
|
find: '@tuturuuu/supabase/next/client',
|
|
34
38
|
replacement: resolve(__dirname, '../supabase/src/next/client.ts'),
|