@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.
Files changed (69) hide show
  1. package/lib/esm/core/components/shadcn/index.js +2 -2
  2. package/lib/esm/core/components/shadcn/index.js.map +1 -1
  3. package/lib/esm/core/components/shadcn/tabs.js +13 -8
  4. package/lib/esm/core/components/shadcn/tabs.js.map +1 -1
  5. package/lib/esm/core/components/shadcn/theme/ThemeProvider.js +1 -0
  6. package/lib/esm/core/components/shadcn/theme/ThemeProvider.js.map +1 -1
  7. package/lib/esm/features/permissions/UserPermissionsProvider.js +1 -0
  8. package/lib/esm/features/permissions/UserPermissionsProvider.js.map +1 -1
  9. package/lib/esm/session/auth/useCurrentTenant.js +63 -0
  10. package/lib/esm/session/auth/useCurrentTenant.js.map +1 -0
  11. package/lib/esm/session/index.js +1 -0
  12. package/lib/esm/session/index.js.map +1 -1
  13. package/lib/esm/shell/SplashScreen.js +10 -3
  14. package/lib/esm/shell/SplashScreen.js.map +1 -1
  15. package/lib/esm/shell/VertesiaShell.js +2 -2
  16. package/lib/esm/shell/VertesiaShell.js.map +1 -1
  17. package/lib/esm/shell/apps/CheckAppAccess.js +23 -0
  18. package/lib/esm/shell/apps/CheckAppAccess.js.map +1 -0
  19. package/lib/esm/shell/apps/index.js +2 -0
  20. package/lib/esm/shell/apps/index.js.map +1 -0
  21. package/lib/esm/shell/index.js +1 -0
  22. package/lib/esm/shell/index.js.map +1 -1
  23. package/lib/esm/shell/login/InviteAcceptModal.js +29 -12
  24. package/lib/esm/shell/login/InviteAcceptModal.js.map +1 -1
  25. package/lib/tsconfig.tsbuildinfo +1 -1
  26. package/lib/types/core/components/shadcn/index.d.ts +2 -2
  27. package/lib/types/core/components/shadcn/index.d.ts.map +1 -1
  28. package/lib/types/core/components/shadcn/tabs.d.ts +4 -1
  29. package/lib/types/core/components/shadcn/tabs.d.ts.map +1 -1
  30. package/lib/types/core/components/shadcn/theme/ThemeProvider.d.ts +2 -1
  31. package/lib/types/core/components/shadcn/theme/ThemeProvider.d.ts.map +1 -1
  32. package/lib/types/features/permissions/UserPermissionsProvider.d.ts +2 -1
  33. package/lib/types/features/permissions/UserPermissionsProvider.d.ts.map +1 -1
  34. package/lib/types/session/auth/useCurrentTenant.d.ts +15 -0
  35. package/lib/types/session/auth/useCurrentTenant.d.ts.map +1 -0
  36. package/lib/types/session/index.d.ts +1 -0
  37. package/lib/types/session/index.d.ts.map +1 -1
  38. package/lib/types/shell/SplashScreen.d.ts +3 -1
  39. package/lib/types/shell/SplashScreen.d.ts.map +1 -1
  40. package/lib/types/shell/VertesiaShell.d.ts +3 -1
  41. package/lib/types/shell/VertesiaShell.d.ts.map +1 -1
  42. package/lib/types/shell/apps/CheckAppAccess.d.ts +6 -0
  43. package/lib/types/shell/apps/CheckAppAccess.d.ts.map +1 -0
  44. package/lib/types/shell/apps/index.d.ts +2 -0
  45. package/lib/types/shell/apps/index.d.ts.map +1 -0
  46. package/lib/types/shell/index.d.ts +1 -0
  47. package/lib/types/shell/index.d.ts.map +1 -1
  48. package/lib/types/shell/login/InviteAcceptModal.d.ts.map +1 -1
  49. package/lib/vertesia-ui-core.js +1 -1
  50. package/lib/vertesia-ui-core.js.map +1 -1
  51. package/lib/vertesia-ui-features.js +1 -1
  52. package/lib/vertesia-ui-features.js.map +1 -1
  53. package/lib/vertesia-ui-session.js +1 -1
  54. package/lib/vertesia-ui-session.js.map +1 -1
  55. package/lib/vertesia-ui-shell.js +1 -1
  56. package/lib/vertesia-ui-shell.js.map +1 -1
  57. package/package.json +4 -4
  58. package/src/core/components/shadcn/index.ts +2 -2
  59. package/src/core/components/shadcn/tabs.tsx +31 -14
  60. package/src/core/components/shadcn/theme/ThemeProvider.tsx +2 -0
  61. package/src/features/permissions/UserPermissionsProvider.tsx +1 -0
  62. package/src/session/auth/useCurrentTenant.ts +73 -0
  63. package/src/session/index.ts +1 -0
  64. package/src/shell/SplashScreen.tsx +36 -3
  65. package/src/shell/VertesiaShell.tsx +4 -2
  66. package/src/shell/apps/CheckAppAccess.tsx +25 -0
  67. package/src/shell/apps/index.ts +1 -0
  68. package/src/shell/index.tsx +1 -0
  69. 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
- <img src='/icon.svg' className='w-10 h-auto animate-pulse rounded-full' />
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";
@@ -4,3 +4,4 @@ export * from "./login/TerminalLogin";
4
4
  export * from "./login/UserSessionMenu";
5
5
  export * from "./utils";
6
6
  export * from './VertesiaShell';
7
+ export * from "./apps/index";
@@ -1,5 +1,5 @@
1
1
  import { TransientToken, UserInviteTokenData } from "@vertesia/common"
2
- import { Button, Modal, ModalBody, ModalTitle } from "@vertesia/ui/core"
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
- console.log("Got invites - showing modal")
18
- setInvites(invites)
19
- /*toast({
20
- status: 'info',
21
- title: 'You have pending invites',
22
- description: 'Click the button on the top right to review them.'
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
- setInvites(remainingInvites)
36
- if (remainingInvites.length === 0) {
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
- <div key={invite.id} className="flex flex-row w-full justify-between border rounded-sm px-2 py-2 ">
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
- <Modal isOpen={showModal} onClose={closeModal}>
66
- <ModalTitle>Review Invites</ModalTitle>
67
- <ModalBody>
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
- </ModalBody>
74
- </Modal>
89
+ </VModalBody>
90
+ </VModal>
75
91
  </div>
76
92
  )
77
93