@tuturuuu/ui 0.34.0 → 0.35.1
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 +36 -0
- package/package.json +5 -5
- package/src/components/ui/custom/nav-link.test.tsx +17 -2
- package/src/components/ui/custom/nav-link.tsx +1 -0
- package/src/components/ui/finance/invoices/hooks.ts +1 -1
- package/src/components/ui/finance/invoices/subscription-billing-groups.test.ts +46 -0
- package/src/components/ui/finance/invoices/subscription-billing-groups.ts +45 -0
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +22 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.35.1](https://github.com/tutur3u/platform/compare/ui-v0.35.0...ui-v0.35.1) (2026-09-26)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **education:** label satellite navigation in Learn and Teach ([7728149](https://github.com/tutur3u/platform/commit/77281496b86def6f02afaaabea0fb39a89557717)) ([#5536](https://github.com/tutur3u/platform/issues/5536)) ([d76aa49](https://github.com/tutur3u/platform/commit/d76aa49d93303156d900c872ada803c43e1c8530))
|
|
9
|
+
* **finance:** count recurring sessions in subscription previews ([94119f5](https://github.com/tutur3u/platform/commit/94119f5126e2c48d7e096a572c5957610ce67724)) ([#5541](https://github.com/tutur3u/platform/issues/5541)) ([e645a51](https://github.com/tutur3u/platform/commit/e645a51b0c9006cc1baf66e9846c5656b2063220))
|
|
10
|
+
|
|
11
|
+
## [0.35.0](https://github.com/tutur3u/platform/compare/ui-v0.34.0...ui-v0.35.0) (2026-09-25)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* **auth:** enforce required account MFA lifecycle ([e08f27d](https://github.com/tutur3u/platform/commit/e08f27dbc082f7d9c951bb0792357638f3dcfb0a)) ([#5448](https://github.com/tutur3u/platform/issues/5448)) ([d7a5ed4](https://github.com/tutur3u/platform/commit/d7a5ed453963e619bd1dc3bd8c8a18c5e027317c))
|
|
17
|
+
* **finance:** add granular invoice audit trail and recovery review ([#5376](https://github.com/tutur3u/platform/issues/5376)) ([b8df666](https://github.com/tutur3u/platform/commit/b8df666804848d96d35ba693d126fe611a3ebf2b))
|
|
18
|
+
* **finance:** add invoice history and recoverable deletion ([3d6dea5](https://github.com/tutur3u/platform/commit/3d6dea5bdbb055073b9b5d5680e391d094874ad2))
|
|
19
|
+
* **finance:** clarify category charts and add promotions management ([88f85ea](https://github.com/tutur3u/platform/commit/88f85eaadb722b42b99537be3c75fabb1715bf09)) ([#5384](https://github.com/tutur3u/platform/issues/5384)) ([b6fd666](https://github.com/tutur3u/platform/commit/b6fd666bccca6aa929166210fb1c6abdb91c1448))
|
|
20
|
+
* **finance:** expand invoice audit review and filters ([c346cac](https://github.com/tutur3u/platform/commit/c346cac5a2dfcdcd944d1fcdfa72f40ec27aa720))
|
|
21
|
+
* **finance:** track tuition coverage and harden invoice exports ([72e07be](https://github.com/tutur3u/platform/commit/72e07bebef58dfee398bcd0d896304d13a2d9aad)) ([#5447](https://github.com/tutur3u/platform/issues/5447)) ([6e822d8](https://github.com/tutur3u/platform/commit/6e822d89d079d9ea8b2677f208c16364cd70d586))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **calendar:** treat working locations as non-reminder events ([454e666](https://github.com/tutur3u/platform/commit/454e666a6465e3debd06260aedc1de7b96ab5d67)) ([#5511](https://github.com/tutur3u/platform/issues/5511)) ([6508ad6](https://github.com/tutur3u/platform/commit/6508ad681aaa174b186022fcbd526e20be682973))
|
|
27
|
+
* **finance:** expose audit amount visibility control ([d3ef238](https://github.com/tutur3u/platform/commit/d3ef2389b1fd189f5b7cd327d1e0b65d03b7b136))
|
|
28
|
+
* **finance:** preserve discount display across invoice views ([11eda37](https://github.com/tutur3u/platform/commit/11eda372d53c60041c676e1e8f9ca0ad06809b13))
|
|
29
|
+
* **finance:** preserve discounts in checkout and saved invoice views ([#5378](https://github.com/tutur3u/platform/issues/5378)) ([2297145](https://github.com/tutur3u/platform/commit/2297145c59d6dbec174e5e22bf69f2fd2b54695b))
|
|
30
|
+
* **finance:** retain actors across invoice audit writes ([75e1139](https://github.com/tutur3u/platform/commit/75e11390aa30493ee1f83f95660cfaf7547e13c0)) ([#5383](https://github.com/tutur3u/platform/issues/5383)) ([a1fbb54](https://github.com/tutur3u/platform/commit/a1fbb54532f652116064ab7d7b8d11b217b2b9d4))
|
|
31
|
+
* **finance:** speed up invoice audit history and improve pagination ([b6c165e](https://github.com/tutur3u/platform/commit/b6c165e88e8888facec7744ee16c1d9378bb90e5)) ([#5380](https://github.com/tutur3u/platform/issues/5380)) ([c829653](https://github.com/tutur3u/platform/commit/c8296532b5282324b28c09d3c06a64894694b46c))
|
|
32
|
+
* **notifications:** replace denied realtime filters with API refresh ([35293ac](https://github.com/tutur3u/platform/commit/35293ac06517c63689780351facec22a93c8da3b))
|
|
33
|
+
* **notifications:** stop invalid user_id realtime filters ([#5377](https://github.com/tutur3u/platform/issues/5377)) ([1c4ab1f](https://github.com/tutur3u/platform/commit/1c4ab1fe9830cfd18fbe98c10b0fbe1d6de168a3))
|
|
34
|
+
* **tasks:** keep synced checklists and board state interactive ([63761b5](https://github.com/tutur3u/platform/commit/63761b59aa8abe99c0e48f1694a1fa0573bdcffa)) ([#5379](https://github.com/tutur3u/platform/issues/5379)) ([c647460](https://github.com/tutur3u/platform/commit/c64746019a2e18798e71f0c5cfbf8bff13a58485))
|
|
35
|
+
* **tasks:** restore checklist controls and board drag UX ([72d2495](https://github.com/tutur3u/platform/commit/72d2495717f7550f2ca4c26234abd2ceebbc280c)) ([#5375](https://github.com/tutur3u/platform/issues/5375)) ([fd9ed4e](https://github.com/tutur3u/platform/commit/fd9ed4ef63272cc2a434ad72aa1081fa641055c2))
|
|
36
|
+
* **ui:** ensure report pagination always consumes text ([2f91cb9](https://github.com/tutur3u/platform/commit/2f91cb9e03cf9fd9432daeb76244e33805bb3da6))
|
|
37
|
+
* **ui:** prevent report pagination from freezing editors ([#5392](https://github.com/tutur3u/platform/issues/5392)) ([efe6e95](https://github.com/tutur3u/platform/commit/efe6e95205134df4fc5b10b9e48b4dafb7dce52b))
|
|
38
|
+
|
|
3
39
|
## [0.34.0](https://github.com/tutur3u/platform/compare/ui-v0.33.0...ui-v0.34.0) (2026-09-25)
|
|
4
40
|
|
|
5
41
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuturuuu/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -87,13 +87,13 @@
|
|
|
87
87
|
"@tiptap/pm": "3.31.3",
|
|
88
88
|
"@tiptap/react": "3.31.3",
|
|
89
89
|
"@tiptap/starter-kit": "3.31.3",
|
|
90
|
-
"@tuturuuu/ai": "0.
|
|
90
|
+
"@tuturuuu/ai": "0.16.1",
|
|
91
91
|
"@tuturuuu/apis": "0.11.4",
|
|
92
92
|
"@tuturuuu/hooks": "0.1.2",
|
|
93
93
|
"@tuturuuu/icons": "0.1.1",
|
|
94
|
-
"@tuturuuu/internal-api": "0.
|
|
94
|
+
"@tuturuuu/internal-api": "0.43.0",
|
|
95
95
|
"@tuturuuu/supabase": "0.5.2",
|
|
96
|
-
"@tuturuuu/utils": "0.
|
|
96
|
+
"@tuturuuu/utils": "0.31.0",
|
|
97
97
|
"@types/debug": "^4.1.13",
|
|
98
98
|
"browser-image-compression": "^2.0.2",
|
|
99
99
|
"class-variance-authority": "^0.7.1",
|
|
@@ -154,7 +154,7 @@
|
|
|
154
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.
|
|
157
|
+
"@tuturuuu/types": "0.33.0",
|
|
158
158
|
"@tuturuuu/typescript-config": "0.1.2",
|
|
159
159
|
"@types/html2canvas": "^1.0.0",
|
|
160
160
|
"@types/lodash": "^4.17.25",
|
|
@@ -26,7 +26,10 @@ vi.mock('@tuturuuu/internal-api', () => ({
|
|
|
26
26
|
function renderNavLink(
|
|
27
27
|
link: ComponentProps<typeof NavLink>['link'],
|
|
28
28
|
props: Partial<
|
|
29
|
-
Pick<
|
|
29
|
+
Pick<
|
|
30
|
+
ComponentProps<typeof NavLink>,
|
|
31
|
+
'isCollapsed' | 'onClick' | 'onSubMenuClick'
|
|
32
|
+
>
|
|
30
33
|
> = {}
|
|
31
34
|
) {
|
|
32
35
|
const queryClient = new QueryClient({
|
|
@@ -42,7 +45,7 @@ function renderNavLink(
|
|
|
42
45
|
<NavLink
|
|
43
46
|
wsId="personal"
|
|
44
47
|
link={link}
|
|
45
|
-
isCollapsed={false}
|
|
48
|
+
isCollapsed={props.isCollapsed ?? false}
|
|
46
49
|
onClick={props.onClick ?? vi.fn()}
|
|
47
50
|
onSubMenuClick={props.onSubMenuClick ?? vi.fn()}
|
|
48
51
|
/>
|
|
@@ -75,6 +78,18 @@ describe('NavLink', () => {
|
|
|
75
78
|
);
|
|
76
79
|
});
|
|
77
80
|
|
|
81
|
+
it('gives collapsed icon links a spoken navigation label', () => {
|
|
82
|
+
renderNavLink(
|
|
83
|
+
{ href: '/personal/tasks/boards', title: 'Boards' },
|
|
84
|
+
{ isCollapsed: true }
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
expect(screen.getByRole('link', { name: 'Boards' })).toHaveAttribute(
|
|
88
|
+
'aria-label',
|
|
89
|
+
'Boards'
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
78
93
|
it('does not prefetch external routes', () => {
|
|
79
94
|
renderNavLink({
|
|
80
95
|
external: true,
|
|
@@ -373,7 +373,6 @@ export const useInfiniteUserInvoices = (
|
|
|
373
373
|
});
|
|
374
374
|
};
|
|
375
375
|
|
|
376
|
-
// Subscription-specific hooks
|
|
377
376
|
export const useUserGroups = (wsId: string, userId: string) => {
|
|
378
377
|
return useQuery({
|
|
379
378
|
queryKey: ['user-groups', wsId, userId],
|
|
@@ -421,6 +420,7 @@ export const useSubscriptionInvoiceContext = (
|
|
|
421
420
|
valid_until?: string | null;
|
|
422
421
|
created_at?: string | null;
|
|
423
422
|
}>,
|
|
423
|
+
scheduledSessionsByGroupId: {} as Record<string, string[]>,
|
|
424
424
|
};
|
|
425
425
|
}
|
|
426
426
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mergeBillingSessionDates } from './subscription-billing-groups';
|
|
3
|
+
import { getBillableQuantityForGroupRange, type UserGroup } from './utils';
|
|
4
|
+
|
|
5
|
+
describe('subscription billing session dates', () => {
|
|
6
|
+
it('uses projected classes for scheduled billing when older materialized dates stop before the invoice month', () => {
|
|
7
|
+
const groupId = 'group-1';
|
|
8
|
+
const groups = [
|
|
9
|
+
{
|
|
10
|
+
workspace_user_groups: {
|
|
11
|
+
id: groupId,
|
|
12
|
+
name: 'Class 7',
|
|
13
|
+
sessions: ['2026-05-30'],
|
|
14
|
+
} as NonNullable<UserGroup['workspace_user_groups']>,
|
|
15
|
+
},
|
|
16
|
+
];
|
|
17
|
+
const planned = [
|
|
18
|
+
'2026-09-05',
|
|
19
|
+
'2026-09-12',
|
|
20
|
+
'2026-09-13',
|
|
21
|
+
'2026-09-19',
|
|
22
|
+
'2026-09-20',
|
|
23
|
+
'2026-09-26',
|
|
24
|
+
'2026-09-27',
|
|
25
|
+
];
|
|
26
|
+
const billingGroups = mergeBillingSessionDates(groups, {
|
|
27
|
+
[groupId]: planned,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
expect(groups[0]?.workspace_user_groups?.sessions).toEqual(['2026-05-30']);
|
|
31
|
+
expect(
|
|
32
|
+
getBillableQuantityForGroupRange({
|
|
33
|
+
groupId,
|
|
34
|
+
now: new Date('2026-09-26T10:00:00.000Z'),
|
|
35
|
+
selectedMonth: '2026-09',
|
|
36
|
+
useAttendanceBased: false,
|
|
37
|
+
userAttendance: [
|
|
38
|
+
{ date: '2026-09-20', group_id: groupId, status: 'PRESENT' },
|
|
39
|
+
{ date: '2026-09-26', group_id: groupId, status: 'PRESENT' },
|
|
40
|
+
],
|
|
41
|
+
userGroups: billingGroups,
|
|
42
|
+
workspaceTimezone: 'Asia/Ho_Chi_Minh',
|
|
43
|
+
})
|
|
44
|
+
).toBe(7);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
import type { UserGroup } from './utils';
|
|
3
|
+
|
|
4
|
+
export function mergeBillingSessionDates(
|
|
5
|
+
userGroups: UserGroup[],
|
|
6
|
+
scheduledSessionsByGroupId?: Record<string, string[]>
|
|
7
|
+
): UserGroup[] {
|
|
8
|
+
return userGroups.map((membership) => {
|
|
9
|
+
const group = membership.workspace_user_groups;
|
|
10
|
+
if (!group) return membership;
|
|
11
|
+
const planned = scheduledSessionsByGroupId?.[group.id] ?? [];
|
|
12
|
+
if (planned.length === 0) return membership;
|
|
13
|
+
const existing = Array.isArray(group.sessions) ? group.sessions : [];
|
|
14
|
+
return {
|
|
15
|
+
...membership,
|
|
16
|
+
workspace_user_groups: {
|
|
17
|
+
...group,
|
|
18
|
+
sessions: Array.from(new Set([...existing, ...planned])),
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function getGroupsWithScheduleIds(userGroups: UserGroup[]): string[] {
|
|
25
|
+
return userGroups.flatMap(({ workspace_user_groups: group }) =>
|
|
26
|
+
group?.id && Array.isArray(group.sessions) && group.sessions.length > 0
|
|
27
|
+
? [group.id]
|
|
28
|
+
: []
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function useSubscriptionBillingGroups(
|
|
33
|
+
userGroups: UserGroup[],
|
|
34
|
+
scheduledSessionsByGroupId?: Record<string, string[]>
|
|
35
|
+
) {
|
|
36
|
+
const billingUserGroups = useMemo(
|
|
37
|
+
() => mergeBillingSessionDates(userGroups, scheduledSessionsByGroupId),
|
|
38
|
+
[scheduledSessionsByGroupId, userGroups]
|
|
39
|
+
);
|
|
40
|
+
const groupsWithScheduleIds = useMemo(
|
|
41
|
+
() => getGroupsWithScheduleIds(userGroups),
|
|
42
|
+
[userGroups]
|
|
43
|
+
);
|
|
44
|
+
return { billingUserGroups, groupsWithScheduleIds };
|
|
45
|
+
}
|
|
@@ -62,6 +62,7 @@ import { createSubscriptionInvoiceWithInternalApi } from './internal-api';
|
|
|
62
62
|
import { formatInvoiceRecalculationDescription } from './invoice-visibility-format';
|
|
63
63
|
import { ProductSelection } from './product-selection';
|
|
64
64
|
import { invalidateInvoiceMutationQueries } from './query-invalidation';
|
|
65
|
+
import { useSubscriptionBillingGroups } from './subscription-billing-groups';
|
|
65
66
|
import type { SubscriptionInvoiceProps } from './subscription-invoice-props';
|
|
66
67
|
import type { SelectedProductItem } from './types';
|
|
67
68
|
import {
|
|
@@ -193,7 +194,6 @@ export function SubscriptionInvoice({
|
|
|
193
194
|
enabled: shouldPreloadPaymentSettings,
|
|
194
195
|
});
|
|
195
196
|
|
|
196
|
-
// Blocked groups check
|
|
197
197
|
const { data: blockedGroupIds = [] } = useInvoiceBlockedGroups(wsId, {
|
|
198
198
|
enabled: hasSelectedGroups,
|
|
199
199
|
});
|
|
@@ -245,7 +245,6 @@ export function SubscriptionInvoice({
|
|
|
245
245
|
const [isCreating, setIsCreating] = useState(false);
|
|
246
246
|
const [createPromotionOpen, setCreatePromotionOpen] = useState(false);
|
|
247
247
|
|
|
248
|
-
// Track previous user ID to detect user changes (skip initial mount for reset)
|
|
249
248
|
const prevUserIdRef = useRef<string | null>(null);
|
|
250
249
|
|
|
251
250
|
const {
|
|
@@ -335,6 +334,11 @@ export function SubscriptionInvoice({
|
|
|
335
334
|
const userAttendance = subscriptionInvoiceContext?.attendance ?? [];
|
|
336
335
|
const latestSubscriptionInvoices =
|
|
337
336
|
subscriptionInvoiceContext?.latestInvoices ?? [];
|
|
337
|
+
const { billingUserGroups, groupsWithScheduleIds } =
|
|
338
|
+
useSubscriptionBillingGroups(
|
|
339
|
+
userGroups,
|
|
340
|
+
subscriptionInvoiceContext?.scheduledSessionsByGroupId
|
|
341
|
+
);
|
|
338
342
|
const userAttendanceError =
|
|
339
343
|
subscriptionInvoiceContextError instanceof Error
|
|
340
344
|
? subscriptionInvoiceContextError
|
|
@@ -404,7 +408,7 @@ export function SubscriptionInvoice({
|
|
|
404
408
|
const billableSessions = useMemo(
|
|
405
409
|
() =>
|
|
406
410
|
getBillableSessionsForGroupsInRange(
|
|
407
|
-
|
|
411
|
+
billingUserGroups,
|
|
408
412
|
selectedGroupIds,
|
|
409
413
|
effectiveSelectedMonth,
|
|
410
414
|
prepaidMonthCount,
|
|
@@ -415,7 +419,7 @@ export function SubscriptionInvoice({
|
|
|
415
419
|
latestSubscriptionInvoices,
|
|
416
420
|
prepaidMonthCount,
|
|
417
421
|
selectedGroupIds,
|
|
418
|
-
|
|
422
|
+
billingUserGroups,
|
|
419
423
|
]
|
|
420
424
|
);
|
|
421
425
|
|
|
@@ -438,12 +442,17 @@ export function SubscriptionInvoice({
|
|
|
438
442
|
const monthlySessions = useMemo(
|
|
439
443
|
() =>
|
|
440
444
|
getBillableSessionsForGroupsInRange(
|
|
441
|
-
|
|
445
|
+
billingUserGroups,
|
|
442
446
|
selectedGroupIds,
|
|
443
447
|
effectiveSelectedMonth,
|
|
444
448
|
prepaidMonthCount
|
|
445
449
|
),
|
|
446
|
-
[
|
|
450
|
+
[
|
|
451
|
+
billingUserGroups,
|
|
452
|
+
effectiveSelectedMonth,
|
|
453
|
+
prepaidMonthCount,
|
|
454
|
+
selectedGroupIds,
|
|
455
|
+
]
|
|
447
456
|
);
|
|
448
457
|
|
|
449
458
|
const {
|
|
@@ -537,7 +546,7 @@ export function SubscriptionInvoice({
|
|
|
537
546
|
prefillAmount: prefillQuantity,
|
|
538
547
|
groupProducts,
|
|
539
548
|
products,
|
|
540
|
-
userGroups,
|
|
549
|
+
userGroups: billingUserGroups,
|
|
541
550
|
useAttendanceBased,
|
|
542
551
|
userAttendance: billableAttendance,
|
|
543
552
|
latestSubscriptionInvoices,
|
|
@@ -550,7 +559,7 @@ export function SubscriptionInvoice({
|
|
|
550
559
|
enabled: true,
|
|
551
560
|
selectedGroupIds,
|
|
552
561
|
selectedMonth: effectiveSelectedMonth,
|
|
553
|
-
userGroups,
|
|
562
|
+
userGroups: billingUserGroups,
|
|
554
563
|
groupProducts,
|
|
555
564
|
subscriptionSelectedProducts,
|
|
556
565
|
userAttendance: billableAttendance,
|
|
@@ -608,8 +617,12 @@ export function SubscriptionInvoice({
|
|
|
608
617
|
resetRounding: resetRoundingSubscription,
|
|
609
618
|
} = useInvoiceRounding(totalBeforeRounding);
|
|
610
619
|
|
|
611
|
-
// Reset subscription state when user changes (including switching to a different user)
|
|
612
620
|
useEffect(() => {
|
|
621
|
+
// URL-prefilled customer, group and month are one initial selection.
|
|
622
|
+
if (prevUserIdRef.current === null) {
|
|
623
|
+
prevUserIdRef.current = selectedUserId;
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
613
626
|
const userChanged = prevUserIdRef.current !== selectedUserId;
|
|
614
627
|
prevUserIdRef.current = selectedUserId;
|
|
615
628
|
|
|
@@ -632,20 +645,6 @@ export function SubscriptionInvoice({
|
|
|
632
645
|
defaultCategoryId,
|
|
633
646
|
]);
|
|
634
647
|
|
|
635
|
-
const groupsWithScheduleIds = useMemo(() => {
|
|
636
|
-
const sessions = (g: (typeof userGroups)[0]) =>
|
|
637
|
-
g.workspace_user_groups?.sessions;
|
|
638
|
-
return userGroups
|
|
639
|
-
.filter(
|
|
640
|
-
(g) =>
|
|
641
|
-
g.workspace_user_groups?.id &&
|
|
642
|
-
Array.isArray(sessions(g)) &&
|
|
643
|
-
(sessions(g)?.length ?? 0) > 0
|
|
644
|
-
)
|
|
645
|
-
.map((g) => g.workspace_user_groups!.id);
|
|
646
|
-
}, [userGroups]);
|
|
647
|
-
|
|
648
|
-
// Auto-select all groups with schedule when userGroups are loaded
|
|
649
648
|
useEffect(() => {
|
|
650
649
|
if (
|
|
651
650
|
groupsWithScheduleIds.length > 0 &&
|