@carlonicora/nextjs-jsonapi 1.36.1 → 1.37.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/dist/{BlockNoteEditor-4MDHRUS2.js → BlockNoteEditor-74FHJO7E.js} +8 -8
- package/dist/{BlockNoteEditor-4MDHRUS2.js.map → BlockNoteEditor-74FHJO7E.js.map} +1 -1
- package/dist/{BlockNoteEditor-SZWO3MDO.mjs → BlockNoteEditor-GTWR6CPI.mjs} +4 -4
- package/dist/BlockNoteEditor-GTWR6CPI.mjs.map +1 -0
- package/dist/billing/index.d.mts +3 -3
- package/dist/billing/index.d.ts +3 -3
- package/dist/billing/index.js +365 -363
- package/dist/billing/index.js.map +1 -1
- package/dist/billing/index.mjs +80 -78
- package/dist/billing/index.mjs.map +1 -1
- package/dist/{chunk-E6PQQTWF.js → chunk-YVEK3SUS.js} +640 -1320
- package/dist/chunk-YVEK3SUS.js.map +1 -0
- package/dist/{chunk-I7DFEJFF.mjs → chunk-ZUUH4CQC.mjs} +737 -1417
- package/dist/chunk-ZUUH4CQC.mjs.map +1 -0
- package/dist/client/index.js +2 -2
- package/dist/client/index.mjs +1 -1
- package/dist/components/index.d.mts +23 -8
- package/dist/components/index.d.ts +23 -8
- package/dist/components/index.js +2 -2
- package/dist/components/index.mjs +1 -1
- package/dist/contexts/index.d.mts +1 -1
- package/dist/contexts/index.d.ts +1 -1
- package/dist/contexts/index.js +2 -2
- package/dist/contexts/index.mjs +1 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/client/context/JsonApiProvider.tsx +1 -5
- package/src/client/hooks/__tests__/useJsonApiGet.test.tsx +9 -9
- package/src/client/hooks/__tests__/useJsonApiMutation.test.tsx +11 -11
- package/src/client/hooks/__tests__/useRehydration.test.ts +13 -34
- package/src/components/editors/BlockNoteEditor.tsx +2 -2
- package/src/components/forms/CommonEditorTrigger.tsx +1 -1
- package/src/components/forms/FormCheckbox.tsx +2 -12
- package/src/components/forms/FormDate.tsx +1 -6
- package/src/components/forms/FormInput.tsx +1 -1
- package/src/components/forms/FormPassword.tsx +1 -7
- package/src/components/forms/FormSelect.tsx +2 -8
- package/src/components/forms/FormSlider.tsx +1 -5
- package/src/components/forms/FormSwitch.tsx +1 -5
- package/src/components/forms/GdprConsentCheckbox.tsx +2 -8
- package/src/components/forms/PasswordInput.tsx +28 -26
- package/src/components/forms/__tests__/FormCheckbox.test.tsx +16 -18
- package/src/components/forms/__tests__/FormDate.test.tsx +14 -30
- package/src/components/forms/__tests__/FormInput.test.tsx +21 -37
- package/src/components/forms/__tests__/FormSelect.test.tsx +15 -21
- package/src/components/tables/ContentListTable.tsx +1 -1
- package/src/components/tables/__tests__/ContentListTable.test.tsx +17 -89
- package/src/components/tables/cells/cell.component.tsx +1 -1
- package/src/contexts/HeaderChildrenContext.tsx +3 -1
- package/src/core/endpoint/__tests__/EndpointCreator.test.ts +2 -7
- package/src/core/factories/__tests__/JsonApiDataFactory.test.ts +3 -3
- package/src/core/factories/__tests__/RehydrationFactory.test.ts +4 -6
- package/src/core/registry/__tests__/DataClassRegistry.test.ts +5 -15
- package/src/core/registry/__tests__/ModuleRegistrar.test.ts +5 -15
- package/src/features/auth/components/GdprConsentSection.tsx +1 -6
- package/src/features/auth/components/details/LandingComponent.tsx +6 -1
- package/src/features/auth/components/forms/AcceptInvitation.tsx +1 -1
- package/src/features/auth/components/forms/ResetPassword.tsx +1 -1
- package/src/features/billing/components/cards/PaymentMethodSummaryCard.tsx +13 -18
- package/src/features/billing/components/cards/SubscriptionSummaryCard.tsx +7 -1
- package/src/features/billing/components/modals/BillingDetailModal.tsx +2 -13
- package/src/features/billing/stripe-customer/components/details/PaymentMethodCard.tsx +8 -1
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodEditor.tsx +2 -13
- package/src/features/billing/stripe-customer/components/forms/PaymentMethodForm.tsx +2 -12
- package/src/features/billing/stripe-invoice/components/details/InvoiceDetails.tsx +6 -1
- package/src/features/billing/stripe-price/components/lists/PricesList.tsx +13 -5
- package/src/features/billing/stripe-product/components/lists/ProductsList.tsx +5 -5
- package/src/features/billing/stripe-subscription/components/containers/SubscriptionsContainer.tsx +1 -3
- package/src/features/billing/stripe-subscription/components/details/SubscriptionDetails.tsx +4 -1
- package/src/features/billing/stripe-subscription/components/forms/CancelSubscriptionDialog.tsx +1 -1
- package/src/features/billing/stripe-subscription/components/lists/SubscriptionsList.tsx +4 -1
- package/src/features/billing/stripe-subscription/components/widgets/PricingCard.tsx +9 -2
- package/src/features/billing/stripe-subscription/components/widgets/SubscriptionStatusBadge.tsx +3 -1
- package/src/features/billing/stripe-subscription/components/wizards/SubscriptionWizard.tsx +1 -7
- package/src/features/billing/stripe-subscription/components/wizards/WizardProgressIndicator.tsx +2 -10
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepPaymentMethod.tsx +3 -13
- package/src/features/billing/stripe-subscription/components/wizards/WizardStepReview.tsx +5 -16
- package/src/features/billing/stripe-usage/components/details/UsageSummaryCard.tsx +1 -1
- package/src/features/billing/stripe-usage/components/lists/UsageHistoryTable.tsx +1 -1
- package/src/features/company/components/details/CompanyDetails.tsx +2 -2
- package/src/features/company/components/forms/CompanyConfigurationSecurityForm.tsx +1 -1
- package/src/features/notification/components/containers/NotificationsListContainer.tsx +1 -1
- package/src/features/notification/components/modals/NotificationModal.tsx +6 -2
- package/src/features/notification/contexts/NotificationContext.tsx +1 -3
- package/src/features/oauth/components/OAuthClientCard.tsx +15 -17
- package/src/features/oauth/components/OAuthClientDetail.tsx +7 -19
- package/src/features/oauth/components/OAuthClientForm.tsx +4 -13
- package/src/features/oauth/components/OAuthClientSecretDisplay.tsx +4 -20
- package/src/features/oauth/components/OAuthRedirectUriInput.tsx +5 -12
- package/src/features/oauth/components/OAuthScopeSelector.tsx +17 -23
- package/src/features/oauth/components/consent/OAuthConsentActions.tsx +3 -16
- package/src/features/oauth/components/consent/OAuthConsentHeader.tsx +3 -12
- package/src/features/oauth/components/consent/OAuthConsentScreen.tsx +5 -20
- package/src/features/oauth/components/consent/OAuthScopeList.tsx +3 -18
- package/src/features/onboarding/contexts/OnboardingContext.tsx +3 -3
- package/src/features/role/components/forms/FormRoles.tsx +1 -7
- package/src/features/user/components/containers/UserContainer.tsx +1 -1
- package/src/features/user/components/details/UserDetails.tsx +1 -1
- package/src/features/user/components/forms/UserDeleter.tsx +1 -1
- package/src/features/user/components/forms/UserEditor.tsx +1 -1
- package/src/features/user/components/forms/UserMultiSelect.tsx +7 -7
- package/src/features/user/components/lists/UserListInAdd.tsx +2 -2
- package/src/features/user/components/lists/UsersList.tsx +7 -1
- package/src/features/user/contexts/CurrentUserContext.tsx +36 -33
- package/src/hooks/__tests__/useDataListRetriever.test.ts +15 -21
- package/src/hooks/__tests__/useDebounce.test.ts +2 -7
- package/src/hooks/useCustomD3Graph.tsx +2 -2
- package/src/shadcnui/custom/multi-select.tsx +28 -2
- package/src/shadcnui/ui/accordion.tsx +21 -23
- package/src/shadcnui/ui/alert-dialog.tsx +45 -62
- package/src/shadcnui/ui/alert.tsx +25 -41
- package/src/shadcnui/ui/avatar.tsx +23 -36
- package/src/shadcnui/ui/badge.tsx +13 -11
- package/src/shadcnui/ui/breadcrumb.tsx +21 -55
- package/src/shadcnui/ui/button.tsx +17 -18
- package/src/shadcnui/ui/calendar.tsx +44 -93
- package/src/shadcnui/ui/carousel.tsx +72 -100
- package/src/shadcnui/ui/chart.tsx +102 -161
- package/src/shadcnui/ui/checkbox.tsx +8 -9
- package/src/shadcnui/ui/combobox.tsx +52 -83
- package/src/shadcnui/ui/command.tsx +43 -77
- package/src/shadcnui/ui/context-menu.tsx +47 -86
- package/src/shadcnui/ui/dialog.tsx +34 -60
- package/src/shadcnui/ui/drawer.tsx +32 -53
- package/src/shadcnui/ui/dropdown-menu.tsx +48 -65
- package/src/shadcnui/ui/field.tsx +39 -48
- package/src/shadcnui/ui/hover-card.tsx +9 -14
- package/src/shadcnui/ui/input-group.tsx +44 -55
- package/src/shadcnui/ui/input-otp.tsx +22 -26
- package/src/shadcnui/ui/input.tsx +6 -6
- package/src/shadcnui/ui/label.tsx +6 -6
- package/src/shadcnui/ui/navigation-menu.tsx +36 -60
- package/src/shadcnui/ui/popover.tsx +15 -38
- package/src/shadcnui/ui/progress.tsx +12 -29
- package/src/shadcnui/ui/radio-group.tsx +9 -15
- package/src/shadcnui/ui/resizable.tsx +14 -24
- package/src/shadcnui/ui/scroll-area.tsx +12 -27
- package/src/shadcnui/ui/select.tsx +41 -65
- package/src/shadcnui/ui/separator.tsx +7 -11
- package/src/shadcnui/ui/sheet.tsx +30 -55
- package/src/shadcnui/ui/sidebar.tsx +141 -189
- package/src/shadcnui/ui/skeleton.tsx +3 -9
- package/src/shadcnui/ui/slider.tsx +11 -23
- package/src/shadcnui/ui/switch.tsx +8 -8
- package/src/shadcnui/ui/tabs.tsx +14 -21
- package/src/shadcnui/ui/textarea.tsx +5 -5
- package/src/shadcnui/ui/toggle.tsx +8 -14
- package/src/shadcnui/ui/tooltip.tsx +11 -23
- package/src/testing/providers/MockJsonApiProvider.tsx +1 -5
- package/src/testing/utils/renderWithProviders.tsx +6 -10
- package/dist/BlockNoteEditor-SZWO3MDO.mjs.map +0 -1
- package/dist/chunk-E6PQQTWF.js.map +0 -1
- package/dist/chunk-I7DFEJFF.mjs.map +0 -1
|
@@ -49,28 +49,29 @@ export function OAuthScopeSelector({
|
|
|
49
49
|
onChange(value.filter((s) => s !== scope));
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
|
-
[value, onChange]
|
|
52
|
+
[value, onChange],
|
|
53
53
|
);
|
|
54
54
|
|
|
55
55
|
// Group scopes by category (before the colon)
|
|
56
|
-
const groupedScopes = availableScopes.reduce(
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
const groupedScopes = availableScopes.reduce(
|
|
57
|
+
(acc, scope) => {
|
|
58
|
+
const [category] = scope.scope.split(":");
|
|
59
|
+
const groupName = category === scope.scope ? "General" : category;
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
if (!acc[groupName]) {
|
|
62
|
+
acc[groupName] = [];
|
|
63
|
+
}
|
|
64
|
+
acc[groupName].push(scope);
|
|
65
|
+
return acc;
|
|
66
|
+
},
|
|
67
|
+
{} as Record<string, OAuthScopeInfo[]>,
|
|
68
|
+
);
|
|
66
69
|
|
|
67
70
|
return (
|
|
68
71
|
<div className="space-y-4">
|
|
69
72
|
<div>
|
|
70
73
|
<Label>{label} *</Label>
|
|
71
|
-
<p className="text-sm text-muted-foreground">
|
|
72
|
-
Select the permissions your application needs.
|
|
73
|
-
</p>
|
|
74
|
+
<p className="text-sm text-muted-foreground">Select the permissions your application needs.</p>
|
|
74
75
|
</div>
|
|
75
76
|
|
|
76
77
|
<div className="space-y-4">
|
|
@@ -92,20 +93,13 @@ export function OAuthScopeSelector({
|
|
|
92
93
|
<Checkbox
|
|
93
94
|
id={`scope-${scopeInfo.scope}`}
|
|
94
95
|
checked={isChecked}
|
|
95
|
-
onCheckedChange={(checked) =>
|
|
96
|
-
handleToggle(scopeInfo.scope, checked === true)
|
|
97
|
-
}
|
|
96
|
+
onCheckedChange={(checked) => handleToggle(scopeInfo.scope, checked === true)}
|
|
98
97
|
disabled={disabled}
|
|
99
98
|
/>
|
|
100
99
|
<div className="flex-1">
|
|
101
|
-
<Label
|
|
102
|
-
htmlFor={`scope-${scopeInfo.scope}`}
|
|
103
|
-
className="text-sm font-medium cursor-pointer"
|
|
104
|
-
>
|
|
100
|
+
<Label htmlFor={`scope-${scopeInfo.scope}`} className="text-sm font-medium cursor-pointer">
|
|
105
101
|
{scopeInfo.name}
|
|
106
|
-
{isAdmin && (
|
|
107
|
-
<span className="ml-2 text-xs text-destructive">(Dangerous)</span>
|
|
108
|
-
)}
|
|
102
|
+
{isAdmin && <span className="ml-2 text-xs text-destructive">(Dangerous)</span>}
|
|
109
103
|
</Label>
|
|
110
104
|
<p className="text-xs text-muted-foreground">{scopeInfo.description}</p>
|
|
111
105
|
</div>
|
|
@@ -14,26 +14,13 @@ export interface OAuthConsentActionsProps {
|
|
|
14
14
|
/**
|
|
15
15
|
* Action buttons for OAuth consent screen
|
|
16
16
|
*/
|
|
17
|
-
export function OAuthConsentActions({
|
|
18
|
-
onApprove,
|
|
19
|
-
onDeny,
|
|
20
|
-
isLoading = false,
|
|
21
|
-
}: OAuthConsentActionsProps) {
|
|
17
|
+
export function OAuthConsentActions({ onApprove, onDeny, isLoading = false }: OAuthConsentActionsProps) {
|
|
22
18
|
return (
|
|
23
19
|
<div className="flex flex-col sm:flex-row gap-3">
|
|
24
|
-
<Button
|
|
25
|
-
variant="outline"
|
|
26
|
-
onClick={onDeny}
|
|
27
|
-
disabled={isLoading}
|
|
28
|
-
className="flex-1"
|
|
29
|
-
>
|
|
20
|
+
<Button variant="outline" onClick={onDeny} disabled={isLoading} className="flex-1">
|
|
30
21
|
Deny
|
|
31
22
|
</Button>
|
|
32
|
-
<Button
|
|
33
|
-
onClick={onApprove}
|
|
34
|
-
disabled={isLoading}
|
|
35
|
-
className="flex-1"
|
|
36
|
-
>
|
|
23
|
+
<Button onClick={onApprove} disabled={isLoading} className="flex-1">
|
|
37
24
|
{isLoading ? "Authorizing..." : "Authorize"}
|
|
38
25
|
</Button>
|
|
39
26
|
</div>
|
|
@@ -16,21 +16,13 @@ export interface OAuthConsentHeaderProps {
|
|
|
16
16
|
* Header component for OAuth consent screen
|
|
17
17
|
* Shows platform logo and requesting app information
|
|
18
18
|
*/
|
|
19
|
-
export function OAuthConsentHeader({
|
|
20
|
-
client,
|
|
21
|
-
logoUrl,
|
|
22
|
-
appName = "Only35",
|
|
23
|
-
}: OAuthConsentHeaderProps) {
|
|
19
|
+
export function OAuthConsentHeader({ client, logoUrl, appName = "Only35" }: OAuthConsentHeaderProps) {
|
|
24
20
|
return (
|
|
25
21
|
<div className="text-center space-y-4">
|
|
26
22
|
{/* Platform Logo */}
|
|
27
23
|
<div className="flex justify-center">
|
|
28
24
|
{logoUrl ? (
|
|
29
|
-
<img
|
|
30
|
-
src={logoUrl}
|
|
31
|
-
alt={appName}
|
|
32
|
-
className="h-12 w-auto"
|
|
33
|
-
/>
|
|
25
|
+
<img src={logoUrl} alt={appName} className="h-12 w-auto" />
|
|
34
26
|
) : (
|
|
35
27
|
<div className="h-12 w-12 rounded-full bg-primary flex items-center justify-center">
|
|
36
28
|
<Shield className="h-6 w-6 text-primary-foreground" />
|
|
@@ -42,8 +34,7 @@ export function OAuthConsentHeader({
|
|
|
42
34
|
<div className="space-y-2">
|
|
43
35
|
<h1 className="text-2xl font-bold">Authorize {client.name}</h1>
|
|
44
36
|
<p className="text-muted-foreground">
|
|
45
|
-
<span className="font-medium text-foreground">{client.name}</span>
|
|
46
|
-
{" "}wants to access your {appName} account
|
|
37
|
+
<span className="font-medium text-foreground">{client.name}</span> wants to access your {appName} account
|
|
47
38
|
</p>
|
|
48
39
|
</div>
|
|
49
40
|
</div>
|
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { ExternalLink, AlertTriangle, Loader2 } from "lucide-react";
|
|
4
|
-
import {
|
|
5
|
-
Card,
|
|
6
|
-
CardContent,
|
|
7
|
-
CardFooter,
|
|
8
|
-
Separator,
|
|
9
|
-
Alert,
|
|
10
|
-
AlertDescription,
|
|
11
|
-
} from "../../../../shadcnui";
|
|
4
|
+
import { Card, CardContent, CardFooter, Separator, Alert, AlertDescription } from "../../../../shadcnui";
|
|
12
5
|
import { OAuthConsentHeader } from "./OAuthConsentHeader";
|
|
13
6
|
import { OAuthScopeList } from "./OAuthScopeList";
|
|
14
7
|
import { OAuthConsentActions } from "./OAuthConsentActions";
|
|
@@ -92,11 +85,7 @@ export function OAuthConsentScreen({
|
|
|
92
85
|
<Card className="w-full max-w-md">
|
|
93
86
|
<CardContent className="pt-6 space-y-6">
|
|
94
87
|
{/* Header */}
|
|
95
|
-
<OAuthConsentHeader
|
|
96
|
-
client={client}
|
|
97
|
-
logoUrl={logoUrl}
|
|
98
|
-
appName={appName}
|
|
99
|
-
/>
|
|
88
|
+
<OAuthConsentHeader client={client} logoUrl={logoUrl} appName={appName} />
|
|
100
89
|
|
|
101
90
|
<Separator />
|
|
102
91
|
|
|
@@ -115,11 +104,7 @@ export function OAuthConsentScreen({
|
|
|
115
104
|
</div>
|
|
116
105
|
|
|
117
106
|
{/* Actions */}
|
|
118
|
-
<OAuthConsentActions
|
|
119
|
-
onApprove={approve}
|
|
120
|
-
onDeny={deny}
|
|
121
|
-
isLoading={isSubmitting}
|
|
122
|
-
/>
|
|
107
|
+
<OAuthConsentActions onApprove={approve} onDeny={deny} isLoading={isSubmitting} />
|
|
123
108
|
</CardContent>
|
|
124
109
|
|
|
125
110
|
{/* Footer */}
|
|
@@ -128,8 +113,8 @@ export function OAuthConsentScreen({
|
|
|
128
113
|
By authorizing, you agree to the app's{" "}
|
|
129
114
|
<a href={termsUrl} className="underline hover:text-foreground" target="_blank" rel="noopener">
|
|
130
115
|
Terms of Service
|
|
131
|
-
</a>
|
|
132
|
-
|
|
116
|
+
</a>{" "}
|
|
117
|
+
and{" "}
|
|
133
118
|
<a href={privacyUrl} className="underline hover:text-foreground" target="_blank" rel="noopener">
|
|
134
119
|
Privacy Policy
|
|
135
120
|
</a>
|
|
@@ -1,16 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
Eye,
|
|
5
|
-
Pencil,
|
|
6
|
-
Image,
|
|
7
|
-
Upload,
|
|
8
|
-
Film,
|
|
9
|
-
FolderPlus,
|
|
10
|
-
User,
|
|
11
|
-
Shield,
|
|
12
|
-
LucideIcon,
|
|
13
|
-
} from "lucide-react";
|
|
3
|
+
import { Eye, Pencil, Image, Upload, Film, FolderPlus, User, Shield, LucideIcon } from "lucide-react";
|
|
14
4
|
import { OAuthScopeInfo } from "../../interfaces/oauth.interface";
|
|
15
5
|
|
|
16
6
|
export interface OAuthScopeListProps {
|
|
@@ -48,15 +38,10 @@ export function OAuthScopeList({ scopes }: OAuthScopeListProps) {
|
|
|
48
38
|
const IconComponent = scope.icon ? SCOPE_ICONS[scope.icon] : Eye;
|
|
49
39
|
|
|
50
40
|
return (
|
|
51
|
-
<li
|
|
52
|
-
key={scope.scope}
|
|
53
|
-
className="flex items-start gap-3 p-3 rounded-lg bg-muted/50"
|
|
54
|
-
>
|
|
41
|
+
<li key={scope.scope} className="flex items-start gap-3 p-3 rounded-lg bg-muted/50">
|
|
55
42
|
<div className="flex-shrink-0 mt-0.5">
|
|
56
43
|
<div className="h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center">
|
|
57
|
-
{IconComponent &&
|
|
58
|
-
<IconComponent className="h-4 w-4 text-primary" />
|
|
59
|
-
)}
|
|
44
|
+
{IconComponent && <IconComponent className="h-4 w-4 text-primary" />}
|
|
60
45
|
</div>
|
|
61
46
|
</div>
|
|
62
47
|
<div className="flex-1">
|
|
@@ -18,10 +18,10 @@ const OnboardingContext = createContext<OnboardingContextValue | null>(null);
|
|
|
18
18
|
export function OnboardingProvider({
|
|
19
19
|
children,
|
|
20
20
|
tours = [],
|
|
21
|
-
tourPaths = {},
|
|
21
|
+
tourPaths: _tourPaths = {},
|
|
22
22
|
labels = DEFAULT_ONBOARDING_LABELS,
|
|
23
23
|
renderCard,
|
|
24
|
-
zIndex = 9999,
|
|
24
|
+
zIndex: _zIndex = 9999,
|
|
25
25
|
}: OnboardingProviderProps) {
|
|
26
26
|
const [isTourActive, setIsTourActive] = useState(false);
|
|
27
27
|
const [activeTourId, setActiveTourId] = useState<string | null>(null);
|
|
@@ -35,7 +35,7 @@ export function OnboardingProvider({
|
|
|
35
35
|
rootsRef.current.forEach((root) => {
|
|
36
36
|
try {
|
|
37
37
|
root.unmount();
|
|
38
|
-
} catch (
|
|
38
|
+
} catch (_e) {
|
|
39
39
|
// Root may already be unmounted
|
|
40
40
|
}
|
|
41
41
|
});
|
|
@@ -2,13 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { useTranslations } from "next-intl";
|
|
4
4
|
import { FormFieldWrapper } from "../../../../components/forms";
|
|
5
|
-
import {
|
|
6
|
-
Checkbox,
|
|
7
|
-
FieldLabel,
|
|
8
|
-
Tooltip,
|
|
9
|
-
TooltipContent,
|
|
10
|
-
TooltipTrigger,
|
|
11
|
-
} from "../../../../shadcnui";
|
|
5
|
+
import { Checkbox, FieldLabel, Tooltip, TooltipContent, TooltipTrigger } from "../../../../shadcnui";
|
|
12
6
|
import { UserInterface } from "../../../user";
|
|
13
7
|
import { useCurrentUserContext } from "../../../user/contexts";
|
|
14
8
|
import { RoleInterface } from "../../data";
|
|
@@ -26,7 +26,7 @@ export function UserDetails({ user }: UserDetailsProps) {
|
|
|
26
26
|
roles = (
|
|
27
27
|
<div className="mb-4 w-full">
|
|
28
28
|
<div className="flex flex-wrap gap-2">
|
|
29
|
-
{user.roles.map((role: RoleInterface,
|
|
29
|
+
{user.roles.map((role: RoleInterface, _index: number) => (
|
|
30
30
|
<Link key={role.id} href={generateUrl({ page: Modules.Role, id: role.id })}>
|
|
31
31
|
<Badge className="mr-2" variant={`default`}>
|
|
32
32
|
{t(`role.roles`, { role: role.id.replaceAll(`-`, ``) })}
|
|
@@ -20,7 +20,7 @@ function UserDeleterInternal({ user, onDeleted, companyId }: UserDeleterProps) {
|
|
|
20
20
|
const { currentUser, company } = useCurrentUserContext<UserInterface>();
|
|
21
21
|
const generateUrl = usePageUrlGenerator();
|
|
22
22
|
const router = useI18nRouter();
|
|
23
|
-
const
|
|
23
|
+
const _t = useTranslations();
|
|
24
24
|
|
|
25
25
|
let cId;
|
|
26
26
|
if (currentUser?.roles.find((role) => role.id === getRoleId().Administrator) && companyId) {
|
|
@@ -106,7 +106,7 @@ function UserEditorInternal({ user, propagateChanges, adminCreated, trigger, onR
|
|
|
106
106
|
errorToast({ title: t(`user.errors.email_exists`), error: "" });
|
|
107
107
|
return;
|
|
108
108
|
}
|
|
109
|
-
} catch (
|
|
109
|
+
} catch (_error) {
|
|
110
110
|
// User does not exist, proceed
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
import { useTranslations } from "next-intl";
|
|
3
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
5
|
import { useWatch } from "react-hook-form";
|
|
5
6
|
import { FormFieldWrapper } from "../../../../components/forms";
|
|
6
7
|
import { Modules } from "../../../../core";
|
|
7
8
|
import { DataListRetriever, useDataListRetriever, useDebounce } from "../../../../hooks";
|
|
8
|
-
import {
|
|
9
|
-
Avatar,
|
|
10
|
-
AvatarFallback,
|
|
11
|
-
AvatarImage,
|
|
12
|
-
MultiSelect,
|
|
13
|
-
} from "../../../../shadcnui";
|
|
9
|
+
import { Avatar, AvatarFallback, AvatarImage, MultiSelect } from "../../../../shadcnui";
|
|
14
10
|
import { useCurrentUserContext } from "../../contexts";
|
|
15
11
|
import { UserInterface } from "../../data";
|
|
16
12
|
import { UserService } from "../../data/user.service";
|
|
@@ -59,10 +55,11 @@ export function UserMultiSelect({
|
|
|
59
55
|
maxCount = 3,
|
|
60
56
|
isRequired = false,
|
|
61
57
|
}: UserMultiSelectProps) {
|
|
58
|
+
const t = useTranslations();
|
|
62
59
|
const { company } = useCurrentUserContext<UserInterface>();
|
|
63
60
|
|
|
64
61
|
const searchTermRef = useRef<string>("");
|
|
65
|
-
const [searchTerm,
|
|
62
|
+
const [searchTerm, _setSearchTerm] = useState<string>("");
|
|
66
63
|
const [isSearching, setIsSearching] = useState<boolean>(false);
|
|
67
64
|
const [userOptions, setUserOptions] = useState<any[]>([]);
|
|
68
65
|
|
|
@@ -194,6 +191,9 @@ export function UserMultiSelect({
|
|
|
194
191
|
placeholder={placeholder}
|
|
195
192
|
maxCount={maxCount}
|
|
196
193
|
animation={0}
|
|
194
|
+
loading={isSearching}
|
|
195
|
+
loadingText={t("ui.search.button")}
|
|
196
|
+
emptyText={t("ui.search.no_results", { type: t("entities.users", { count: 2 }) })}
|
|
197
197
|
/>
|
|
198
198
|
)}
|
|
199
199
|
</FormFieldWrapper>
|
|
@@ -28,11 +28,11 @@ export function UserListInAdd({ data, existingUsers, setSelectedUser, setLevelOp
|
|
|
28
28
|
<CommandItem
|
|
29
29
|
className="cursor-pointer hover:bg-muted data-selected:hover:bg-muted bg-transparent data-selected:bg-transparent"
|
|
30
30
|
key={user.id}
|
|
31
|
-
onClick={(
|
|
31
|
+
onClick={(_e) => {
|
|
32
32
|
setSelectedUser(user);
|
|
33
33
|
setLevelOpen?.(true);
|
|
34
34
|
}}
|
|
35
|
-
onSelect={(
|
|
35
|
+
onSelect={(_e) => {
|
|
36
36
|
setSelectedUser(user);
|
|
37
37
|
setLevelOpen?.(true);
|
|
38
38
|
}}
|
|
@@ -16,7 +16,13 @@ type UsersListProps = {
|
|
|
16
16
|
restrictToJoinRequests?: boolean;
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
export function UsersList({
|
|
19
|
+
export function UsersList({
|
|
20
|
+
data,
|
|
21
|
+
optionComponents: _optionComponents,
|
|
22
|
+
removeFunction: _removeFunction,
|
|
23
|
+
hideOptions: _hideOptions,
|
|
24
|
+
showRelevance: _showRelevance,
|
|
25
|
+
}: UsersListProps) {
|
|
20
26
|
const t = useTranslations();
|
|
21
27
|
|
|
22
28
|
return (
|
|
@@ -51,7 +51,7 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
|
|
|
51
51
|
if (!token && dehydratedUser) setDehydratedUser(null);
|
|
52
52
|
}, [dehydratedUser, setDehydratedUser]);
|
|
53
53
|
|
|
54
|
-
const matchUrlToModule = (
|
|
54
|
+
const matchUrlToModule = (_params?: { path: string }): ModuleWithPermissions | undefined => {
|
|
55
55
|
const moduleKeys = Object.getOwnPropertyNames(Modules).filter(
|
|
56
56
|
(key) => key !== "prototype" && key !== "_factory" && key !== "length" && key !== "name",
|
|
57
57
|
);
|
|
@@ -138,41 +138,44 @@ export const CurrentUserProvider = ({ children }: { children: React.ReactNode })
|
|
|
138
138
|
// Function to refresh user data from the API
|
|
139
139
|
// skipCookieUpdate: When true, only updates React state without calling the Server Action
|
|
140
140
|
// This prevents page reloads when refresh is triggered by WebSocket events
|
|
141
|
-
const refreshUser = useCallback(
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
const refreshUser = useCallback(
|
|
142
|
+
async (options?: { skipCookieUpdate?: boolean }): Promise<void> => {
|
|
143
|
+
if (isRefreshing) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
145
146
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
147
|
+
setIsRefreshing(true);
|
|
148
|
+
try {
|
|
149
|
+
const fullUser = await UserService.findFullUser();
|
|
150
|
+
if (fullUser) {
|
|
151
|
+
const dehydrated = fullUser.dehydrate();
|
|
152
|
+
|
|
153
|
+
setDehydratedUser(dehydrated as any);
|
|
154
|
+
setUser(fullUser);
|
|
155
|
+
|
|
156
|
+
// Update authentication cookies with fresh user data
|
|
157
|
+
// Skip when triggered by WebSocket to prevent page reload (Server Actions modify cookies)
|
|
158
|
+
if (!options?.skipCookieUpdate) {
|
|
159
|
+
await getTokenHandler()?.updateToken({
|
|
160
|
+
userId: fullUser.id,
|
|
161
|
+
companyId: fullUser.company?.id,
|
|
162
|
+
roles: fullUser.roles.map((role) => role.id),
|
|
163
|
+
features: fullUser.company?.features?.map((feature) => feature.id) ?? [],
|
|
164
|
+
modules: fullUser.modules.map((module) => ({
|
|
165
|
+
id: module.id,
|
|
166
|
+
permissions: module.permissions,
|
|
167
|
+
})),
|
|
168
|
+
});
|
|
169
|
+
}
|
|
168
170
|
}
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error("Failed to refresh user data:", error);
|
|
173
|
+
} finally {
|
|
174
|
+
setIsRefreshing(false);
|
|
169
175
|
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
setIsRefreshing(false);
|
|
174
|
-
}
|
|
175
|
-
}, [isRefreshing, setDehydratedUser]);
|
|
176
|
+
},
|
|
177
|
+
[isRefreshing, setDehydratedUser],
|
|
178
|
+
);
|
|
176
179
|
|
|
177
180
|
// WebSocket integration for real-time token updates
|
|
178
181
|
const { socket, isConnected } = useSocketContext();
|
|
@@ -22,7 +22,7 @@ describe("useDataListRetriever", () => {
|
|
|
22
22
|
useDataListRetriever({
|
|
23
23
|
retriever,
|
|
24
24
|
module: mockModule,
|
|
25
|
-
})
|
|
25
|
+
}),
|
|
26
26
|
);
|
|
27
27
|
|
|
28
28
|
// Wait for async operations to settle
|
|
@@ -42,7 +42,7 @@ describe("useDataListRetriever", () => {
|
|
|
42
42
|
retriever,
|
|
43
43
|
module: mockModule,
|
|
44
44
|
ready: false,
|
|
45
|
-
})
|
|
45
|
+
}),
|
|
46
46
|
);
|
|
47
47
|
|
|
48
48
|
expect(result.current.ready).toBe(false);
|
|
@@ -63,7 +63,7 @@ describe("useDataListRetriever", () => {
|
|
|
63
63
|
useDataListRetriever({
|
|
64
64
|
retriever,
|
|
65
65
|
module: mockModule,
|
|
66
|
-
})
|
|
66
|
+
}),
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
await waitFor(() => {
|
|
@@ -82,7 +82,7 @@ describe("useDataListRetriever", () => {
|
|
|
82
82
|
useDataListRetriever({
|
|
83
83
|
retriever,
|
|
84
84
|
module: mockModule,
|
|
85
|
-
})
|
|
85
|
+
}),
|
|
86
86
|
);
|
|
87
87
|
|
|
88
88
|
await waitFor(() => {
|
|
@@ -96,16 +96,14 @@ describe("useDataListRetriever", () => {
|
|
|
96
96
|
|
|
97
97
|
describe("search", () => {
|
|
98
98
|
it("should search and call retriever with search term", async () => {
|
|
99
|
-
const mockData = [
|
|
100
|
-
createMockApiData({ type: "articles", id: "1", attributes: { title: "Search Result" } }),
|
|
101
|
-
];
|
|
99
|
+
const mockData = [createMockApiData({ type: "articles", id: "1", attributes: { title: "Search Result" } })];
|
|
102
100
|
const retriever = vi.fn().mockResolvedValue(mockData);
|
|
103
101
|
|
|
104
102
|
const { result } = renderHook(() =>
|
|
105
103
|
useDataListRetriever({
|
|
106
104
|
retriever,
|
|
107
105
|
module: mockModule,
|
|
108
|
-
})
|
|
106
|
+
}),
|
|
109
107
|
);
|
|
110
108
|
|
|
111
109
|
await waitFor(() => {
|
|
@@ -133,7 +131,7 @@ describe("useDataListRetriever", () => {
|
|
|
133
131
|
useDataListRetriever({
|
|
134
132
|
retriever,
|
|
135
133
|
module: mockModule,
|
|
136
|
-
})
|
|
134
|
+
}),
|
|
137
135
|
);
|
|
138
136
|
|
|
139
137
|
await waitFor(() => {
|
|
@@ -161,7 +159,7 @@ describe("useDataListRetriever", () => {
|
|
|
161
159
|
useDataListRetriever({
|
|
162
160
|
retriever,
|
|
163
161
|
module: mockModule,
|
|
164
|
-
})
|
|
162
|
+
}),
|
|
165
163
|
);
|
|
166
164
|
|
|
167
165
|
await waitFor(() => {
|
|
@@ -189,7 +187,7 @@ describe("useDataListRetriever", () => {
|
|
|
189
187
|
retriever,
|
|
190
188
|
module: mockModule,
|
|
191
189
|
ready: false,
|
|
192
|
-
})
|
|
190
|
+
}),
|
|
193
191
|
);
|
|
194
192
|
|
|
195
193
|
expect(result.current.ready).toBe(false);
|
|
@@ -209,16 +207,14 @@ describe("useDataListRetriever", () => {
|
|
|
209
207
|
|
|
210
208
|
describe("element management", () => {
|
|
211
209
|
it("should update element with setRefreshedElement", async () => {
|
|
212
|
-
const mockData = [
|
|
213
|
-
createMockApiData({ type: "articles", id: "1", attributes: { title: "Original" } }),
|
|
214
|
-
];
|
|
210
|
+
const mockData = [createMockApiData({ type: "articles", id: "1", attributes: { title: "Original" } })];
|
|
215
211
|
const retriever = vi.fn().mockResolvedValue(mockData);
|
|
216
212
|
|
|
217
213
|
const { result } = renderHook(() =>
|
|
218
214
|
useDataListRetriever({
|
|
219
215
|
retriever,
|
|
220
216
|
module: mockModule,
|
|
221
|
-
})
|
|
217
|
+
}),
|
|
222
218
|
);
|
|
223
219
|
|
|
224
220
|
await waitFor(() => {
|
|
@@ -250,7 +246,7 @@ describe("useDataListRetriever", () => {
|
|
|
250
246
|
useDataListRetriever({
|
|
251
247
|
retriever,
|
|
252
248
|
module: mockModule,
|
|
253
|
-
})
|
|
249
|
+
}),
|
|
254
250
|
);
|
|
255
251
|
|
|
256
252
|
await waitFor(() => {
|
|
@@ -276,7 +272,7 @@ describe("useDataListRetriever", () => {
|
|
|
276
272
|
useDataListRetriever({
|
|
277
273
|
retriever,
|
|
278
274
|
module: mockModule,
|
|
279
|
-
})
|
|
275
|
+
}),
|
|
280
276
|
);
|
|
281
277
|
|
|
282
278
|
await waitFor(() => {
|
|
@@ -288,9 +284,7 @@ describe("useDataListRetriever", () => {
|
|
|
288
284
|
});
|
|
289
285
|
|
|
290
286
|
await waitFor(() => {
|
|
291
|
-
expect(retriever).toHaveBeenCalledWith(
|
|
292
|
-
expect.objectContaining({ filter: "active" })
|
|
293
|
-
);
|
|
287
|
+
expect(retriever).toHaveBeenCalledWith(expect.objectContaining({ filter: "active" }));
|
|
294
288
|
});
|
|
295
289
|
});
|
|
296
290
|
|
|
@@ -301,7 +295,7 @@ describe("useDataListRetriever", () => {
|
|
|
301
295
|
useDataListRetriever({
|
|
302
296
|
retriever,
|
|
303
297
|
module: mockModule,
|
|
304
|
-
})
|
|
298
|
+
}),
|
|
305
299
|
);
|
|
306
300
|
|
|
307
301
|
await waitFor(() => {
|
|
@@ -92,9 +92,7 @@ describe("useDebounce", () => {
|
|
|
92
92
|
|
|
93
93
|
it("should pass multiple arguments to callback", () => {
|
|
94
94
|
const callback = vi.fn();
|
|
95
|
-
const { result } = renderHook(() =>
|
|
96
|
-
useDebounce((a: string, b: number, c: boolean) => callback(a, b, c), 500)
|
|
97
|
-
);
|
|
95
|
+
const { result } = renderHook(() => useDebounce((a: string, b: number, c: boolean) => callback(a, b, c), 500));
|
|
98
96
|
|
|
99
97
|
act(() => {
|
|
100
98
|
result.current("hello", 42, true);
|
|
@@ -111,10 +109,7 @@ describe("useDebounce", () => {
|
|
|
111
109
|
const callback1 = vi.fn();
|
|
112
110
|
const callback2 = vi.fn();
|
|
113
111
|
|
|
114
|
-
const { result, rerender } = renderHook(
|
|
115
|
-
({ cb }) => useDebounce(cb, 500),
|
|
116
|
-
{ initialProps: { cb: callback1 } }
|
|
117
|
-
);
|
|
112
|
+
const { result, rerender } = renderHook(({ cb }) => useDebounce(cb, 500), { initialProps: { cb: callback1 } });
|
|
118
113
|
|
|
119
114
|
act(() => {
|
|
120
115
|
result.current("test");
|
|
@@ -269,7 +269,7 @@ export function useCustomD3Graph(
|
|
|
269
269
|
}
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
for (const [
|
|
272
|
+
for (const [_nodeId, node] of nodeHierarchy.entries()) {
|
|
273
273
|
if (node.depth === 1 && node.angle !== undefined && node.x !== undefined && node.y !== undefined) {
|
|
274
274
|
const childAngle = node.angle;
|
|
275
275
|
const childX = node.x;
|
|
@@ -527,7 +527,7 @@ export function useCustomD3Graph(
|
|
|
527
527
|
const Icon = d.icon as React.FC<{ size: number; color: string }>;
|
|
528
528
|
const iconSvg = renderToStaticMarkup(<Icon size={nodeRadius / 2} color="white" />);
|
|
529
529
|
|
|
530
|
-
const
|
|
530
|
+
const _iconGroup = d3
|
|
531
531
|
.select(this)
|
|
532
532
|
.append("g")
|
|
533
533
|
.html(iconSvg)
|