@vertesia/ui 0.70.0 → 0.72.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/lib/esm/core/components/shadcn/index.js +2 -2
- package/lib/esm/core/components/shadcn/index.js.map +1 -1
- package/lib/esm/core/components/shadcn/tabs.js +13 -8
- package/lib/esm/core/components/shadcn/tabs.js.map +1 -1
- package/lib/esm/core/components/shadcn/theme/ThemeProvider.js +1 -0
- package/lib/esm/core/components/shadcn/theme/ThemeProvider.js.map +1 -1
- package/lib/esm/features/permissions/UserPermissionsProvider.js +1 -0
- package/lib/esm/features/permissions/UserPermissionsProvider.js.map +1 -1
- package/lib/esm/session/auth/useCurrentTenant.js +63 -0
- package/lib/esm/session/auth/useCurrentTenant.js.map +1 -0
- package/lib/esm/session/index.js +1 -0
- package/lib/esm/session/index.js.map +1 -1
- package/lib/esm/shell/SplashScreen.js +10 -3
- package/lib/esm/shell/SplashScreen.js.map +1 -1
- package/lib/esm/shell/VertesiaShell.js +2 -2
- package/lib/esm/shell/VertesiaShell.js.map +1 -1
- package/lib/esm/shell/apps/CheckAppAccess.js +23 -0
- package/lib/esm/shell/apps/CheckAppAccess.js.map +1 -0
- package/lib/esm/shell/apps/index.js +2 -0
- package/lib/esm/shell/apps/index.js.map +1 -0
- package/lib/esm/shell/index.js +1 -0
- package/lib/esm/shell/index.js.map +1 -1
- package/lib/esm/shell/login/InviteAcceptModal.js +29 -12
- package/lib/esm/shell/login/InviteAcceptModal.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/components/shadcn/index.d.ts +2 -2
- package/lib/types/core/components/shadcn/index.d.ts.map +1 -1
- package/lib/types/core/components/shadcn/tabs.d.ts +4 -1
- package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
- package/lib/types/core/components/shadcn/theme/ThemeProvider.d.ts +2 -1
- package/lib/types/core/components/shadcn/theme/ThemeProvider.d.ts.map +1 -1
- package/lib/types/features/permissions/UserPermissionsProvider.d.ts +2 -1
- package/lib/types/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
- package/lib/types/session/auth/useCurrentTenant.d.ts +15 -0
- package/lib/types/session/auth/useCurrentTenant.d.ts.map +1 -0
- package/lib/types/session/index.d.ts +1 -0
- package/lib/types/session/index.d.ts.map +1 -1
- package/lib/types/shell/SplashScreen.d.ts +3 -1
- package/lib/types/shell/SplashScreen.d.ts.map +1 -1
- package/lib/types/shell/VertesiaShell.d.ts +3 -1
- package/lib/types/shell/VertesiaShell.d.ts.map +1 -1
- package/lib/types/shell/apps/CheckAppAccess.d.ts +6 -0
- package/lib/types/shell/apps/CheckAppAccess.d.ts.map +1 -0
- package/lib/types/shell/apps/index.d.ts +2 -0
- package/lib/types/shell/apps/index.d.ts.map +1 -0
- package/lib/types/shell/index.d.ts +1 -0
- package/lib/types/shell/index.d.ts.map +1 -1
- package/lib/types/shell/login/InviteAcceptModal.d.ts.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-features.js +1 -1
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/package.json +4 -4
- package/src/core/components/shadcn/index.ts +2 -2
- package/src/core/components/shadcn/tabs.tsx +31 -14
- package/src/core/components/shadcn/theme/ThemeProvider.tsx +2 -0
- package/src/features/permissions/UserPermissionsProvider.tsx +1 -0
- package/src/session/auth/useCurrentTenant.ts +73 -0
- package/src/session/index.ts +1 -0
- package/src/shell/SplashScreen.tsx +36 -3
- package/src/shell/VertesiaShell.tsx +4 -2
- package/src/shell/apps/CheckAppAccess.tsx +25 -0
- package/src/shell/apps/index.ts +1 -0
- package/src/shell/index.tsx +1 -0
- package/src/shell/login/InviteAcceptModal.tsx +36 -20
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Transition } from "@headlessui/react";
|
|
2
|
-
import { Fragment, useEffect, useState } from "react";
|
|
2
|
+
import { Fragment, ReactNode, useEffect, useState } from "react";
|
|
3
3
|
import { useUserSession } from "@vertesia/ui/session";
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
interface SplashScreenProps {
|
|
7
|
+
icon?: ReactNode;
|
|
7
8
|
}
|
|
8
|
-
export function SplashScreen({ }: SplashScreenProps) {
|
|
9
|
+
export function SplashScreen({ icon: Icon }: SplashScreenProps) {
|
|
9
10
|
const { isLoading } = useUserSession();
|
|
10
11
|
const [show, setShow] = useState(true);
|
|
11
12
|
|
|
@@ -32,10 +33,42 @@ export function SplashScreen({ }: SplashScreenProps) {
|
|
|
32
33
|
<div style={{ zIndex: 999999 }} className='fixed inset-x-0 inset-y-0'>
|
|
33
34
|
<div className="flex w-full h-full items-center justify-center">
|
|
34
35
|
<div className="animate-[spin_4s_linear_infinite]">
|
|
35
|
-
<
|
|
36
|
+
<div className='animate-pulse rounded-full bg-transparent'>
|
|
37
|
+
{Icon || <LoadingIcon />}
|
|
38
|
+
</div>
|
|
36
39
|
</div>
|
|
37
40
|
</div>
|
|
38
41
|
</div>
|
|
39
42
|
</Transition>
|
|
40
43
|
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function LoadingIcon() {
|
|
47
|
+
const stopColor1 = "currentColor";
|
|
48
|
+
const stopColor2 = "currentColor";
|
|
49
|
+
// const stopColor1 = "#4F46E5";
|
|
50
|
+
// const stopColor2 = "#4F46E5";
|
|
51
|
+
return (
|
|
52
|
+
<svg
|
|
53
|
+
className="w-8 h-8 text-indigo-600"
|
|
54
|
+
viewBox="0 0 50 50"
|
|
55
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
+
>
|
|
57
|
+
<defs>
|
|
58
|
+
<linearGradient id="spinner-gradient" x1="1" y1="0" x2="0" y2="1">
|
|
59
|
+
<stop offset="0%" stopColor={stopColor1} stopOpacity="1" />
|
|
60
|
+
<stop offset="100%" stopColor={stopColor2} stopOpacity="0" />
|
|
61
|
+
</linearGradient>
|
|
62
|
+
</defs>
|
|
63
|
+
<circle
|
|
64
|
+
cx="25"
|
|
65
|
+
cy="25"
|
|
66
|
+
r="20"
|
|
67
|
+
stroke="url(#spinner-gradient)"
|
|
68
|
+
strokeWidth="5"
|
|
69
|
+
fill="none"
|
|
70
|
+
strokeLinecap="round"
|
|
71
|
+
/>
|
|
72
|
+
</svg>
|
|
73
|
+
)
|
|
41
74
|
}
|
|
@@ -3,19 +3,21 @@ import { UserPermissionProvider } from "@vertesia/ui/features";
|
|
|
3
3
|
import { UserSessionProvider } from "@vertesia/ui/session";
|
|
4
4
|
import { SplashScreen } from "./SplashScreen";
|
|
5
5
|
import { SigninScreen } from "./login/SigninScreen";
|
|
6
|
+
import { ReactNode } from "react";
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
interface VertesiaShellProps {
|
|
9
10
|
children: React.ReactNode;
|
|
10
11
|
lightLogo?: string;
|
|
11
12
|
darkLogo?: string;
|
|
13
|
+
loadingIcon?: ReactNode;
|
|
12
14
|
}
|
|
13
|
-
export function VertesiaShell({ children, lightLogo, darkLogo }: VertesiaShellProps) {
|
|
15
|
+
export function VertesiaShell({ children, lightLogo, darkLogo, loadingIcon }: VertesiaShellProps) {
|
|
14
16
|
return (
|
|
15
17
|
<ToastProvider>
|
|
16
18
|
<UserSessionProvider>
|
|
17
19
|
<ThemeProvider defaultTheme="system" storageKey="vite-ui-theme">
|
|
18
|
-
<SplashScreen />
|
|
20
|
+
<SplashScreen icon={loadingIcon} />
|
|
19
21
|
<SigninScreen allowedPrefix="/shared/" lightLogo={lightLogo} darkLogo={darkLogo} />
|
|
20
22
|
<UserPermissionProvider>
|
|
21
23
|
{children}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ErrorBox } from "@vertesia/ui/core";
|
|
2
|
+
import { useUserSession } from "@vertesia/ui/session";
|
|
3
|
+
import { ReactNode, useEffect, useState } from "react";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export function CheckAppAccess({ name, children }: { name: string, children: ReactNode }) {
|
|
7
|
+
const [hasAccess, setHasAccess] = useState<boolean | null>(null);
|
|
8
|
+
const { authToken } = useUserSession();
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
if (authToken) {
|
|
12
|
+
setHasAccess(authToken.apps?.includes(name) || false);
|
|
13
|
+
}
|
|
14
|
+
}, [authToken])
|
|
15
|
+
|
|
16
|
+
if (hasAccess == null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (hasAccess) {
|
|
21
|
+
return children;
|
|
22
|
+
} else {
|
|
23
|
+
return <ErrorBox title="Forbidden">Access is not granted to this application</ErrorBox>
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./CheckAppAccess";
|
package/src/shell/index.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TransientToken, UserInviteTokenData } from "@vertesia/common"
|
|
2
|
-
import { Button,
|
|
2
|
+
import { Button, VModal, VModalBody, VModalTitle } from "@vertesia/ui/core"
|
|
3
3
|
import { useEffect, useState } from "react"
|
|
4
4
|
import { useUserSession } from "@vertesia/ui/session"
|
|
5
5
|
|
|
@@ -14,15 +14,22 @@ export function InviteAcceptModal() {
|
|
|
14
14
|
useEffect(() => {
|
|
15
15
|
client.account.listInvites().then(invites => {
|
|
16
16
|
if (invites.length > 0) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
// Filter out legacy invites that do not have account data
|
|
18
|
+
const notLegacyInvites = invites.filter(i => i.data.account);
|
|
19
|
+
if (notLegacyInvites.length === 0) {
|
|
20
|
+
console.log("No valid invites found, closing modal")
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// If we have valid invites, show the modal
|
|
24
|
+
console.log("Found valid invites", notLegacyInvites.length)
|
|
24
25
|
setShowModal(true);
|
|
26
|
+
setInvites(notLegacyInvites);
|
|
27
|
+
} else {
|
|
28
|
+
console.log("No invites found, closing modal")
|
|
29
|
+
setShowModal(false);
|
|
25
30
|
}
|
|
31
|
+
}).catch(err => {
|
|
32
|
+
console.error("Error fetching invites", err);
|
|
26
33
|
})
|
|
27
34
|
}, [account?.id])
|
|
28
35
|
|
|
@@ -32,8 +39,11 @@ export function InviteAcceptModal() {
|
|
|
32
39
|
await client.account.acceptInvite(invite.id);
|
|
33
40
|
await session.fetchAccounts();
|
|
34
41
|
const remainingInvites = invites.filter(i => i.id !== invite.id)
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
|
|
43
|
+
const notLegacyInvites = remainingInvites.filter(i => i.data.account);
|
|
44
|
+
if (notLegacyInvites.length > 0) {
|
|
45
|
+
setInvites(notLegacyInvites);
|
|
46
|
+
} else {
|
|
37
47
|
closeModal();
|
|
38
48
|
}
|
|
39
49
|
}
|
|
@@ -47,31 +57,37 @@ export function InviteAcceptModal() {
|
|
|
47
57
|
}
|
|
48
58
|
}
|
|
49
59
|
|
|
50
|
-
const inviteList = invites.map(invite =>
|
|
51
|
-
|
|
60
|
+
const inviteList = invites.map(invite => {
|
|
61
|
+
if (!invite.data.account) {
|
|
62
|
+
console.warn("Invite has no account data", invite);
|
|
63
|
+
return null; // Skip rendering this invite
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return (<div key={invite.id} className="flex flex-row w-full justify-between border rounded-sm px-2 py-2 ">
|
|
52
67
|
<div className="flex flex-col">
|
|
53
|
-
<div className="w-full font-semibold">{invite.data.account.name}</div>
|
|
68
|
+
<div className="w-full font-semibold">{invite.data.account.name ?? invite.data.account}</div>
|
|
69
|
+
{invite.data.project && <div className="w-full text-base">- {invite.data.project.name}</div>}
|
|
54
70
|
<div className="text-xs">Role: {invite.data.role}</div>
|
|
55
71
|
<div className="text-xs">by {invite.data.invitedBy.name}</div>
|
|
56
72
|
</div>
|
|
57
73
|
<div className="flex flex-col gap-4">
|
|
58
74
|
<Button size={'xs'} onClick={() => accept(invite)}>Accept</Button> <Button size={'xs'} variant="secondary" onClick={() => reject(invite)}>Reject</Button>
|
|
59
75
|
</div>
|
|
60
|
-
</div>
|
|
61
|
-
)
|
|
76
|
+
</div>)
|
|
77
|
+
})
|
|
62
78
|
|
|
63
79
|
return (
|
|
64
80
|
<div>
|
|
65
|
-
<
|
|
66
|
-
<
|
|
67
|
-
<
|
|
81
|
+
<VModal isOpen={showModal} onClose={closeModal}>
|
|
82
|
+
<VModalTitle>Review Invites</VModalTitle>
|
|
83
|
+
<VModalBody>
|
|
68
84
|
<div className="text-sm pb-4">
|
|
69
85
|
You have received the following invites to join other accounts.
|
|
70
86
|
Please review and accept or declined them.
|
|
71
87
|
</div>
|
|
72
88
|
{inviteList}
|
|
73
|
-
</
|
|
74
|
-
</
|
|
89
|
+
</VModalBody>
|
|
90
|
+
</VModal>
|
|
75
91
|
</div>
|
|
76
92
|
)
|
|
77
93
|
|