From 83148ae02740f07082a95c76bade7efc7f319f66 Mon Sep 17 00:00:00 2001 From: Dimitri POSTOLOV Date: Fri, 7 Mar 2025 02:55:52 +0100 Subject: [PATCH] all in one --- .gitignore | 1 + packages/xy-ui/components/ui/select.tsx | 4 +- pnpm-lock.yaml | 923 +++++++++++++++++- sites/reactflow.dev/.env.development | 2 + sites/reactflow.dev/.env.production | 2 + sites/reactflow.dev/next.config.ts | 5 +- sites/reactflow.dev/package.json | 15 +- .../(auth)/email-verification/page.tsx | 35 + .../email-verification/resend-link/page.tsx | 19 + .../(auth)/email-verification/verify/page.tsx | 24 + .../src/app/(content-pages)/(auth)/layout.tsx | 30 + .../(auth)/reset-password/page.tsx | 19 + .../(auth)/signin/magic-link/page.tsx | 19 + .../(content-pages)/(auth)/signin/page.tsx | 18 + .../(content-pages)/(auth)/signup/page.tsx | 22 + .../dashboard/account/_cards/billing.tsx | 29 + .../dashboard/account/_cards/change-email.tsx | 73 ++ .../account/_cards/change-password.tsx | 75 ++ .../account/_cards/delete-account.tsx | 120 +++ .../dashboard/account/page.tsx | 25 + .../dashboard/auth-protected.tsx | 24 + .../examples/[framework]/[id]/page.tsx | 44 + .../dashboard/examples/[framework]/page.tsx | 58 ++ .../dashboard/examples/page.tsx | 21 + .../app/(content-pages)/dashboard/layout.tsx | 21 + .../app/(content-pages)/dashboard/page.tsx | 79 ++ .../subscribe/non-commercial-edu-oss/page.tsx | 26 + .../non-commercial-edu-oss/signup.tsx | 179 ++++ .../dashboard/subscribe/page.tsx | 17 + .../dashboard/support/page.tsx | 96 ++ .../dashboard/team/_cards/manage-team.tsx | 290 ++++++ .../(content-pages)/dashboard/team/page.tsx | 21 + .../dashboard/templates/[id]/page.tsx | 44 + .../dashboard/templates/page.tsx | 20 + sites/reactflow.dev/src/app/_meta.global.ts | 6 + sites/reactflow.dev/src/app/global.css | 37 +- sites/reactflow.dev/src/app/layout.tsx | 4 +- sites/reactflow.dev/src/app/pro/layout.tsx | 10 - sites/reactflow.dev/src/components/navbar.tsx | 24 + .../src/components/nextra-layout.tsx | 22 +- .../pro/AuthForms/AuthFormWrapper.tsx | 94 ++ .../pro/AuthForms/AuthNotification.tsx | 130 +++ .../pro/AuthForms/ResendVerificationLink.tsx | 56 ++ .../pro/AuthForms/ResetPassword.tsx | 54 + .../pro/AuthForms/SignInEmailPassword.tsx | 107 ++ .../pro/AuthForms/SignInMagicLink.tsx | 67 ++ .../components/pro/AuthForms/SignInOAuth.tsx | 20 + .../pro/AuthForms/SignUpEmailPassword.tsx | 104 ++ .../src/components/pro/AuthForms/index.tsx | 7 + .../pro/CustomerPortalButton/index.tsx | 10 + .../components/pro/DashboardHeader/index.tsx | 45 + .../pro/DashboardHeader/subscription-plan.tsx | 26 + .../src/components/pro/ExamplesGrid/index.tsx | 27 + .../components/pro/ExamplesGrid/teaser.tsx | 54 + .../src/components/pro/Fathom/index.tsx | 29 + .../src/components/pro/Footer/index.tsx | 43 + .../src/components/pro/Icons/index.tsx | 22 + .../src/components/pro/Loader/index.tsx | 45 + .../components/pro/Loader/loader.module.css | 121 +++ .../src/components/pro/Logo/index.tsx | 95 ++ .../src/components/pro/Navigation/NavMenu.tsx | 37 + .../components/pro/Navigation/UserMenu.tsx | 70 ++ .../src/components/pro/Notification/index.tsx | 36 + .../pro/Notification/not-subscribed.tsx | 22 + .../src/components/pro/Pill/index.tsx | 19 + .../pro/PricingTable/PricingPlan.tsx | 76 ++ .../src/components/pro/PricingTable/index.tsx | 64 ++ .../pro/ProExampleViewer/download-button.tsx | 64 ++ .../components/pro/ProExampleViewer/index.tsx | 83 ++ .../pro/ProExampleViewer/overview-button.tsx | 19 + .../pro/ProExampleViewer/tabs/editor.tsx | 55 ++ .../pro/ProExampleViewer/tabs/index.tsx | 145 +++ .../pro/ProExampleViewer/tabs/markdown.tsx | 123 +++ .../pro/ProExampleViewer/tabs/preview.tsx | 16 + .../pro/ProExampleViewer/variant-select.tsx | 47 + .../Providers/SubscriptionProvider/index.tsx | 60 ++ .../components/pro/Providers/index.client.ts | 4 + .../src/components/pro/Providers/index.tsx | 23 + .../components/pro/Sidebar/SidebarItem.tsx | 47 + .../src/components/pro/Sidebar/index.tsx | 64 ++ .../pro/SubscriptionFeature/index.tsx | 121 +++ .../pro/SubscriptionStatus/index.tsx | 40 + .../src/fonts/NTDapper-black.woff2 | Bin 0 -> 25336 bytes .../src/fonts/NTDapper-bold.woff2 | Bin 0 -> 25652 bytes .../src/fonts/NTDapper-extralight.woff2 | Bin 0 -> 25300 bytes .../src/fonts/NTDapper-light.woff2 | Bin 0 -> 25128 bytes .../src/fonts/NTDapper-medium.woff2 | Bin 0 -> 25584 bytes .../src/fonts/NTDapper-regular.woff2 | Bin 0 -> 24676 bytes .../src/fonts/NTDapper-thin.woff2 | Bin 0 -> 24828 bytes sites/reactflow.dev/src/fonts/index.ts | 22 + .../src/hooks/useDownloadExample.ts | 44 + .../src/hooks/useNhostFunction.ts | 34 + .../reactflow.dev/src/hooks/useQueryString.ts | 6 + .../src/hooks/useStripeCustomerPortal.ts | 22 + .../src/hooks/useSubscription.ts | 47 + sites/reactflow.dev/src/middleware.ts | 27 + sites/reactflow.dev/src/types/index.ts | 46 + sites/reactflow.dev/src/types/raw-loader.d.ts | 4 + sites/reactflow.dev/src/utils/nhost.ts | 35 + sites/reactflow.dev/src/utils/pro-utils.ts | 44 + 100 files changed, 5135 insertions(+), 90 deletions(-) create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/resend-link/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/verify/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/layout.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/reset-password/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/magic-link/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/(auth)/signup/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/billing.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-email.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-password.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/delete-account.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/account/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/auth-protected.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/[id]/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/layout.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/signup.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/support/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/team/_cards/manage-team.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/team/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/[id]/page.tsx create mode 100644 sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/page.tsx create mode 100644 sites/reactflow.dev/src/components/navbar.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/AuthFormWrapper.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/AuthNotification.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/ResendVerificationLink.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/ResetPassword.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/SignInEmailPassword.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/SignInMagicLink.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/SignInOAuth.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/SignUpEmailPassword.tsx create mode 100644 sites/reactflow.dev/src/components/pro/AuthForms/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/CustomerPortalButton/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/DashboardHeader/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/DashboardHeader/subscription-plan.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ExamplesGrid/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ExamplesGrid/teaser.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Fathom/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Footer/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Icons/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Loader/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Loader/loader.module.css create mode 100644 sites/reactflow.dev/src/components/pro/Logo/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Navigation/NavMenu.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Navigation/UserMenu.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Notification/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Notification/not-subscribed.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Pill/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/PricingTable/PricingPlan.tsx create mode 100644 sites/reactflow.dev/src/components/pro/PricingTable/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/download-button.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/overview-button.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/editor.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/markdown.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/preview.tsx create mode 100644 sites/reactflow.dev/src/components/pro/ProExampleViewer/variant-select.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Providers/SubscriptionProvider/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Providers/index.client.ts create mode 100644 sites/reactflow.dev/src/components/pro/Providers/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Sidebar/SidebarItem.tsx create mode 100644 sites/reactflow.dev/src/components/pro/Sidebar/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/SubscriptionFeature/index.tsx create mode 100644 sites/reactflow.dev/src/components/pro/SubscriptionStatus/index.tsx create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-black.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-bold.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-extralight.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-light.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-medium.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-regular.woff2 create mode 100644 sites/reactflow.dev/src/fonts/NTDapper-thin.woff2 create mode 100644 sites/reactflow.dev/src/fonts/index.ts create mode 100644 sites/reactflow.dev/src/hooks/useDownloadExample.ts create mode 100644 sites/reactflow.dev/src/hooks/useNhostFunction.ts create mode 100644 sites/reactflow.dev/src/hooks/useQueryString.ts create mode 100644 sites/reactflow.dev/src/hooks/useStripeCustomerPortal.ts create mode 100644 sites/reactflow.dev/src/hooks/useSubscription.ts create mode 100644 sites/reactflow.dev/src/middleware.ts create mode 100644 sites/reactflow.dev/src/types/index.ts create mode 100644 sites/reactflow.dev/src/types/raw-loader.d.ts create mode 100644 sites/reactflow.dev/src/utils/nhost.ts create mode 100644 sites/reactflow.dev/src/utils/pro-utils.ts diff --git a/.gitignore b/.gitignore index 23ada77ee..c682d83c6 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,4 @@ yarn-error.log* tmp vite.config.ts.* .idea/ +tsconfig.tsbuildinfo diff --git a/packages/xy-ui/components/ui/select.tsx b/packages/xy-ui/components/ui/select.tsx index 889c6aa13..830de8892 100644 --- a/packages/xy-ui/components/ui/select.tsx +++ b/packages/xy-ui/components/ui/select.tsx @@ -40,7 +40,7 @@ const SelectContent = React.forwardRef< {children} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8196abec8..c6f7e4df9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -507,9 +507,27 @@ importers: sites/reactflow.dev: dependencies: + '@apollo/client': + specifier: 3.7.15 + version: 3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@codesandbox/sandpack-react': + specifier: 2.6.7 + version: 2.6.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@codesandbox/sandpack-themes': + specifier: 2.0.21 + version: 2.0.21 '@heroicons/react': specifier: ^2.1.5 version: 2.2.0(react@18.3.1) + '@nhost/nhost-js': + specifier: ^3.2.5 + version: 3.2.5(graphql@16.8.1) + '@nhost/react': + specifier: ^3.10.1 + version: 3.10.1(@types/react@18.3.17)(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@nhost/react-apollo': + specifier: ^17.0.1 + version: 17.0.1(@apollo/client@3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nhost/nhost-js@3.2.5(graphql@16.8.1))(@nhost/react@3.10.1(@types/react@18.3.17)(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@typeform/embed-react': specifier: ^3.9.0 version: 3.20.0(react@18.3.1) @@ -522,18 +540,27 @@ importers: d3: specifier: ^7.9.0 version: 7.9.0 + fathom-client: + specifier: 3.5.0 + version: 3.5.0 + file-saver: + specifier: 2.0.5 + version: 2.0.5 + jszip: + specifier: 3.10.1 + version: 3.10.1 next: - specifier: ^15.1.7 - version: 15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^15.2.1 + version: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-sitemap: specifier: ^4.2.3 - version: 4.2.3(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 4.2.3(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) nextra: specifier: ^4.2.13 - version: 4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) + version: 4.2.13(acorn@8.14.0)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) nextra-theme-docs: specifier: ^4.2.13 - version: 4.2.13(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) + version: 4.2.14(@types/react@18.3.17)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -543,6 +570,15 @@ importers: react-icons: specifier: ^4.10.1 version: 4.12.0(react@18.3.1) + react-markdown: + specifier: 9.0.0 + version: 9.0.0(@types/react@18.3.17)(react@18.3.1) + react-syntax-highlighter: + specifier: 15.5.0 + version: 15.5.0(react@18.3.1) + remark-gfm: + specifier: 4.0.0 + version: 4.0.0 timeago-react: specifier: ^3.0.6 version: 3.0.6(react@18.3.1) @@ -615,7 +651,7 @@ importers: version: 4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) nextra-theme-docs: specifier: ^4.2.13 - version: 4.2.13(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) + version: 4.2.14(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -685,7 +721,7 @@ importers: version: 4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) nextra-theme-docs: specifier: ^4.2.13 - version: 4.2.13(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) + version: 4.2.14(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) react: specifier: ^18.3.1 version: 18.3.1 @@ -815,6 +851,24 @@ packages: '@antfu/utils@0.7.10': resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==} + '@apollo/client@3.7.15': + resolution: {integrity: sha512-pLScjo4GAQRWKWyEg2J3FlQr9fbUAuADT0EI2+JlLf2BjaU9I7WUaZVD9w+0qNPE8BZqs53MRQq0OCm1QCW+eg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + graphql-ws: ^5.5.5 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + subscriptions-transport-ws: ^0.9.0 || ^0.11.0 + peerDependenciesMeta: + graphql-ws: + optional: true + react: + optional: true + react-dom: + optional: true + subscriptions-transport-ws: + optional: true + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -959,6 +1013,15 @@ packages: react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 + '@codesandbox/sandpack-react@2.6.7': + resolution: {integrity: sha512-BgFXzd5xYxrnzD70cShlBRJpa2zXuPSW6qc4Ei6wXwEvpwFKlOSdUD/F0pmBnZpw7SrZ4JUn8iLPtyy0IheZXw==} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + + '@codesandbox/sandpack-themes@2.0.21': + resolution: {integrity: sha512-CMH/MO/dh6foPYb/3eSn2Cu/J3+1+/81Fsaj7VggICkCrmRk0qG5dmgjGAearPTnRkOGORIPHuRqwNXgw0E6YQ==} + '@corex/deepmerge@4.0.43': resolution: {integrity: sha512-N8uEMrMPL0cu/bdboEWpQYb/0i2K5Qn8eCsxzOmxSggJbbQte7ljMRoXm917AbntqTGOzdTu+vP3KOOzoC70HQ==} @@ -1189,6 +1252,11 @@ packages: '@formatjs/intl-localematcher@0.6.0': resolution: {integrity: sha512-4rB4g+3hESy1bHSBG3tDFaMY2CH67iT7yne1e+0CLTsGLDcmoEWWpJjjpWVaYgYfYuohIRuo0E+N536gd2ZHZA==} + '@graphql-typed-document-node/core@3.2.0': + resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} + peerDependencies: + graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + '@headlessui/react@2.2.0': resolution: {integrity: sha512-RzCEg+LXsuI7mHiSomsu/gBJSjpupm6A1qIZ5sWjd7JhARNlMiSA4kKfJpCKwU9tE+zMRterhhrP74PvfJrpXQ==} engines: {node: '>=10'} @@ -1478,6 +1546,9 @@ packages: '@next/env@15.1.7': resolution: {integrity: sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==} + '@next/env@15.2.1': + resolution: {integrity: sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q==} + '@next/eslint-plugin-next@14.2.15': resolution: {integrity: sha512-pKU0iqKRBlFB/ocOI1Ip2CkKePZpYpnw5bEItEkuZ/Nr9FQP1+p7VDWr4VfOdff4i9bFmrOaeaU1bFEyAcxiMQ==} @@ -1493,6 +1564,12 @@ packages: cpu: [arm64] os: [darwin] + '@next/swc-darwin-arm64@15.2.1': + resolution: {integrity: sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + '@next/swc-darwin-x64@14.2.15': resolution: {integrity: sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==} engines: {node: '>= 10'} @@ -1505,6 +1582,12 @@ packages: cpu: [x64] os: [darwin] + '@next/swc-darwin-x64@15.2.1': + resolution: {integrity: sha512-E/w8ervu4fcG5SkLhvn1NE/2POuDCDEy5gFbfhmnYXkyONZR68qbUlJlZwuN82o7BrBVAw+tkR8nTIjGiMW1jQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + '@next/swc-linux-arm64-gnu@14.2.15': resolution: {integrity: sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==} engines: {node: '>= 10'} @@ -1517,6 +1600,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-gnu@15.2.1': + resolution: {integrity: sha512-gXDX5lIboebbjhiMT6kFgu4svQyjoSed6dHyjx5uZsjlvTwOAnZpn13w9XDaIMFFHw7K8CpBK7HfDKw0VZvUXQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-arm64-musl@14.2.15': resolution: {integrity: sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==} engines: {node: '>= 10'} @@ -1529,6 +1618,12 @@ packages: cpu: [arm64] os: [linux] + '@next/swc-linux-arm64-musl@15.2.1': + resolution: {integrity: sha512-3v0pF/adKZkBWfUffmB/ROa+QcNTrnmYG4/SS+r52HPwAK479XcWoES2I+7F7lcbqc7mTeVXrIvb4h6rR/iDKg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + '@next/swc-linux-x64-gnu@14.2.15': resolution: {integrity: sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==} engines: {node: '>= 10'} @@ -1541,6 +1636,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-gnu@15.2.1': + resolution: {integrity: sha512-RbsVq2iB6KFJRZ2cHrU67jLVLKeuOIhnQB05ygu5fCNgg8oTewxweJE8XlLV+Ii6Y6u4EHwETdUiRNXIAfpBww==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-linux-x64-musl@14.2.15': resolution: {integrity: sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==} engines: {node: '>= 10'} @@ -1553,6 +1654,12 @@ packages: cpu: [x64] os: [linux] + '@next/swc-linux-x64-musl@15.2.1': + resolution: {integrity: sha512-QHsMLAyAIu6/fWjHmkN/F78EFPKmhQlyX5C8pRIS2RwVA7z+t9cTb0IaYWC3EHLOTjsU7MNQW+n2xGXr11QPpg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + '@next/swc-win32-arm64-msvc@14.2.15': resolution: {integrity: sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==} engines: {node: '>= 10'} @@ -1565,6 +1672,12 @@ packages: cpu: [arm64] os: [win32] + '@next/swc-win32-arm64-msvc@15.2.1': + resolution: {integrity: sha512-Gk42XZXo1cE89i3hPLa/9KZ8OuupTjkDmhLaMKFohjf9brOeZVEa3BQy1J9s9TWUqPhgAEbwv6B2+ciGfe54Vw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + '@next/swc-win32-ia32-msvc@14.2.15': resolution: {integrity: sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==} engines: {node: '>= 10'} @@ -1583,6 +1696,49 @@ packages: cpu: [x64] os: [win32] + '@next/swc-win32-x64-msvc@15.2.1': + resolution: {integrity: sha512-YjqXCl8QGhVlMR8uBftWk0iTmvtntr41PhG1kvzGp0sUP/5ehTM+cwx25hKE54J0CRnHYjSGjSH3gkHEaHIN9g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nhost/apollo@8.0.5': + resolution: {integrity: sha512-gOKOoYA6c8TDIGKza5vATXSWE4hRmdknc6Q+gTajhL/M/678t4/6gUv0Oy77YCoWF9EqOF/SLZO3kDSiVK9kbQ==} + peerDependencies: + '@apollo/client': ^3.7.10 + '@nhost/nhost-js': 3.2.5 + + '@nhost/graphql-js@0.3.0': + resolution: {integrity: sha512-CVYq6wx0VbaYdpUBmfNO/6mZatHB5+YBCqFjWyxhpN1nzHCHEO6rgdL7j0qk31OFE6XAX0z7AQZSXg1Pn63GUw==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + + '@nhost/hasura-auth-js@2.10.1': + resolution: {integrity: sha512-zTM3F3fkSsSej+FYlrQ9mIHmKoflS9xMRrYnhTCT6u8e7H3uE3l+J/NALZFrqeQgdeILiiwA8s5YaIzJOfEyww==} + + '@nhost/hasura-storage-js@2.7.0': + resolution: {integrity: sha512-idP3d71GB5V5fXU6rWVpiaL3kJr7qs9P48DZMPtpI4xoAxY7t0RgJHwlsbYsNIY6n1f2qJRgAiWGQClAQ8eyKA==} + + '@nhost/nhost-js@3.2.5': + resolution: {integrity: sha512-cmGtssEp7NlLGPCYCWZWddz8eQmsIhPCyLN392ygXvI4u9F6HgMvhVf3aCHtdm5fuxvfKpjwiUOmwAb186ohwg==} + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 + + '@nhost/react-apollo@17.0.1': + resolution: {integrity: sha512-mfUyy/oUiu61tiW7sVoY2BoZti7JzQ9Z8NhwVOM/nDV0NiwDsmhiOTQMOnhSwMBATQIdJFHvDDAZpn870MToGQ==} + peerDependencies: + '@apollo/client': ^3.7.10 + '@nhost/react': 3.10.1 + graphql: ^16.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + + '@nhost/react@3.10.1': + resolution: {integrity: sha512-aZXw8/luL1yS8yhbFpdLDuVWeJPgAKjEN/1J9yp6JuZADSzWuM2Z0BxOw+PjV1eEy1IDbHzx/fhZ+rcQ2csHQA==} + peerDependencies: + react: ^17.0.0 || ^18.1.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.1.0 || ^19.0.0 + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -2275,6 +2431,12 @@ packages: '@shikijs/vscode-textmate@10.0.1': resolution: {integrity: sha512-fTIQwLF+Qhuws31iw7Ncl1R3HUDtGwIipiJ9iU+UsDUwMhegFcQKQHd51nZjb7CArq0MvON8rbgCGQYWHUKAdg==} + '@simplewebauthn/browser@9.0.1': + resolution: {integrity: sha512-wD2WpbkaEP4170s13/HUxPcAV5y4ZXaKo1TfNklS5zDefPinIgXOpgz1kpEvobAsaLPa2KeH7AKKX/od1mrBJw==} + + '@simplewebauthn/types@9.0.1': + resolution: {integrity: sha512-tGSRP1QvsAvsJmnOlRQyw/mvK9gnPtjEc5fg2+m8n+QUa+D7rvrKkOYyfpy42GTs90X3RDOnqJgfHt+qO67/+w==} + '@sindresorhus/slugify@2.2.1': resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==} engines: {node: '>=12'} @@ -2515,6 +2677,9 @@ packages: '@types/glob@7.2.0': resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==} + '@types/hast@2.3.10': + resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} + '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} @@ -2706,6 +2871,34 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@wry/context@0.7.4': + resolution: {integrity: sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==} + engines: {node: '>=8'} + + '@wry/equality@0.5.7': + resolution: {integrity: sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==} + engines: {node: '>=8'} + + '@wry/trie@0.3.2': + resolution: {integrity: sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==} + engines: {node: '>=8'} + + '@wry/trie@0.4.3': + resolution: {integrity: sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==} + engines: {node: '>=8'} + + '@xstate/react@3.2.2': + resolution: {integrity: sha512-feghXWLedyq8JeL13yda3XnHPZKwYDN5HPBLykpLeuNpr9178tQd2/3d0NrH6gSd0sG5mLuLeuD+ck830fgzLQ==} + peerDependencies: + '@xstate/fsm': ^2.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + xstate: ^4.37.2 + peerDependenciesMeta: + '@xstate/fsm': + optional: true + xstate: + optional: true + '@xyflow/react@12.3.2': resolution: {integrity: sha512-+bK3L61BDIvUX++jMiEqIjy5hIIyVmfeiUavpeOZIYKwg6NW0pR5EnHJM2JFfkVqZisFauzS9EgmI+tvTqx9Qw==} peerDependencies: @@ -2718,12 +2911,6 @@ packages: react: '>=17' react-dom: '>=17' - '@xyflow/react@12.4.3': - resolution: {integrity: sha512-oO50TIY4rbgOURK5pmvL4LwLOQdh6YkvrvOBZPBedltJ1TINCRp0FiyYKfYhLnaDcW8/aayvGtFpUcSkPQxpGg==} - peerDependencies: - react: '>=17' - react-dom: '>=17' - '@xyflow/react@12.4.4': resolution: {integrity: sha512-9RZ9dgKZNJOlbrXXST5HPb5TcXPOIDGondjwcjDro44OQRPl1E0ZRPTeWPGaQtVjbg4WpR4BUYwOeshNI2TuVg==} peerDependencies: @@ -2741,9 +2928,6 @@ packages: '@xyflow/system@0.0.47': resolution: {integrity: sha512-aUXJPIvsCFxGX70ccRG8LPsR+A8ExYXfh/noYNpqn8udKerrLdSHxMG2VsvUrQ1PGex10fOpbJwFU4A+I/Xv8w==} - '@xyflow/system@0.0.51': - resolution: {integrity: sha512-cYnuM3oWQQjx2Rdz0LdZCnbUaWZdZDiik20kPDYsa5SIlq++ZDIcKiDF6a93ncfMv9Ej5GWfDkouE7bObrdRqQ==} - '@xyflow/system@0.0.52': resolution: {integrity: sha512-pJBMaoh/GEebIABWEIxAai0yf57dm+kH7J/Br+LnLFPuJL87Fhcmm4KFWd/bCUy/kCWUg+2/yFAGY0AUHRPOnQ==} @@ -2929,6 +3113,9 @@ packages: bare-stream@2.6.1: resolution: {integrity: sha512-eVZbtKM+4uehzrsj49KtCy3Pbg7kO1pJ3SKZ1SFrIH/0pnj9scuGGgUlNDf/7qS8WKtGdiJY5Kyhs/ivYPTB/g==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -3127,6 +3314,9 @@ packages: code-red@1.0.4: resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + codesandbox-import-util-types@2.2.3: + resolution: {integrity: sha512-Qj00p60oNExthP2oR3vvXmUGjukij+rxJGuiaKM6tyUmSyimdZsqHI/TUvFFClAffk9s7hxGnQgWQ8KCce27qQ==} + collapse-white-space@2.1.0: resolution: {integrity: sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw==} @@ -3218,6 +3408,9 @@ packages: core-js-pure@3.39.0: resolution: {integrity: sha512-7fEcWwKI4rJinnK+wLTezeg2smbFFdSBP6E2kQZNbnzM2s1rpKQ6aaRteZSSg7FLU3P0HGGVo/gbpfanU36urg==} + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} @@ -3968,6 +4161,10 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -3981,9 +4178,15 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fathom-client@3.5.0: + resolution: {integrity: sha512-BiRDS9Q9a8Zma0H717FWC5cvf545K/CsxBpxKT22TcSl1EbRhhlHWIJgrdeiQUfdorBK2ppy09TwMOhRsbos/A==} + fathom-client@3.7.2: resolution: {integrity: sha512-sWtaNivhg7uwp/q1bUuIiNj4LeQZMEZ5NXXFFpZ8le4uDedAfQG84gPOdYehtVXbl+1yX2s8lmXZ2+IQ9a/xxA==} + fault@1.0.4: + resolution: {integrity: sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==} + fault@2.0.1: resolution: {integrity: sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==} @@ -3998,6 +4201,9 @@ packages: picomatch: optional: true + fetch-ponyfill@7.1.0: + resolution: {integrity: sha512-FhbbL55dj/qdVO3YNK7ZEkshvj3eQ7EuIGV2I6ic/2YiocvyWv+7jg2s4AyS0wdRU75s3tA8ZxI/xPigb0v5Aw==} + figures@3.2.0: resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} engines: {node: '>=8'} @@ -4006,6 +4212,9 @@ packages: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} engines: {node: ^10.12.0 || >=12.0.0} + file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -4172,6 +4381,22 @@ packages: graphemer@1.4.0: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphql-tag@2.12.6: + resolution: {integrity: sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==} + engines: {node: '>=10'} + peerDependencies: + graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + + graphql-ws@5.16.2: + resolution: {integrity: sha512-E1uccsZxt/96jH/OwmLPuXMACILs76pKF2i3W861LpKBCYtGIyPQGtWLuBLkND4ox1KHns70e83PS4te50nvPQ==} + engines: {node: '>=10'} + peerDependencies: + graphql: '>=0.11 <=16' + + graphql@16.8.1: + resolution: {integrity: sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + hachure-fill@0.5.2: resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==} @@ -4228,6 +4453,9 @@ packages: hast-util-is-element@3.0.0: resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + hast-util-parse-selector@2.2.5: + resolution: {integrity: sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==} + hast-util-parse-selector@4.0.0: resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==} @@ -4255,15 +4483,27 @@ packages: hast-util-whitespace@3.0.0: resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + hastscript@6.0.0: + resolution: {integrity: sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==} + hastscript@9.0.0: resolution: {integrity: sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw==} header-case@1.0.1: resolution: {integrity: sha512-i0q9mkOeSuhXw6bGgiQCCBgY/jlZuV/7dZXyZ9c6LcBrqwvT8eT719E9uxE5LiZftdl+z81Ugbg/VvXV4OJOeQ==} + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + html-to-image@1.11.11: resolution: {integrity: sha512-9gux8QhvjRO/erSnDPv28noDZcPZmYE7e1vFsBLKLlRlKDSqNJYebj6Qz1TGd5lsRV+X+xYyjCKjuZdABinWjA==} + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} @@ -4305,6 +4545,9 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -4574,6 +4817,9 @@ packages: resolution: {integrity: sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==} engines: {node: '>=18'} + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} @@ -4584,6 +4830,9 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-unfetch@3.1.0: + resolution: {integrity: sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==} + iterator.prototype@1.1.4: resolution: {integrity: sha512-x4WH0BWmrMmg4oHHl+duwubhrvczGlyuGAZu3nvrf0UXOfPu8IhZObFEr7DE/iv01YgVZrsOiRcqw2srkKEDIA==} engines: {node: '>= 0.4'} @@ -4608,6 +4857,10 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4651,6 +4904,13 @@ packages: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + katex@0.16.21: resolution: {integrity: sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==} hasBin: true @@ -4689,6 +4949,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + lightningcss-darwin-arm64@1.29.1: resolution: {integrity: sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==} engines: {node: '>= 12.0.0'} @@ -4807,6 +5070,9 @@ packages: lower-case@1.1.4: resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} + lowlight@1.20.0: + resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -5227,11 +5493,32 @@ packages: sass: optional: true - nextra-theme-docs@4.2.13: - resolution: {integrity: sha512-PO+ltZFtKXe2Jt4hgdJi5JahQHOjt2qmz5wVSfhS+PZDMAuFCa0UubBvkCyQDF3YdKjUI2gJ8CW0fGw84yeseQ==} + next@15.2.1: + resolution: {integrity: sha512-zxbsdQv3OqWXybK5tMkPCBKyhIz63RstJ+NvlfkaLMc/m5MwXgz2e92k+hSKcyBpyADhMk2C31RIiaDjUZae7g==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + nextra-theme-docs@4.2.14: + resolution: {integrity: sha512-Z+eRIRMbssB8kgDjkucfqAd894+lga78cPiAcCD2YQY7VPyFFeYmXJIwpEiLmV3/13FtT1QVoJuMmRaUTjtJIQ==} peerDependencies: next: '>=14' - nextra: 4.2.13 + nextra: 4.2.14 react: '>=18' react-dom: '>=18' @@ -5249,6 +5536,15 @@ packages: no-case@2.3.2: resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} + node-fetch@2.6.13: + resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -5335,6 +5631,9 @@ packages: oniguruma-to-es@3.1.0: resolution: {integrity: sha512-BJ3Jy22YlgejHSO7Fvmz1kKazlaPmRSUH+4adTDUS/dKQ4wLxI+gALZ8updbaux7/m7fIlpgOZ5fp/Inq5jUAw==} + optimism@0.16.2: + resolution: {integrity: sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ==} + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -5380,6 +5679,9 @@ packages: package-manager-detector@0.2.7: resolution: {integrity: sha512-g4+387DXDKlZzHkP+9FLt8yKj8+/3tOkPv7DVTJGGRm00RkEWgqbFstX1mXJ4M0VDYhUqsTOiISqNOJnhAu3PQ==} + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + param-case@2.1.1: resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} @@ -5618,6 +5920,17 @@ packages: engines: {node: '>=14'} hasBin: true + prismjs@1.27.0: + resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} + engines: {node: '>=6'} + + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + progress@2.0.3: resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} engines: {node: '>=0.4.0'} @@ -5718,6 +6031,12 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react-markdown@9.0.0: + resolution: {integrity: sha512-v6yNf3AB8GfJ8lCpUvzxAXKxgsHpdmWPlcVRQ6Nocsezp255E/IDrF31kLQsPJeB/cKto/geUwjU36wH784FCA==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + react-medium-image-zoom@5.2.13: resolution: {integrity: sha512-KcBL4OsoUQJgIFh6vQgt/6sRGqDy6bQBcsbhGD2tsy4B5Pw3dWrboocVOyIm76RRALEZ6Qwp3EDvIvfEv0m5sg==} peerDependencies: @@ -5775,6 +6094,11 @@ packages: '@types/react': optional: true + react-syntax-highlighter@15.5.0: + resolution: {integrity: sha512-+zq2myprEnQmH5yw6Gqc8lD55QHnpKaU8TOcFeC/Lg/MQSs8UknEA0JC4nTZGFAXC2J2Hyj/ijJ7NlabyPi2gg==} + peerDependencies: + react: '>= 0.14.0' + react-use-measure@2.1.7: resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==} peerDependencies: @@ -5791,6 +6115,9 @@ packages: read-cache@1.0.0: resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -5822,6 +6149,9 @@ packages: resolution: {integrity: sha512-B5dj6usc5dkk8uFliwjwDHM8To5/QwdKz9JcBZ8Ic4G1f0YmeeJTtE/ZTdgRFPAfxZFiUaPhZ1Jcs4qeagItGQ==} engines: {node: '>= 0.4'} + refractor@3.6.0: + resolution: {integrity: sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==} + regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -5919,6 +6249,10 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true + response-iterator@0.2.20: + resolution: {integrity: sha512-RfNi9saiJ9VKznrRZEGZtlfeiQI7NWMUlXvmkvO60xaHfW1y+36EOibZkV59LuKNak8VIqL6IyxYxhMOGTurIQ==} + engines: {node: '>=0.8'} + restore-cursor@3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} engines: {node: '>=8'} @@ -5989,6 +6323,9 @@ packages: resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} engines: {node: '>=0.4'} + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} @@ -6044,6 +6381,9 @@ packages: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} @@ -6194,6 +6534,9 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.3.0: resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} @@ -6339,6 +6682,10 @@ packages: swap-case@1.1.2: resolution: {integrity: sha512-BAmWG6/bx8syfc6qXPprof3Mn5vQgf5dwdUNJhsNqU9WdPt5P+ES/wQ5bxfijy8zwZgZZHslC3iAsxsuQMCzJQ==} + symbol-observable@4.0.0: + resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==} + engines: {node: '>=0.10'} + system-architecture@0.1.0: resolution: {integrity: sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==} engines: {node: '>=18'} @@ -6457,6 +6804,10 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + ts-invariant@0.10.3: + resolution: {integrity: sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==} + engines: {node: '>=8'} + ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} hasBin: true @@ -6599,6 +6950,9 @@ packages: resolution: {integrity: sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA==} engines: {node: '>=14.0'} + unfetch@4.2.0: + resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -6725,6 +7079,15 @@ packages: react-dom: optional: true + use-isomorphic-layout-effect@1.2.0: + resolution: {integrity: sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + use-sidecar@1.1.3: resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} engines: {node: '>=10'} @@ -6919,6 +7282,9 @@ packages: resolution: {integrity: sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==} engines: {node: '>=0.1'} + xstate@4.38.3: + resolution: {integrity: sha512-SH7nAaaPQx57dx6qvfcIgqKRXIh4L0A1iYEqim4s1u7c9VoCgzZc+63FY90AKU4ZzOC2cfJzTnpO4zK7fCUzzw==} + xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -6954,6 +7320,12 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zen-observable-ts@1.2.5: + resolution: {integrity: sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==} + + zen-observable@0.8.15: + resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==} + zod-validation-error@3.4.0: resolution: {integrity: sha512-ZOPR9SVY6Pb2qqO5XHt+MkkTRxGXb4EVtnjc9JpXUOtUB1T9Ru7mZOT361AN3MsetVe7R0a1KZshJDZdgp9miQ==} engines: {node: '>=18.0.0'} @@ -7141,6 +7513,27 @@ snapshots: '@antfu/utils@0.7.10': {} + '@apollo/client@3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + '@wry/context': 0.7.4 + '@wry/equality': 0.5.7 + '@wry/trie': 0.4.3 + graphql: 16.8.1 + graphql-tag: 2.12.6(graphql@16.8.1) + hoist-non-react-statics: 3.3.2 + optimism: 0.16.2 + prop-types: 15.8.1 + response-iterator: 0.2.20 + symbol-observable: 4.0.0 + ts-invariant: 0.10.3 + tslib: 2.8.1 + zen-observable-ts: 1.2.5 + optionalDependencies: + graphql-ws: 5.16.2(graphql@16.8.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -7386,6 +7779,33 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 17.0.2 + '@codesandbox/sandpack-react@2.6.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@codemirror/autocomplete': 6.18.4 + '@codemirror/commands': 6.7.1 + '@codemirror/lang-css': 6.3.1 + '@codemirror/lang-html': 6.4.9 + '@codemirror/lang-javascript': 6.2.2 + '@codemirror/language': 6.10.7 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.36.0 + '@codesandbox/sandpack-client': 2.19.8 + '@lezer/highlight': 1.2.1 + '@react-hook/intersection-observer': 3.1.2(react@18.3.1) + '@stitches/core': 1.2.8 + anser: 2.3.0 + clean-set: 1.1.2 + codesandbox-import-util-types: 2.2.3 + dequal: 2.0.3 + escape-carriage: 1.3.1 + lz-string: 1.5.0 + react: 18.3.1 + react-devtools-inline: 4.4.0 + react-dom: 18.3.1(react@18.3.1) + react-is: 17.0.2 + + '@codesandbox/sandpack-themes@2.0.21': {} + '@corex/deepmerge@4.0.43': {} '@cspotcode/source-map-support@0.8.1': @@ -7550,6 +7970,10 @@ snapshots: dependencies: tslib: 2.8.1 + '@graphql-typed-document-node/core@3.2.0(graphql@16.8.1)': + dependencies: + graphql: 16.8.1 + '@headlessui/react@2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react': 0.26.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -7829,6 +8253,8 @@ snapshots: '@next/env@15.1.7': {} + '@next/env@15.2.1': {} + '@next/eslint-plugin-next@14.2.15': dependencies: glob: 10.3.10 @@ -7839,42 +8265,63 @@ snapshots: '@next/swc-darwin-arm64@15.1.7': optional: true + '@next/swc-darwin-arm64@15.2.1': + optional: true + '@next/swc-darwin-x64@14.2.15': optional: true '@next/swc-darwin-x64@15.1.7': optional: true + '@next/swc-darwin-x64@15.2.1': + optional: true + '@next/swc-linux-arm64-gnu@14.2.15': optional: true '@next/swc-linux-arm64-gnu@15.1.7': optional: true + '@next/swc-linux-arm64-gnu@15.2.1': + optional: true + '@next/swc-linux-arm64-musl@14.2.15': optional: true '@next/swc-linux-arm64-musl@15.1.7': optional: true + '@next/swc-linux-arm64-musl@15.2.1': + optional: true + '@next/swc-linux-x64-gnu@14.2.15': optional: true '@next/swc-linux-x64-gnu@15.1.7': optional: true + '@next/swc-linux-x64-gnu@15.2.1': + optional: true + '@next/swc-linux-x64-musl@14.2.15': optional: true '@next/swc-linux-x64-musl@15.1.7': optional: true + '@next/swc-linux-x64-musl@15.2.1': + optional: true + '@next/swc-win32-arm64-msvc@14.2.15': optional: true '@next/swc-win32-arm64-msvc@15.1.7': optional: true + '@next/swc-win32-arm64-msvc@15.2.1': + optional: true + '@next/swc-win32-ia32-msvc@14.2.15': optional: true @@ -7884,6 +8331,81 @@ snapshots: '@next/swc-win32-x64-msvc@15.1.7': optional: true + '@next/swc-win32-x64-msvc@15.2.1': + optional: true + + '@nhost/apollo@8.0.5(@apollo/client@3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nhost/nhost-js@3.2.5(graphql@16.8.1))': + dependencies: + '@apollo/client': 3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@nhost/nhost-js': 3.2.5(graphql@16.8.1) + graphql: 16.8.1 + graphql-ws: 5.16.2(graphql@16.8.1) + jwt-decode: 4.0.0 + + '@nhost/graphql-js@0.3.0(graphql@16.8.1)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.8.1) + base-64: 1.0.0 + graphql: 16.8.1 + isomorphic-unfetch: 3.1.0 + jwt-decode: 4.0.0 + transitivePeerDependencies: + - encoding + + '@nhost/hasura-auth-js@2.10.1': + dependencies: + '@simplewebauthn/browser': 9.0.1 + fetch-ponyfill: 7.1.0 + js-cookie: 3.0.5 + jwt-decode: 4.0.0 + xstate: 4.38.3 + transitivePeerDependencies: + - encoding + + '@nhost/hasura-storage-js@2.7.0': + dependencies: + fetch-ponyfill: 7.1.0 + form-data: 4.0.1 + graphql: 16.8.1 + xstate: 4.38.3 + transitivePeerDependencies: + - encoding + + '@nhost/nhost-js@3.2.5(graphql@16.8.1)': + dependencies: + '@nhost/graphql-js': 0.3.0(graphql@16.8.1) + '@nhost/hasura-auth-js': 2.10.1 + '@nhost/hasura-storage-js': 2.7.0 + graphql: 16.8.1 + isomorphic-unfetch: 3.1.0 + transitivePeerDependencies: + - encoding + + '@nhost/react-apollo@17.0.1(@apollo/client@3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nhost/nhost-js@3.2.5(graphql@16.8.1))(@nhost/react@3.10.1(@types/react@18.3.17)(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@apollo/client': 3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@nhost/apollo': 8.0.5(@apollo/client@3.7.15(graphql-ws@5.16.2(graphql@16.8.1))(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@nhost/nhost-js@3.2.5(graphql@16.8.1)) + '@nhost/react': 3.10.1(@types/react@18.3.17)(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + graphql: 16.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@nhost/nhost-js' + + '@nhost/react@3.10.1(@types/react@18.3.17)(graphql@16.8.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@nhost/nhost-js': 3.2.5(graphql@16.8.1) + '@xstate/react': 3.2.2(@types/react@18.3.17)(react@18.3.1)(xstate@4.38.3) + jwt-decode: 4.0.0 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + xstate: 4.38.3 + transitivePeerDependencies: + - '@types/react' + - '@xstate/fsm' + - encoding + - graphql + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -8597,6 +9119,12 @@ snapshots: '@shikijs/vscode-textmate@10.0.1': {} + '@simplewebauthn/browser@9.0.1': + dependencies: + '@simplewebauthn/types': 9.0.1 + + '@simplewebauthn/types@9.0.1': {} + '@sindresorhus/slugify@2.2.1': dependencies: '@sindresorhus/transliterate': 1.6.0 @@ -8711,7 +9239,7 @@ snapshots: dependencies: commander: 10.0.1 execa: 5.1.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 fs-extra: 10.1.0 gradient-string: 2.0.2 inquirer: 8.2.6 @@ -8915,6 +9443,10 @@ snapshots: '@types/minimatch': 5.1.2 '@types/node': 22.10.7 + '@types/hast@2.3.10': + dependencies: + '@types/unist': 2.0.11 + '@types/hast@3.0.4': dependencies: '@types/unist': 3.0.3 @@ -9071,7 +9603,7 @@ snapshots: '@typescript-eslint/types': 8.18.1 '@typescript-eslint/visitor-keys': 8.18.1 debug: 4.4.0 - fast-glob: 3.3.2 + fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 @@ -9130,20 +9662,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@xyflow/react@12.3.2(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@wry/context@0.7.4': + dependencies: + tslib: 2.8.1 + + '@wry/equality@0.5.7': + dependencies: + tslib: 2.8.1 + + '@wry/trie@0.3.2': + dependencies: + tslib: 2.8.1 + + '@wry/trie@0.4.3': + dependencies: + tslib: 2.8.1 + + '@xstate/react@3.2.2(@types/react@18.3.17)(react@18.3.1)(xstate@4.38.3)': dependencies: - '@xyflow/system': 0.0.43 - classcat: 5.0.5 react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.17)(react@18.3.1) + use-isomorphic-layout-effect: 1.2.0(@types/react@18.3.17)(react@18.3.1) + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: + xstate: 4.38.3 transitivePeerDependencies: - '@types/react' - - immer - '@xyflow/react@12.3.6(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@xyflow/react@12.3.2(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@xyflow/system': 0.0.47 + '@xyflow/system': 0.0.43 classcat: 5.0.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -9152,9 +9699,9 @@ snapshots: - '@types/react' - immer - '@xyflow/react@12.4.3(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@xyflow/react@12.3.6(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@xyflow/system': 0.0.51 + '@xyflow/system': 0.0.47 classcat: 5.0.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -9201,16 +9748,6 @@ snapshots: d3-selection: 3.0.0 d3-zoom: 3.0.0 - '@xyflow/system@0.0.51': - dependencies: - '@types/d3-drag': 3.0.7 - '@types/d3-selection': 3.0.11 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - '@xyflow/system@0.0.52': dependencies: '@types/d3-drag': 3.0.7 @@ -9428,6 +9965,8 @@ snapshots: streamx: 2.21.1 optional: true + base-64@1.0.0: {} + base64-js@1.5.1: {} basic-ftp@5.0.5: {} @@ -9668,6 +10207,8 @@ snapshots: estree-walker: 3.0.3 periscopic: 3.1.0 + codesandbox-import-util-types@2.2.3: {} + collapse-white-space@2.1.0: {} color-convert@1.9.3: @@ -9747,6 +10288,8 @@ snapshots: core-js-pure@3.39.0: {} + core-util-is@1.0.3: {} + cors@2.8.5: dependencies: object-assign: 4.1.1 @@ -10383,8 +10926,8 @@ snapshots: '@typescript-eslint/parser': 8.18.1(eslint@8.57.1)(typescript@5.7.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.2(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) @@ -10412,34 +10955,34 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 enhanced-resolve: 5.17.1 eslint: 8.57.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 get-tsconfig: 4.8.1 is-bun-module: 1.3.0 is-glob: 4.0.3 stable-hash: 0.0.4 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.18.1(eslint@8.57.1)(typescript@5.7.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 @@ -10450,7 +10993,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.18.1(eslint@8.57.1)(typescript@5.7.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.0 is-glob: 4.0.3 @@ -10743,6 +11286,14 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} @@ -10755,8 +11306,14 @@ snapshots: dependencies: reusify: 1.0.4 + fathom-client@3.5.0: {} + fathom-client@3.7.2: {} + fault@1.0.4: + dependencies: + format: 0.2.2 + fault@2.0.1: dependencies: format: 0.2.2 @@ -10767,6 +11324,12 @@ snapshots: fdir@6.4.2: {} + fetch-ponyfill@7.1.0: + dependencies: + node-fetch: 2.6.13 + transitivePeerDependencies: + - encoding + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 @@ -10775,6 +11338,8 @@ snapshots: dependencies: flat-cache: 3.2.0 + file-saver@2.0.5: {} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -10956,7 +11521,7 @@ snapshots: '@types/glob': 7.2.0 array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob: 7.2.3 ignore: 5.3.2 merge2: 1.4.1 @@ -10973,6 +11538,17 @@ snapshots: graphemer@1.4.0: {} + graphql-tag@2.12.6(graphql@16.8.1): + dependencies: + graphql: 16.8.1 + tslib: 2.8.1 + + graphql-ws@5.16.2(graphql@16.8.1): + dependencies: + graphql: 16.8.1 + + graphql@16.8.1: {} + hachure-fill@0.5.2: {} handlebars@4.7.8: @@ -11055,6 +11631,8 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hast-util-parse-selector@2.2.5: {} + hast-util-parse-selector@4.0.0: dependencies: '@types/hast': 3.0.4 @@ -11155,6 +11733,14 @@ snapshots: dependencies: '@types/hast': 3.0.4 + hastscript@6.0.0: + dependencies: + '@types/hast': 2.3.10 + comma-separated-tokens: 1.0.8 + hast-util-parse-selector: 2.2.5 + property-information: 5.6.0 + space-separated-tokens: 1.1.5 + hastscript@9.0.0: dependencies: '@types/hast': 3.0.4 @@ -11168,8 +11754,16 @@ snapshots: no-case: 2.3.2 upper-case: 1.1.3 + highlight.js@10.7.3: {} + + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + html-to-image@1.11.11: {} + html-url-attributes@3.0.1: {} + html-void-elements@3.0.0: {} htmlparser2@6.1.0: @@ -11217,6 +11811,8 @@ snapshots: ignore@5.3.2: {} + immediate@3.0.6: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -11477,12 +12073,21 @@ snapshots: dependencies: system-architecture: 0.1.0 + isarray@1.0.0: {} + isarray@2.0.5: {} isbinaryfile@4.0.10: {} isexe@2.0.0: {} + isomorphic-unfetch@3.1.0: + dependencies: + node-fetch: 2.7.0 + unfetch: 4.2.0 + transitivePeerDependencies: + - encoding + iterator.prototype@1.1.4: dependencies: define-data-property: 1.1.4 @@ -11517,6 +12122,8 @@ snapshots: jiti@1.21.7: {} + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -11554,6 +12161,15 @@ snapshots: object.assign: 4.1.5 object.values: 1.2.0 + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + jwt-decode@4.0.0: {} + katex@0.16.21: dependencies: commander: 8.3.0 @@ -11591,6 +12207,10 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lie@3.3.0: + dependencies: + immediate: 3.0.6 + lightningcss-darwin-arm64@1.29.1: optional: true @@ -11682,6 +12302,11 @@ snapshots: lower-case@1.1.4: {} + lowlight@1.20.0: + dependencies: + fault: 1.0.4 + highlight.js: 10.7.3 + lru-cache@10.4.3: {} lru-cache@11.0.2: {} @@ -12347,10 +12972,18 @@ snapshots: dependencies: '@corex/deepmerge': 4.0.43 '@next/env': 13.5.7 - fast-glob: 3.3.2 + fast-glob: 3.3.3 minimist: 1.2.8 next: 15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-sitemap@4.2.3(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + dependencies: + '@corex/deepmerge': 4.0.43 + '@next/env': 13.5.7 + fast-glob: 3.3.3 + minimist: 1.2.8 + next: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes@0.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -12408,7 +13041,32 @@ snapshots: - '@babel/core' - babel-plugin-macros - nextra-theme-docs@4.2.13(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)): + next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@next/env': 15.2.1 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001689 + postcss: 8.4.31 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + styled-jsx: 5.1.6(react@18.3.1) + optionalDependencies: + '@next/swc-darwin-arm64': 15.2.1 + '@next/swc-darwin-x64': 15.2.1 + '@next/swc-linux-arm64-gnu': 15.2.1 + '@next/swc-linux-arm64-musl': 15.2.1 + '@next/swc-linux-x64-gnu': 15.2.1 + '@next/swc-linux-x64-musl': 15.2.1 + '@next/swc-win32-arm64-msvc': 15.2.1 + '@next/swc-win32-x64-msvc': 15.2.1 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + nextra-theme-docs@4.2.14(@types/react@18.3.17)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)): dependencies: '@headlessui/react': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) clsx: 2.1.1 @@ -12427,6 +13085,25 @@ snapshots: - immer - use-sync-external-store + nextra-theme-docs@4.2.14(@types/react@18.3.17)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(nextra@4.2.13(acorn@8.14.0)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)): + dependencies: + '@headlessui/react': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + clsx: 2.1.1 + next: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next-themes: 0.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + nextra: 4.2.13(acorn@8.14.0)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2) + react: 18.3.1 + react-compiler-runtime: 0.0.0-experimental-22c6e49-20241219(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + scroll-into-view-if-needed: 3.1.0 + zod: 3.24.1 + zod-validation-error: 3.4.0(zod@3.24.1) + zustand: 5.0.3(@types/react@18.3.17)(react@18.3.1)(use-sync-external-store@1.2.2(react@18.3.1)) + transitivePeerDependencies: + - '@types/react' + - immer + - use-sync-external-store + nextra@4.2.13(acorn@8.14.0)(next@14.2.15(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2): dependencies: '@formatjs/intl-localematcher': 0.6.0 @@ -12523,6 +13200,54 @@ snapshots: - supports-color - typescript + nextra@4.2.13(acorn@8.14.0)(next@15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2): + dependencies: + '@formatjs/intl-localematcher': 0.6.0 + '@headlessui/react': 2.2.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mdx-js/mdx': 3.1.0(acorn@8.14.0) + '@napi-rs/simple-git': 0.1.19 + '@shikijs/twoslash': 2.3.2(typescript@5.7.2) + '@theguild/remark-mermaid': 0.2.0(react@18.3.1) + '@theguild/remark-npm2yarn': 0.3.3 + better-react-mathjax: 2.0.3(react@18.3.1) + clsx: 2.1.1 + estree-util-to-js: 2.0.0 + estree-util-value-to-estree: 3.2.1 + fast-glob: 3.3.2 + github-slugger: 2.0.0 + hast-util-to-estree: 3.1.0 + katex: 0.16.21 + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm: 3.0.0 + mdast-util-to-hast: 13.2.0 + negotiator: 1.0.0 + next: 15.2.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-compiler-runtime: 0.0.0-experimental-22c6e49-20241219(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + react-medium-image-zoom: 5.2.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + rehype-katex: 7.0.1 + rehype-pretty-code: 0.14.0(shiki@2.3.2) + rehype-raw: 7.0.0 + remark-frontmatter: 5.0.0 + remark-gfm: 4.0.0 + remark-math: 6.0.0 + remark-reading-time: 2.0.1 + remark-smartypants: 3.0.2 + shiki: 2.3.2 + slash: 5.1.0 + title: 4.0.1 + unist-util-remove: 4.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-children: 3.0.0 + yaml: 2.6.1 + zod: 3.24.1 + zod-validation-error: 3.4.0(zod@3.24.1) + transitivePeerDependencies: + - acorn + - supports-color + - typescript + nlcst-to-string@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -12531,6 +13256,10 @@ snapshots: dependencies: lower-case: 1.1.4 + node-fetch@2.6.13: + dependencies: + whatwg-url: 5.0.0 + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 @@ -12625,6 +13354,11 @@ snapshots: regex: 6.0.1 regex-recursion: 6.0.2 + optimism@0.16.2: + dependencies: + '@wry/context': 0.7.4 + '@wry/trie': 0.3.2 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -12695,6 +13429,8 @@ snapshots: package-manager-detector@0.2.7: {} + pako@1.0.11: {} + param-case@2.1.1: dependencies: no-case: 2.3.2 @@ -12903,6 +13639,12 @@ snapshots: prettier@3.4.2: {} + prismjs@1.27.0: {} + + prismjs@1.29.0: {} + + process-nextick-args@2.0.1: {} + progress@2.0.3: {} prop-types@15.8.1: @@ -13025,6 +13767,24 @@ snapshots: react-is@17.0.2: {} + react-markdown@9.0.0(@types/react@18.3.17)(react@18.3.1): + dependencies: + '@types/hast': 3.0.4 + '@types/react': 18.3.17 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.2 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + micromark-util-sanitize-uri: 2.0.1 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.1 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + react-medium-image-zoom@5.2.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -13083,6 +13843,15 @@ snapshots: optionalDependencies: '@types/react': 18.3.17 + react-syntax-highlighter@15.5.0(react@18.3.1): + dependencies: + '@babel/runtime': 7.26.0 + highlight.js: 10.7.3 + lowlight: 1.20.0 + prismjs: 1.29.0 + react: 18.3.1 + refractor: 3.6.0 + react-use-measure@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: react: 18.3.1 @@ -13097,6 +13866,16 @@ snapshots: dependencies: pify: 2.3.0 + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + readable-stream@3.6.2: dependencies: inherits: 2.0.4 @@ -13152,6 +13931,12 @@ snapshots: gopd: 1.2.0 which-builtin-type: 1.2.1 + refractor@3.6.0: + dependencies: + hastscript: 6.0.0 + parse-entities: 2.0.0 + prismjs: 1.27.0 + regenerator-runtime@0.14.1: {} regex-recursion@6.0.2: @@ -13326,6 +14111,8 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + response-iterator@0.2.20: {} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 @@ -13431,6 +14218,8 @@ snapshots: has-symbols: 1.1.0 isarray: 2.0.5 + safe-buffer@5.1.2: {} + safe-buffer@5.2.1: {} safe-regex-test@1.1.0: @@ -13507,6 +14296,8 @@ snapshots: functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 + setimmediate@1.0.5: {} + setprototypeof@1.2.0: {} shallowequal@1.1.0: {} @@ -13719,6 +14510,10 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 @@ -13864,6 +14659,8 @@ snapshots: lower-case: 1.1.4 upper-case: 1.1.3 + symbol-observable@4.0.0: {} + system-architecture@0.1.0: {} tabbable@6.2.0: {} @@ -14050,6 +14847,10 @@ snapshots: ts-interface-checker@0.1.13: {} + ts-invariant@0.10.3: + dependencies: + tslib: 2.8.1 + ts-node@10.9.2(@types/node@20.17.10)(typescript@5.7.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -14239,6 +15040,8 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 + unfetch@4.2.0: {} + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -14399,6 +15202,12 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + use-isomorphic-layout-effect@1.2.0(@types/react@18.3.17)(react@18.3.1): + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.17 + use-sidecar@1.1.3(@types/react@18.3.17)(react@18.3.1): dependencies: detect-node-es: 1.1.0 @@ -14578,6 +15387,8 @@ snapshots: xmldom-sre@0.1.31: {} + xstate@4.38.3: {} + xtend@4.0.2: {} y18n@5.0.8: {} @@ -14607,6 +15418,12 @@ snapshots: yocto-queue@0.1.0: {} + zen-observable-ts@1.2.5: + dependencies: + zen-observable: 0.8.15 + + zen-observable@0.8.15: {} + zod-validation-error@3.4.0(zod@3.24.1): dependencies: zod: 3.24.1 diff --git a/sites/reactflow.dev/.env.development b/sites/reactflow.dev/.env.development index 4c5cfb540..46562d28f 100644 --- a/sites/reactflow.dev/.env.development +++ b/sites/reactflow.dev/.env.development @@ -3,3 +3,5 @@ NEXT_PUBLIC_PRO_EXAMPLES_URL=https://pro-examples-staging.reactflow.dev NEXT_PUBLIC_PRO_PLATFORM_URL=http://localhost:3000 NEXT_PUBLIC_NHOST_API_URL=https://local.functions.nhost.run/v1 NEXT_PUBLIC_UI_COMPONENTS_URL=http://localhost:3004 +NEXT_PUBLIC_NHOST_SUBDOMAIN= +NEXT_PUBLIC_NHOST_REGION= diff --git a/sites/reactflow.dev/.env.production b/sites/reactflow.dev/.env.production index 1ad3ab703..d40883559 100644 --- a/sites/reactflow.dev/.env.production +++ b/sites/reactflow.dev/.env.production @@ -1,3 +1,5 @@ NEXT_PUBLIC_EXAMPLES_URL=https://example-apps.xyflow.com NEXT_PUBLIC_PRO_PLATFORM_URL=https://pro.reactflow.dev NEXT_PUBLIC_NHOST_API_URL=https://fdsuchqhfchojqpemwyn.functions.eu-central-1.nhost.run/v1 +NEXT_PUBLIC_NHOST_SUBDOMAIN= +NEXT_PUBLIC_NHOST_REGION= diff --git a/sites/reactflow.dev/next.config.ts b/sites/reactflow.dev/next.config.ts index 9c933091b..a9392c068 100644 --- a/sites/reactflow.dev/next.config.ts +++ b/sites/reactflow.dev/next.config.ts @@ -6,7 +6,10 @@ import reactFlowPackageJson from '@xyflow/react/package.json' with { type: 'json // This is used for finding out the real deploy slug for a preview deployment // afaik this is the only way because Vercel doesn't expose this information const slugRegex = /-git-(.*?)\.vercel\.app/; -export function parsePreviewDeploySlug(branchUrl: string) { +export function parsePreviewDeploySlug(branchUrl?: string) { + if (!branchUrl) { + throw new Error('Missing branchUrl'); + } return branchUrl.match(slugRegex)?.[1]; } diff --git a/sites/reactflow.dev/package.json b/sites/reactflow.dev/package.json index 41f2d9727..d1aba56e4 100644 --- a/sites/reactflow.dev/package.json +++ b/sites/reactflow.dev/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "private": true, "scripts": { + "types:check": "tsc --noEmit", "dev": "next --port 3002", "build": "next build", "postbuild": "next-sitemap", @@ -19,7 +20,7 @@ "@xyflow/react": "^12.4.4", "@xyflow/xy-ui": "workspace:*", "d3": "^7.9.0", - "next": "^15.1.7", + "next": "^15.2.1", "next-sitemap": "^4.2.3", "nextra": "^4.2.13", "nextra-theme-docs": "^4.2.13", @@ -27,6 +28,18 @@ "react-dom": "^18.3.1", "react-icons": "^4.10.1", "timeago-react": "^3.0.6", + "@nhost/nhost-js": "^3.2.5", + "@nhost/react": "^3.10.1", + "@nhost/react-apollo": "^17.0.1", + "remark-gfm": "4.0.0", + "react-syntax-highlighter": "15.5.0", + "react-markdown": "9.0.0", + "@codesandbox/sandpack-react": "2.6.7", + "@codesandbox/sandpack-themes": "2.0.21", + "file-saver": "2.0.5", + "jszip": "3.10.1", + "@apollo/client": "3.7.15", + "fathom-client": "3.5.0", "ui-components": "workspace:*", "xy-shared": "workspace:*" }, diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/page.tsx new file mode 100644 index 000000000..0a1c514f4 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/page.tsx @@ -0,0 +1,35 @@ +'use client'; + +import React from 'react'; +import Link from 'next/link'; +import { Heading, Text } from '@xyflow/xy-ui'; +import { useSearchParams } from 'next/navigation'; +import { EnvelopeIcon } from '@heroicons/react/24/outline'; + +function VerifyEmailPage() { + const defaultEmail = useSearchParams()?.get('email'); + const linkQueryParams = defaultEmail ? `?email=${defaultEmail}` : ''; + + return ( +
+

+ + Verification +

+ We just sent you an email + + + If you didn{"'"}t receive an email, you can request a new one{' '} + + here + + . + +
+ ); +} + +export default VerifyEmailPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/resend-link/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/resend-link/page.tsx new file mode 100644 index 000000000..3074e51da --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/resend-link/page.tsx @@ -0,0 +1,19 @@ +import { + ResendVerificationLink, + AuthFormWrapper, +} from '@/components/pro/AuthForms'; + +const ResendVerificationLinkPage = () => { + return ( + + + + ); +}; + +export default ResendVerificationLinkPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/verify/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/verify/page.tsx new file mode 100644 index 000000000..c24911f1f --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/email-verification/verify/page.tsx @@ -0,0 +1,24 @@ +import { redirect } from 'next/navigation'; +import { getNhost } from '@/utils/nhost'; +import { FC } from 'react'; +import { SearchParams } from '@/types'; + +type PageProps = { + searchParams: SearchParams; +}; + +const VerifyEmailPage: FC = async (props) => { + const searchParams = await props.searchParams; + const nhost = await getNhost(); + const { ticket, redirectTo, type } = searchParams; + + if (ticket && redirectTo && type) { + redirect( + `${nhost.auth.url}/verify?ticket=${ticket}&type=${type}&redirectTo=${redirectTo}`, + ); + } + + redirect('/?error=invalid-ticket'); +}; + +export default VerifyEmailPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/layout.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/layout.tsx new file mode 100644 index 000000000..66320266a --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/layout.tsx @@ -0,0 +1,30 @@ +import { FC, ReactNode, Suspense } from 'react'; +import { PageLoader } from '@/components/pro/Loader'; +import { getNhost } from '@/utils/nhost'; +import { redirect } from 'next/navigation'; + +const Layout: FC<{ children: ReactNode }> = async ({ children }) => { + const nhost = await getNhost(); + const isAuthenticated = nhost.auth.isAuthenticated(); + + if (isAuthenticated) { + redirect('/'); + } + + return ( + }> +
+
+
{children}
+
+ + ); +}; + +export default Layout; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/reset-password/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/reset-password/page.tsx new file mode 100644 index 000000000..30003e953 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/reset-password/page.tsx @@ -0,0 +1,19 @@ +import { ResetPassword, AuthFormWrapper } from '@/components/pro/AuthForms'; + +const authFormLinks = [{ href: '/signin', label: 'Back to login' }]; + +const ResetPasswordPage = () => { + return ( + + + + ); +}; + +export default ResetPasswordPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/magic-link/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/magic-link/page.tsx new file mode 100644 index 000000000..d9958de87 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/magic-link/page.tsx @@ -0,0 +1,19 @@ +import { SignInMagicLink, AuthFormWrapper } from '@/components/pro/AuthForms'; + +const authFormLinks = [ + { href: '/signin', label: 'Sign in using Email and Password' }, +]; + +const SignInEmailPasswordPage = () => { + return ( + + + + ); +}; + +export default SignInEmailPasswordPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/page.tsx new file mode 100644 index 000000000..5507c8e69 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signin/page.tsx @@ -0,0 +1,18 @@ +import { + SignInEmailPassword, + AuthFormWrapper, +} from '@/components/pro/AuthForms'; + +const authFormLinks = [ + { href: '/signup', label: "Don't have an account? Sign Up" }, +]; + +const SignInEmailPasswordPage = () => { + return ( + + + + ); +}; + +export default SignInEmailPasswordPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/(auth)/signup/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signup/page.tsx new file mode 100644 index 000000000..83d403124 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/(auth)/signup/page.tsx @@ -0,0 +1,22 @@ +import { + SignUpEmailPassword, + AuthFormWrapper, +} from '@/components/pro/AuthForms'; + +const authFormLinks = [ + { href: '/signin', label: 'Already have an account? Sign In' }, +]; + +const SignUpPage = () => { + return ( + + + + ); +}; + +export default SignUpPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/billing.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/billing.tsx new file mode 100644 index 000000000..37e74fcf2 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/billing.tsx @@ -0,0 +1,29 @@ +import { + Card, + CardHeader, + CardDescription, + CardTitle, + CardFooter, +} from '@xyflow/xy-ui'; +import CustomerPortalButton from '@/components/pro/CustomerPortalButton'; + +function BillingCard() { + return ( + + + Billing & Subscription + + Open the Stripe customer portal to download your invoices, change your + billing details and change or cancel your subscription plan. + + + + + Open Customer Portal + + + + ); +} + +export default BillingCard; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-email.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-email.tsx new file mode 100644 index 000000000..9333000f5 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-email.tsx @@ -0,0 +1,73 @@ +'use client'; + +import { useState } from 'react'; +import { useUserEmail, useChangeEmail } from '@nhost/react'; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardFooter, + Button, + Input, + InputLabel, +} from '@xyflow/xy-ui'; + +function ChangeEmailCard() { + const email = useUserEmail(); + const { changeEmail, isLoading, needsEmailVerification, isError, error } = + useChangeEmail(); + const [newEmail, setNewEmail] = useState(''); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + await changeEmail(newEmail); + }; + + return ( + + + Change Email + {needsEmailVerification && ( + + Please confirm your new email by clicking the link we sent to{' '} + {newEmail}. + + )} + {isError && ( + + {error ? error.message : 'Something went wrong.'} + + )} + + +
+
+ New Email + setNewEmail(evt.target.value)} + required + id="email" + placeholder={email} + disabled={isLoading || needsEmailVerification} + /> +
+ +
+
+
+ ); +} + +export default ChangeEmailCard; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-password.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-password.tsx new file mode 100644 index 000000000..3459958d1 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/change-password.tsx @@ -0,0 +1,75 @@ +'use client'; + +import { useState } from 'react'; +import { useChangePassword } from '@nhost/react'; +import { + Card, + CardHeader, + CardDescription, + CardTitle, + CardFooter, + Button, + Input, + InputLabel, +} from '@xyflow/xy-ui'; + +function ChangePasswordCard() { + const [isLoading, setIsLoading] = useState(false); + const { changePassword, isSuccess, isError, error } = useChangePassword(); + const [newPassword, setNewPassword] = useState(''); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + setIsLoading(true); + await changePassword(newPassword); + setIsLoading(false); + }; + + return ( + + + Change Password + {isSuccess && ( + + Your password has been updated. + + )} + {isError && ( + + {error ? error.message : 'Something went wrong.'} + + )} + + +
+
+ New Password + setNewPassword(evt.target.value)} + required + id="password" + placeholder="Enter password..." + /> +
+ +
+
+
+ ); +} + +export default ChangePasswordCard; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/delete-account.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/delete-account.tsx new file mode 100644 index 000000000..996577d36 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/_cards/delete-account.tsx @@ -0,0 +1,120 @@ +'use client'; + +import { useState } from 'react'; +import { useSignOut, useUserEmail } from '@nhost/react'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, + Card, + CardHeader, + CardTitle, + CardDescription, + CardFooter, + Button, + Input, +} from '@xyflow/xy-ui'; +import useNhostFunction from '@/hooks/useNhostFunction'; + +export default function DeleteAccountCard() { + const userEmail = useUserEmail(); + const [confirmUserEmail, setConfirmUserEmail] = useState(''); + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + const nhostFunction = useNhostFunction(); + const { signOut } = useSignOut(); + const isDeleteConfirmed = userEmail === confirmUserEmail; + + const deleteAccount = async () => { + if (isDeleteConfirmed) { + setIsDeleteLoading(true); + + const { success } = await nhostFunction('/users/delete', {}); + + if (success) { + await signOut(); + } + + setIsDeleteLoading(false); + } + }; + + return ( + + + + Delete Account + + + + Are you sure? + +
+ By clicking the button below, you will delete your React Flow + Pro account. This action is irreversible. Please confirm that + you want to: +
+
    +
  • + Delete your React Flow Pro account and all the data + associated with it +
  • +
  • + Cancel your subscription and lose access for the remaining + time +
  • +
  • + Remove access to the pro features for invited team members +
  • +
+
+ If you want to proceed, please enter your email in the form + below: +
+ setConfirmUserEmail(evt.target.value)} + value={confirmUserEmail} + type="email" + placeholder={`Type ${userEmail} to confirm...`} + required + /> +
+
+ + Cancel + + {isDeleteLoading ? 'Please wait...' : 'Confirm Deletion'} + + +
+ + Use this button to delete your account and all your data. This + action is irreversible. + +
+ + cannot be a descendant of + + +
+
+ ); +} diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/page.tsx new file mode 100644 index 000000000..2024c56b9 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/account/page.tsx @@ -0,0 +1,25 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; + +import ChangeEmailCard from './_cards/change-email'; +import ChangePasswordCard from './_cards/change-password'; +import DeleteAccountCard from './_cards/delete-account'; +import BillingCard from './_cards/billing'; + +function AccountPage() { + return ( +
+ +
+ + + + +
+
+ ); +} + +export default AccountPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/auth-protected.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/auth-protected.tsx new file mode 100644 index 000000000..1a76032bd --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/auth-protected.tsx @@ -0,0 +1,24 @@ +'use client'; + +import { FC, ReactNode } from 'react'; +import { redirect } from 'next/navigation'; +import { PageLoader } from '@/components/pro/Loader'; +import useQueryString from '@/hooks/useQueryString'; +import useSubscription from '@/hooks/useSubscription'; +import { useAuthenticationStatus } from '@nhost/react'; + +export const AuthProtected: FC<{ children: ReactNode }> = ({ children }) => { + const { isLoading, isAuthenticated } = useAuthenticationStatus(); + const isSubscriptionLoading = useSubscription().isLoading; + const queryString = useQueryString(); + + if (isLoading || isSubscriptionLoading) { + return ; + } + + if (!isAuthenticated) { + redirect(`/signin${queryString}`); + } + + return children; +}; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/[id]/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/[id]/page.tsx new file mode 100644 index 000000000..88e497cf8 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/[id]/page.tsx @@ -0,0 +1,44 @@ +import { FC } from 'react'; +import { notFound } from 'next/navigation'; +import ProExampleViewer from '@/components/pro/ProExampleViewer'; +import { Framework } from '@/types'; +import { getExampleConfig } from '@/utils/pro-utils'; + +type PageProps = { + params: Promise<{ id: string; framework: Framework }>; +}; + +const ProExamplePage: FC = async (props) => { + let exampleConfig = null; + const params = await props.params; + + try { + exampleConfig = await getExampleConfig(params); + } catch (err) { + console.error(err); + } + + if (!exampleConfig || !Object.values(Framework).includes(params.framework)) { + notFound(); + } + + return ( + + ); +}; + +export default ProExamplePage; + +export async function generateStaticParams() { + return []; + // const examples = await getExampleList(); + // return examples.map((example) => ({ id: example.id, framework: example.framework })); +} + +// https://github.com/leerob/on-demand-isr/blob/main/app/%5Bid%5D/page.tsx +export const dynamic = 'force-static'; +export const dynamicParams = true; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/page.tsx new file mode 100644 index 000000000..db2788291 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/[framework]/page.tsx @@ -0,0 +1,58 @@ +import { FC } from 'react'; +import { notFound, redirect } from 'next/navigation'; +import DashboardHeader from '@/components/pro/DashboardHeader'; +import NotSubscribedNotification from '@/components/pro/Notification/not-subscribed'; +import ExamplesGrid from '@/components/pro/ExamplesGrid'; +import { Framework } from '@/types'; +import { getExampleList } from '@/utils/pro-utils'; + +type PageProps = { + params: Promise<{ framework: Framework }>; +}; + +const ProExamplesOverview: FC = async (props) => { + const params = await props.params; + const examples = (await getExampleList()).filter( + (example) => example.type !== 'template', + ); + + // this is used to redirect the legacy urls (pro.reactflow.dev/examples/auto-layout) to the new url structure + // e.g. pro.reactflow.dev/examples/react/auto-layout + const exampleById = examples.find( + (example) => example.id === params.framework, + ); + + if (exampleById) { + redirect(`/examples/${exampleById.framework}/${exampleById.id}`); + } + + if (!Object.values(Framework).includes(params.framework)) { + notFound(); + } + + return ( +
+ + {params.framework} Pro Examples + + } + description="A continuously growing collection of advanced React Flow examples. During your subscription you can access the source code of all Pro examples." + /> + + +
+ ); +}; + +export default ProExamplesOverview; + +export async function generateStaticParams() { + return []; + // return Object.values(Framework).map((framework) => ({ framework })); +} + +// https://github.com/leerob/on-demand-isr/blob/main/app/%5Bid%5D/page.tsx +export const dynamic = 'force-static'; +export const dynamicParams = true; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/page.tsx new file mode 100644 index 000000000..e4296a544 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/examples/page.tsx @@ -0,0 +1,21 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; +import ExamplesGrid from '@/components/pro/ExamplesGrid'; +import NotSubscribedNotification from '@/components/pro/Notification/not-subscribed'; +import { getExampleList } from '@/utils/pro-utils'; + +export default async function ProExamplesOverview() { + const examples = (await getExampleList()).filter( + (example) => example.type !== 'template', + ); + + return ( + <> + + + + + ); +} diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/layout.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/layout.tsx new file mode 100644 index 000000000..ba43e9ed3 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/layout.tsx @@ -0,0 +1,21 @@ +import { FC, Suspense, ReactNode } from 'react'; +import { Sidebar } from '@/components/pro/Sidebar'; +import { PageLoader } from '@/components/pro/Loader'; +import { AuthProtected } from './auth-protected'; + +const DashboardLayout: FC<{ children: ReactNode }> = ({ children }) => { + return ( + }> + +
+ +
+ {children} +
+
+
+
+ ); +}; + +export default DashboardLayout; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/page.tsx new file mode 100644 index 000000000..f6f1ea21a --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/page.tsx @@ -0,0 +1,79 @@ +import SubscriptionFeature from '@/components/pro/SubscriptionFeature'; +import { SubscriptionPlan } from '@/types'; +import DashboardHeader from '@/components/pro/DashboardHeader'; +import { FC } from 'react'; + +const OverviewPage: FC = () => { + return ( + <> + +
+
+ + + + + + + + + +
+
+ + ); +}; + +export default OverviewPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/page.tsx new file mode 100644 index 000000000..dbde6ba49 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/page.tsx @@ -0,0 +1,26 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; +import { Card, CardHeader } from '@xyflow/xy-ui'; + +import SignUp from './signup'; + +export const metadata = { + robots: 'noindex, nofollow', +}; + +const SignUpEduPage = () => { + return ( +
+ + + + + + +
+ ); +}; + +export default SignUpEduPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/signup.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/signup.tsx new file mode 100644 index 000000000..7bb46c36f --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/non-commercial-edu-oss/signup.tsx @@ -0,0 +1,179 @@ +'use client'; + +import { useState } from 'react'; + +import Link from 'next/link'; +import Head from 'next/head'; +import { Button, Heading, Input, InputLabel } from '@xyflow/xy-ui'; +import useNhostFunction from '@/hooks/useNhostFunction'; +import useSubscription from '@/hooks/useSubscription'; + +function Signup() { + const [isLoading, setLoading] = useState(false); + const [isError, setError] = useState(false); + const [isSuccess, setSuccess] = useState(false); + const [email, setEmail] = useState(''); + const [url, setUrl] = useState(''); + const [confirmed, setConfirmed] = useState(false); + const [projectType, setProjectType] = useState<'oss' | 'student' | undefined>( + undefined, + ); + const callNhostFunction = useNhostFunction(); + const { isSubscribed } = useSubscription(); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + setLoading(true); + evt.preventDefault(); + + const response = await callNhostFunction('/subscribe-edu-oss', { + plan: projectType, + }); + + if (response.error) { + setError(true); + } else { + setSuccess(true); + } + + setLoading(false); + }; + + const changeProjectType = (type: 'oss' | 'student') => { + setProjectType(type); + setEmail(''); + setUrl(''); + setConfirmed(false); + }; + + const isDisabled = + !confirmed || + !projectType || + (projectType === 'student' && !email) || + (projectType === 'oss' && !url); + + if (isSuccess || isSubscribed) { + return ( +
+ + You are signed up. + + +
+ ); + } + + return ( + <> + + + +
+
+ {isError && ( +
+ Something went wrong. Please{' '} + + Contact Us + {' '} + for support. +
+ )} +
+
Project Type
+
+ changeProjectType(evt.target.value as 'oss')} + className="w-4 h-4" + /> + +
+
+ + changeProjectType(evt.target.value as 'student') + } + className="w-4 h-4" + /> + +
+
+ {projectType === 'student' && ( +
+ Your University Email + setEmail(evt.target.value)} + autoComplete="on" + /> +
+ )} + {projectType === 'oss' && ( +
+ Project Link + setUrl(evt.target.value)} + /> +
+ )} + {projectType && ( +
+ setConfirmed(evt.target.checked)} + /> + +
+ )} + +
+
+ + ); +} + +export default Signup; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/page.tsx new file mode 100644 index 000000000..1cf349b64 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/subscribe/page.tsx @@ -0,0 +1,17 @@ +import { FAQ, reactFlowProFaqItems } from '@xyflow/xy-ui'; +import DashboardHeader from '@/components/pro/DashboardHeader'; +import PricingTable from '@/components/pro/PricingTable'; + +export default function SubscribePage() { + return ( + <> + + + + + + ); +} diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/support/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/support/page.tsx new file mode 100644 index 000000000..c608a2c5b --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/support/page.tsx @@ -0,0 +1,96 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; +import SubscriptionFeature from '@/components/pro/SubscriptionFeature'; +import { SubscriptionPlan } from '@/types'; +import { Link } from '@xyflow/xy-ui'; + +const SupportPage = () => { + return ( +
+ +
+
+ How to quickly get support for your problem +
+
    +
  • + The best way to contact us is via email. If you’re a Professional or + Enterprise subscriber, click the link below to use that email + address. +
  • +
  • + Include a codesandbox that we can access which reproduces your + issue. You can use our codesandbox starters:{' '} + + Javascript + + ,{' '} + + Typescript + +
  • +
  • + If you think you found a bug in the library, please{' '} + + open an issue on github + + . Subscribers can prioritize issues by sending us an mail with the + link to{' '} + + info@xyflow.com + +
  • +
  • + Email us using the account you’re currently logged into, or + reference it so we can check your subscription status +
  • +
+
+
+
+ + + +
+
+
+ ); +}; + +export default SupportPage; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/_cards/manage-team.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/_cards/manage-team.tsx new file mode 100644 index 000000000..fe5f82667 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/_cards/manage-team.tsx @@ -0,0 +1,290 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { gql } from '@apollo/client'; +import { useAuthQuery } from '@nhost/react-apollo'; +import { useUserEmail, useUserId } from '@nhost/react'; +import { + Button, + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + Input, + InputLabel, + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + CardTitle, +} from '@xyflow/xy-ui'; +import useNhostFunction from '@/hooks/useNhostFunction'; +import { PlanLabel } from '@/components/pro/SubscriptionStatus'; +import Loader from '@/components/pro/Loader'; +import { Currency } from '@/types'; +import { getCurrencySign } from '@/utils/pro-utils'; + +const GET_TEAM_MEMBERS = gql` + query GetTeamMembers($userId: uuid) { + team_subscriptions( + where: { created_by: { _eq: $userId } } + order_by: { created_at: asc } + ) { + email + } + } +`; + +type TeamMember = { + email: string; +}; + +type TeamStatus = { + includedSeats: number; + currency: Currency; + billingPeriod: 'month' | 'year'; +}; + +export default function ManageTeamCard() { + const userId = useUserId(); + const [status, setStatus] = useState(null); + const [confirmPayment, setConfirmPayment] = useState(false); + const [confirmDeleteMember, setConfirmDeleteMember] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [isDeleteLoading, setIsDeleteLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [memberEmail, setMemberEmail] = useState(''); + const { data, refetch } = useAuthQuery(GET_TEAM_MEMBERS, { + variables: { userId }, + }); + const nhostFunction = useNhostFunction(); + const userEmail = useUserEmail(); + + useEffect(() => { + const updateStatus = async () => { + const status = await nhostFunction('/team/status', {}); + + if (!status || status.error) { + return; + } + + setStatus(status); + }; + + updateStatus(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + const removeMember = async (email: string) => { + setErrorMessage(null); + setIsDeleteLoading(true); + const { error } = await nhostFunction('/team/remove', { email }); + + if (error) { + setErrorMessage( + typeof error.message === 'string' + ? error.message + : 'Something went wrong. Please contact us.', + ); + } + + await refetch(); + setIsDeleteLoading(false); + setConfirmDeleteMember(null); + }; + + const addMember = async ({ + paymentConfirmed, + }: { + paymentConfirmed: boolean; + }) => { + setIsLoading(true); + setErrorMessage(null); + + const response = await nhostFunction('/team/invite', { + email: memberEmail, + paymentConfirmed, + }); + + if (!response || response.error) { + setIsLoading(false); + setErrorMessage( + typeof response.message === 'string' + ? response.message + : 'Something went wrong. Please contact us.', + ); + setConfirmPayment(false); + return; + } + + if (response.needsPaymentConfirmation) { + setConfirmPayment(true); + setIsLoading(false); + return; + } + + await refetch(); + setIsLoading(false); + setConfirmPayment(false); + }; + + const onAdd = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + await addMember({ paymentConfirmed: false }); + }; + + const currencySign = getCurrencySign(status?.currency); + const monthlySeatPrice = status?.currency === Currency.INR ? 2000 : 20; + const includedSeats = status?.includedSeats ?? 0; + const remainingSeats = Math.max( + 0, + includedSeats - (data?.team_subscriptions?.length ?? 0), + ); + + if (!status) { + return ( +
+ +
+ ); + } + + return ( + + + Team Members + + You have {remainingSeats} remaining{' '} + {remainingSeats === 1 ? 'seat' : 'seats'} included in your{' '} + plan. + {remainingSeats === 0 && ( + Additional seats will be charged based on your usage. + )} + + {confirmPayment && ( + + + + Add a new team member + +

+ By clicking Confirm Payment, you will add one additional + seat to your pro plan at the cost of {currencySign} + {monthlySeatPrice} per month. +

{' '} +

+ You will only pay for the time that the seat is listed in + your team and the amount will be added to your next invoice. +

+ {/* Adding a new seat will charge {currencySign} */} + {/* {seatPrice} per {status.billingPeriod} with your next invoice. Please confirm to continue. */} +
+
+ + setConfirmPayment(false)}> + Cancel + + addMember({ paymentConfirmed: true })} + > + {isLoading ? 'Please wait...' : 'Confirm Payment'} + + +
+
+ )} + {confirmDeleteMember && ( + + + + Are you sure? + + This will remove {confirmDeleteMember} from + your team. They will no longer have access to your + subscription features. + + + + setConfirmDeleteMember(null)}> + Cancel + + removeMember(confirmDeleteMember)} + > + {isDeleteLoading ? 'Please wait...' : 'Confirm Deletion'} + + + + + )} +
+ +
+ +
{userEmail}
+
+ {data?.team_subscriptions?.map((member: TeamMember, i: number) => ( + +
+ {member.email}{' '} + {status && i >= includedSeats && ( + + extra seat + + )} +
+ +
+ ))} +
+ +
+
+ Add Team Member + setMemberEmail(evt.target.value)} + required + id="email" + placeholder="Member Email" + disabled={isLoading} + /> + {errorMessage && ( + + {errorMessage} + + )} +
+ +
+
+
+ ); +} diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/page.tsx new file mode 100644 index 000000000..ca710e2a6 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/team/page.tsx @@ -0,0 +1,21 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; +import ManageTeamCard from './_cards/manage-team'; +import NotSubscribedNotification from '@/components/pro/Notification/not-subscribed'; +import { Subscribed } from '@/components/pro/SubscriptionStatus'; + +export default function TeamPage() { + return ( +
+ +
+ + + + +
+
+ ); +} diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/[id]/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/[id]/page.tsx new file mode 100644 index 000000000..c33f06530 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/[id]/page.tsx @@ -0,0 +1,44 @@ +import { FC } from 'react'; +import { notFound } from 'next/navigation'; +import ProExampleViewer from '@/components/pro/ProExampleViewer'; +import { getExampleConfig } from '@/utils/pro-utils'; +import { Framework } from '@/types'; + +type PageProps = { + params: Promise<{ id: string }>; +}; + +const ProTemplatePage: FC = async (props) => { + let exampleConfig = null; + const params = await props.params; + + try { + exampleConfig = await getExampleConfig(params); + } catch (err) { + console.error(err); + } + + if (!exampleConfig) { + notFound(); + } + + return ( + + ); +}; + +export default ProTemplatePage; + +export async function generateStaticParams() { + return []; + // const examples = await getExampleList(); + // return examples.map((example) => ({ id: example.id, framework: example.framework })); +} + +// https://github.com/leerob/on-demand-isr/blob/main/app/%5Bid%5D/page.tsx +export const dynamic = 'force-static'; +export const dynamicParams = true; diff --git a/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/page.tsx b/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/page.tsx new file mode 100644 index 000000000..731b44e28 --- /dev/null +++ b/sites/reactflow.dev/src/app/(content-pages)/dashboard/templates/page.tsx @@ -0,0 +1,20 @@ +import DashboardHeader from '@/components/pro/DashboardHeader'; +import ExamplesGrid from '@/components/pro/ExamplesGrid'; +import NotSubscribedNotification from '@/components/pro/Notification/not-subscribed'; +import { getExampleList } from '@/utils/pro-utils'; + +export default async function TemplatesOverview() { + const examples = await getExampleList(); + const templates = examples.filter((example) => example.type === 'template'); + + return ( + <> + + + + + ); +} diff --git a/sites/reactflow.dev/src/app/_meta.global.ts b/sites/reactflow.dev/src/app/_meta.global.ts index 2b20f85f0..dc22b59a5 100644 --- a/sites/reactflow.dev/src/app/_meta.global.ts +++ b/sites/reactflow.dev/src/app/_meta.global.ts @@ -247,6 +247,12 @@ const metaRecord = { }, 'developer-survey-2024': { display: 'hidden' }, 'whats-new': { display: 'hidden' }, + // TODO by @dima -- maybe by default make `display: 'hidden'` ? + dashboard: { display: 'hidden' }, + 'email-verification': { display: 'hidden' }, + 'reset-password': { display: 'hidden' }, + signin: { display: 'hidden' }, + signup: { display: 'hidden' }, }; export default metaRecord; diff --git a/sites/reactflow.dev/src/app/global.css b/sites/reactflow.dev/src/app/global.css index a7db4c78f..e2dfa85a9 100644 --- a/sites/reactflow.dev/src/app/global.css +++ b/sites/reactflow.dev/src/app/global.css @@ -15,13 +15,42 @@ @layer l-base { :root { - --primary: 333 100% 50%; - --primary-foreground: 210 40% 98%; --docsearch-primary-color: hsl(var(--primary)); - - --secondary-foreground: 333 100% 50%; --accent: 333 100% 50%; --shiki-color-background: red; + + --primary: 333 100% 50%; + --primary-foreground: 210 40% 98%; + --secondary: 0 100% 100%; + --secondary-foreground: 333 100% 50%; + + --color-xyflow: 248 1% 85%; + --color-react: 333 100% 50%; + --color-svelte: 15 100% 50%; + + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 0.5rem; + + --light: 215.4 16.3% 46.9%; } } diff --git a/sites/reactflow.dev/src/app/layout.tsx b/sites/reactflow.dev/src/app/layout.tsx index 0e628c827..3cf9510f4 100644 --- a/sites/reactflow.dev/src/app/layout.tsx +++ b/sites/reactflow.dev/src/app/layout.tsx @@ -4,7 +4,7 @@ import reactFlowPackageJson from '@xyflow/react/package.json'; import { Html } from '@/components/html.client'; import { generateRootMetadata } from 'xy-shared/server'; import { Fathom } from 'xy-shared'; - +import { Providers } from '@/components/pro/Providers'; import './global.css'; export const metadata = generateRootMetadata('React Flow', { @@ -30,7 +30,7 @@ const RootLayout: FC<{ - {children} + {children} ); diff --git a/sites/reactflow.dev/src/app/pro/layout.tsx b/sites/reactflow.dev/src/app/pro/layout.tsx index e5959e29f..004edae0b 100644 --- a/sites/reactflow.dev/src/app/pro/layout.tsx +++ b/sites/reactflow.dev/src/app/pro/layout.tsx @@ -40,16 +40,6 @@ const Layout: FC<{ children: ReactNode }> = async ({ children }) => { ], ...remainingCategories, }} - navbar={ - - } > {children} diff --git a/sites/reactflow.dev/src/components/navbar.tsx b/sites/reactflow.dev/src/components/navbar.tsx new file mode 100644 index 000000000..0837c3295 --- /dev/null +++ b/sites/reactflow.dev/src/components/navbar.tsx @@ -0,0 +1,24 @@ +import { FC } from 'react'; +import { LogoLabel } from '@xyflow/xy-ui'; +import { Navbar as NextraNavbar } from 'nextra-theme-docs'; +import NavMenu from './pro/Navigation/NavMenu'; +import { getNhost } from '@/utils/nhost'; + +export const Navbar: FC<{ children }> = async ({ children }) => { + const nhost = await getNhost(); + const isAuthenticated = nhost.auth.isAuthenticated(); + return ( + + } + logoLink={false} + > + <> + {children} + + + + ); +}; diff --git a/sites/reactflow.dev/src/components/nextra-layout.tsx b/sites/reactflow.dev/src/components/nextra-layout.tsx index afe44244a..2fee7ec70 100644 --- a/sites/reactflow.dev/src/components/nextra-layout.tsx +++ b/sites/reactflow.dev/src/components/nextra-layout.tsx @@ -1,15 +1,16 @@ /* eslint react/jsx-sort-props: 'error' */ import { ComponentProps, FC, ReactNode } from 'react'; import { PageMapItem } from 'nextra'; -import { Layout, Navbar } from 'nextra-theme-docs'; -import { Footer as XYFooter, LogoLabel } from '@xyflow/xy-ui'; +import { Layout } from 'nextra-theme-docs'; +import { Footer as XYFooter } from '@xyflow/xy-ui'; import { TOC, getLastChangelog } from 'xy-shared/server'; +import { Navbar } from './navbar'; export const NextraLayout: FC<{ children: ReactNode; pageMap: PageMapItem[]; footerCategories: ComponentProps['categories']; - navbar: ReactNode; + navbar?: ReactNode; }> = async ({ children, pageMap, footerCategories, navbar }) => { return ( } - navbar={ - - } - logoLink={false} - > - {navbar} - - } + navbar={{navbar}} nextThemes={{ forcedTheme: 'light', defaultTheme: 'light' }} pageMap={pageMap} // Set to null to avoid rendering search in mobile nav, since we added search in navbar already diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/AuthFormWrapper.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/AuthFormWrapper.tsx new file mode 100644 index 000000000..4ecddc13c --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/AuthFormWrapper.tsx @@ -0,0 +1,94 @@ +import { ReactNode, Suspense } from 'react'; + +import Link from 'next/link'; +import { ArrowLongRightIcon } from '@heroicons/react/20/solid'; +import { SignInOAuth } from '@/components/pro/AuthForms'; +import { + Card, + CardContent, + CardHeader, + CardTitle, + CardDescription, + Heading, + Text, +} from '@xyflow/xy-ui'; +import { ExpiredTokenNotification } from './AuthNotification'; + +type AuthFormWrapperProps = { + children?: ReactNode; + links?: { href: string; label: string }[]; + showOAuth?: boolean; + showHero?: boolean; + title?: ReactNode; + description?: ReactNode; +}; + +const AuthFormWrapper = ({ + children, + links = [], + showOAuth = true, + showHero = true, + title = null, + description = null, +}: AuthFormWrapperProps) => { + return ( + <> + + + +
+ {showHero && ( +
+ + Build Better Node-Based UIs with{' '} + React Flow + + + By subscribing to React Flow Pro you are securing the maintenance + and development of our open source libraries. + +
+ )} +
+ {/*
+ +
*/} + + + {title && {title}} + {description && {description}} + + + {children} + {showOAuth && ( + <> +
+
+ + or + +
+
+ + + )} +
+
+
+ {links.map((link) => ( +
+ + {link.label} +
+ ))} +
+
+
+ + ); +}; + +export default AuthFormWrapper; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/AuthNotification.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/AuthNotification.tsx new file mode 100644 index 000000000..59fca038b --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/AuthNotification.tsx @@ -0,0 +1,130 @@ +'use client'; + +import * as React from 'react'; +import Link from 'next/link'; +import { useSearchParams } from 'next/navigation'; +import { Alert, AlertTitle, AlertDescription, Button, cn } from '@xyflow/xy-ui'; +import { ErrorPayload } from '@nhost/nhost-js'; + +type AuthErrorProps = { + error?: ErrorPayload; +}; + +type AuthNotificationProps = { + variant?: 'error' | 'success' | 'default'; + title?: React.ReactNode; + description?: React.ReactNode; + children?: React.ReactNode; + className?: string; +}; + +export function AuthNotification({ + variant = 'default', + title, + description, + children, + className, +}: AuthNotificationProps) { + return ( + +
+ {title && {title}} + {description && {description}} +
+ {children} +
+ ); +} + +export function ExpiredTokenNotification() { + const searchParams = useSearchParams(); + const isExpiredTokenError = searchParams.get('error') === 'invalid-ticket'; + + if (!isExpiredTokenError) { + return null; + } + + return ( +
+ + + + + +
+ ); +} + +export function MagicLinkSuccessNotification() { + return ( + + ); +} + +export function AuthErrorNotification({ error }: AuthErrorProps) { + const errorId = error?.error; + + if (errorId === 'invalid-email-password') { + return ( + + Please try again,{' '} + + reset your password + + , or get a{' '} + + magic link + + . + + } + variant="error" + /> + ); + } + + if (errorId === 'email-already-in-use') { + return ( + + Please use a different email or{' '} + + . + + } + variant="error" + /> + ); + } + + if (errorId) { + return ( + + ); + } + + return null; +} diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/ResendVerificationLink.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/ResendVerificationLink.tsx new file mode 100644 index 000000000..17cc2c9af --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/ResendVerificationLink.tsx @@ -0,0 +1,56 @@ +'use client'; + +import { useSearchParams } from 'next/navigation'; +import { useState } from 'react'; +import { useSignInEmailPasswordless } from '@nhost/react'; +import { Button, Input, InputLabel } from '@xyflow/xy-ui'; + +import { AuthErrorNotification, AuthNotification } from './AuthNotification'; + +function ResendVerificationLink() { + const defaultEmail = useSearchParams()?.get('email'); + const [email, setEmail] = useState(defaultEmail || ''); + const { signInEmailPasswordless, isSuccess, isLoading, isError, error } = + useSignInEmailPasswordless(); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + signInEmailPasswordless(email); + }; + + return ( +
+ {isError && } + {isSuccess && ( + + )} +
+ Email + setEmail(evt.target.value)} + /> +
+ + + ); +} + +export default ResendVerificationLink; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/ResetPassword.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/ResetPassword.tsx new file mode 100644 index 000000000..ed439cbb8 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/ResetPassword.tsx @@ -0,0 +1,54 @@ +'use client'; + +import { useState } from 'react'; +import { useResetPassword } from '@nhost/react'; + +import { Button, Input, InputLabel } from '@xyflow/xy-ui'; +import { AuthErrorNotification, AuthNotification } from './AuthNotification'; + +function ResetPassword() { + const [email, setEmail] = useState(''); + const { resetPassword, isLoading, isSent, isError, error } = + useResetPassword(); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + resetPassword(email, { redirectTo: '/account' }); + }; + + return ( +
+ {isError && } + {isSent && ( + + )} +
+ Email + setEmail(evt.target.value)} + /> +
+ + + ); +} + +export default ResetPassword; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/SignInEmailPassword.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/SignInEmailPassword.tsx new file mode 100644 index 000000000..a7d0aa859 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/SignInEmailPassword.tsx @@ -0,0 +1,107 @@ +'use client'; + +import { FC, useState } from 'react'; +import { useSearchParams } from 'next/navigation'; +import { useSignInEmailPassword } from '@nhost/react'; +import Link from 'next/link'; +import { redirect } from 'next/navigation'; + +import { Button, Input, InputLabel } from '@xyflow/xy-ui'; +import { AuthErrorNotification } from './AuthNotification'; + +const SignInEmailPassword: FC = () => { + const searchParams = useSearchParams(); + const [form, setForm] = useState<{ email: string; password: string }>({ + email: '', + password: '', + }); + const { + signInEmailPassword, + isLoading, + isSuccess, + needsEmailVerification, + isError, + error, + } = useSignInEmailPassword(); + + const handleFormSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + signInEmailPassword(form.email, form.password); + }; + + if (needsEmailVerification) { + redirect(`/email-verification?email=${form.email}`); + } + + if (isSuccess) { + redirect(searchParams.get('redirectTo') || '/dashboard'); + } + + return ( +
+ {isError && } +
+
+ + Email + + setForm({ ...form, email: evt.target.value })} + autoComplete="on" + placeholder="Email" + required + variant="square" + /> +
+
+ + Password + + ) => + setForm({ ...form, password: evt.target.value }) + } + placeholder="Password" + required + variant="square" + /> +
+ Having trouble signing in?{' '} + + Reset your password + {' '} + or{' '} + + get a magic link + + . +
+
+ +
+ + ); +}; + +export default SignInEmailPassword; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/SignInMagicLink.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/SignInMagicLink.tsx new file mode 100644 index 000000000..fdd9ee0cd --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/SignInMagicLink.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { FC, useState } from 'react'; +import { Button, Input, InputLabel } from '@xyflow/xy-ui'; +import { useSignInEmailPasswordless } from '@nhost/react'; + +import { MagicLinkSuccessNotification } from './AuthNotification'; +import { redirect } from 'next/navigation'; + +const SignInMagicLink: FC = () => { + const [email, setEmail] = useState(''); + const { signInEmailPasswordless, isSuccess, isLoading } = + useSignInEmailPasswordless(); + + const onChange = (evt: React.ChangeEvent) => { + setEmail(evt.target.value); + }; + + const onSubmit = (evt: React.SyntheticEvent) => { + evt.preventDefault(); + signInEmailPasswordless(email); + }; + + if (isSuccess) { + redirect(`/email-verification?email=${email}`); + } + + return ( +
+
+ {isSuccess ? ( + + ) : ( + <> +
+ + Email + + +
+ + + + )} +
+
+ ); +}; + +export default SignInMagicLink; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/SignInOAuth.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/SignInOAuth.tsx new file mode 100644 index 000000000..c7cdbf6ce --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/SignInOAuth.tsx @@ -0,0 +1,20 @@ +'use client'; + +import { useProviderLink } from '@nhost/react'; +import { Button } from '@xyflow/xy-ui'; + +const SignInOAuth = () => { + const { github } = useProviderLink({ metadata: {} }); + + return ( + + ); +}; + +export default SignInOAuth; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/SignUpEmailPassword.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/SignUpEmailPassword.tsx new file mode 100644 index 000000000..f9e587b6d --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/SignUpEmailPassword.tsx @@ -0,0 +1,104 @@ +'use client'; + +import { FC, useState } from 'react'; +import { useSignUpEmailPassword } from '@nhost/react'; +import Link from 'next/link'; + +import { Button, Input, InputLabel } from '@xyflow/xy-ui'; +import { redirect } from 'next/navigation'; + +import { AuthErrorNotification } from './AuthNotification'; + +const Signup: FC = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const { + signUpEmailPassword, + isLoading, + isError, + needsEmailVerification, + isSuccess, + error, + } = useSignUpEmailPassword(); + + const handleSubmit = async (evt: React.SyntheticEvent) => { + evt.preventDefault(); + await signUpEmailPassword(email, password); + }; + + if (needsEmailVerification) { + const queryParams = email ? `?email=${email}` : ''; + redirect(`/email-verification${queryParams}`); + } + + if (isSuccess) { + redirect('/dashboard'); + } + + return ( +
+ {isError && } +
+ + Email + + setEmail(evt.target.value)} + autoComplete="on" + placeholder="Email" + required + /> +
+
+ + Password + + ) => + setPassword(evt.target.value) + } + placeholder="Password" + required + /> +
+ By signing up, you agree to our{' '} + + Terms of Use + {' '} + and{' '} + + Privacy Policy + + . +
+
+ + + + ); +}; + +export default Signup; diff --git a/sites/reactflow.dev/src/components/pro/AuthForms/index.tsx b/sites/reactflow.dev/src/components/pro/AuthForms/index.tsx new file mode 100644 index 000000000..5d580e17f --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/AuthForms/index.tsx @@ -0,0 +1,7 @@ +export { default as SignInMagicLink } from './SignInMagicLink'; +export { default as SignInOAuth } from './SignInOAuth'; +export { default as SignInEmailPassword } from './SignInEmailPassword'; +export { default as SignUpEmailPassword } from './SignUpEmailPassword'; +export { default as ResetPassword } from './ResetPassword'; +export { default as ResendVerificationLink } from './ResendVerificationLink'; +export { default as AuthFormWrapper } from './AuthFormWrapper'; diff --git a/sites/reactflow.dev/src/components/pro/CustomerPortalButton/index.tsx b/sites/reactflow.dev/src/components/pro/CustomerPortalButton/index.tsx new file mode 100644 index 000000000..0770caa37 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/CustomerPortalButton/index.tsx @@ -0,0 +1,10 @@ +'use client'; + +// @todo how can this be turned into a server component? +import { Button, ButtonProps } from '@xyflow/xy-ui'; +import useStripeCustomerPortal from '@/hooks/useStripeCustomerPortal'; + +export default function CustomerPortalButton(props: ButtonProps) { + const { openCustomerPortal } = useStripeCustomerPortal(); + return + + + + + + ); +}; + +export default NavMenu; diff --git a/sites/reactflow.dev/src/components/pro/Navigation/UserMenu.tsx b/sites/reactflow.dev/src/components/pro/Navigation/UserMenu.tsx new file mode 100644 index 000000000..b910d5ebd --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/Navigation/UserMenu.tsx @@ -0,0 +1,70 @@ +'use client'; + +import Link from 'next/link'; +import { useSignOut, useUserEmail } from '@nhost/react'; +import { + Select, + SelectContent, + SelectSeparator, + SelectLabel, + SelectTrigger, + SelectGroup, +} from '@xyflow/xy-ui'; +import { UserIcon } from '@heroicons/react/24/solid'; +import useStripeCustomerPortal from '@/hooks/useStripeCustomerPortal'; +import { PlanLabel, Subscribed } from '@/components/pro/SubscriptionStatus'; +import { FC } from 'react'; + +export const UserMenu: FC = () => { + const userEmail = useUserEmail(); + const { openCustomerPortal } = useStripeCustomerPortal(); + const { signOut } = useSignOut(); + return ( + + ); +}; diff --git a/sites/reactflow.dev/src/components/pro/Notification/index.tsx b/sites/reactflow.dev/src/components/pro/Notification/index.tsx new file mode 100644 index 000000000..9fdcdc24f --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/Notification/index.tsx @@ -0,0 +1,36 @@ +import { Button, cn } from '@xyflow/xy-ui'; + +type NotificationProps = { + title?: React.ReactNode; + description?: React.ReactNode; + button?: { label: string; href: string }; +} & React.HTMLAttributes; + +// @todo maybe move this into ui package +export default function Notification({ + title, + description, + button, + className, + ...rest +}: NotificationProps) { + return ( +
+
+ {title &&
{title}
} + {description &&
{description}
} +
+ {button && ( + + + + )} +
+ ); +} diff --git a/sites/reactflow.dev/src/components/pro/Notification/not-subscribed.tsx b/sites/reactflow.dev/src/components/pro/Notification/not-subscribed.tsx new file mode 100644 index 000000000..06ddd1451 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/Notification/not-subscribed.tsx @@ -0,0 +1,22 @@ +import Notification from '@/components/pro/Notification'; +import { NotSubscribed } from '@/components/pro/SubscriptionStatus'; + +// @todo this should be imported from '.' but is causing a build error, not sure why +export type NotificationProps = { + title?: React.ReactNode; + description?: React.ReactNode; + button?: { label: string; href: string }; +} & React.HTMLAttributes; + +export default function NotSubscribedNotification(props: NotificationProps) { + return ( + + + + ); +} diff --git a/sites/reactflow.dev/src/components/pro/Pill/index.tsx b/sites/reactflow.dev/src/components/pro/Pill/index.tsx new file mode 100644 index 000000000..16c833a45 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/Pill/index.tsx @@ -0,0 +1,19 @@ +import { cn } from '@xyflow/xy-ui'; + +export type PillProps = { + children: React.ReactNode; + className?: string; +}; + +export default function Pill({ children, className }: PillProps) { + return ( +
+ {children} +
+ ); +} diff --git a/sites/reactflow.dev/src/components/pro/PricingTable/PricingPlan.tsx b/sites/reactflow.dev/src/components/pro/PricingTable/PricingPlan.tsx new file mode 100644 index 000000000..c485542ea --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/PricingTable/PricingPlan.tsx @@ -0,0 +1,76 @@ +'use client'; + +import useNhostFunction from '@/hooks/useNhostFunction'; +import { useState } from 'react'; +import { + Button, + Card, + CardContent, + CardHeader, + CardTitle, +} from '@xyflow/xy-ui'; + +type PricingPlanProps = { + plan: 'starter' | 'pro' | 'enterprise'; + interval?: 'month' | 'year'; + currency?: 'usd' | 'eur'; + seats?: number; +}; + +export default function PricingPlan({ + plan, + interval = 'month', + currency = 'usd', + seats = 0, +}: PricingPlanProps) { + const callNhostFunction = useNhostFunction(); + const [isLoading, setLoading] = useState(false); + + const subscribe = async () => { + setLoading(true); + + const response = await callNhostFunction('/stripe/create-checkout', { + plan, + interval, + currency, + }); + + if (response.url) { + window.location.href = response.res.data.url; + setTimeout(() => setLoading(false), 500); + return; + } + + setLoading(false); + }; + + return ( + + + {plan} +
$129
+
+ +
+
    +
  • Access to Pro Examples
  • +
  • + Invite {seats} team member{seats > 1 ? 's' : ''} +
  • +
  • Support the development
  • +
+
+
+ + + +
+ ); +} diff --git a/sites/reactflow.dev/src/components/pro/PricingTable/index.tsx b/sites/reactflow.dev/src/components/pro/PricingTable/index.tsx new file mode 100644 index 000000000..6a646845a --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/PricingTable/index.tsx @@ -0,0 +1,64 @@ +'use client'; + +import useNhostFunction from '@/hooks/useNhostFunction'; +import { useState } from 'react'; +import { + PricingTable, + defaultSubscriptionPlans, + BillingInterval, + SubscriptionPlanId, +} from '@xyflow/xy-ui'; + +export default function PricingTableComponent() { + const callNhostFunction = useNhostFunction(); + const [plans, setPlans] = useState(defaultSubscriptionPlans); + + const setLoading = (planId: SubscriptionPlanId, isLoading: boolean) => { + setPlans((plans) => + plans.map((plan) => + plan.id === planId + ? { ...plan, buttonLabel: isLoading ? 'Loading...' : 'Subscribe' } + : plan, + ), + ); + }; + + const subscribe = async ({ + plan, + interval, + }: { + plan: SubscriptionPlanId; + interval: BillingInterval; + }) => { + if (plan === SubscriptionPlanId.ENTERPRISE) { + return window.open('https://reactflow.dev/pro/enterprise', '_blank'); + } + + setLoading(plan, true); + + const response = await callNhostFunction('/stripe/create-checkout', { + plan, + interval, + }); + + if (!response.error && response.url) { + window.location.href = response.url; + setTimeout(() => setLoading(plan, false), 500); + return; + } + + setLoading(plan, false); + }; + + return ( + + subscribe({ + plan: subscribeConfig.plan, + interval: subscribeConfig.billingInterval, + }) + } + /> + ); +} diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/download-button.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/download-button.tsx new file mode 100644 index 000000000..26ed11314 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/download-button.tsx @@ -0,0 +1,64 @@ +import { useState } from 'react'; +import { Button } from '@xyflow/xy-ui'; +import { ArrowDownTrayIcon } from '@heroicons/react/24/outline'; + +import JSZip from 'jszip'; +import { saveAs } from 'file-saver'; +import { SandpackFiles } from '@codesandbox/sandpack-react'; +import useDownloadExample from '@/hooks/useDownloadExample'; +import { Framework } from '@/types'; + +const ignoreFiles = ['/config.json']; + +export default function DownloadButton({ + fileName, + files, + exampleId, + frameworkId, +}: { + fileName: string; + files: null | SandpackFiles; + exampleId: string; + frameworkId: Framework; +}) { + const [isDownloading, setIsDownloading] = useState(false); + const downloadExample = useDownloadExample({ + exampleId, + framework: frameworkId, + }); + + const downloadZip = async () => { + setIsDownloading(true); + const downloadFiles = files || (await downloadExample()); + const zip = new JSZip(); + + Object.entries(downloadFiles).map(([fileName, fileContent]) => { + if (ignoreFiles.includes(fileName)) { + return; + } + + const content = + typeof fileContent === 'string' ? fileContent : fileContent.code; + + zip.file(fileName.replace(/^\//, ''), content); + }); + + const content = await zip.generateAsync({ type: 'blob' }); + + saveAs(content, `${fileName}.zip`); + setIsDownloading(false); + }; + + return ( + + ); +} diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/index.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/index.tsx new file mode 100644 index 000000000..0ab1f0d6d --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/index.tsx @@ -0,0 +1,83 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { type SandpackFiles } from '@codesandbox/sandpack-react'; + +import { Framework, ProExampleConfig } from '@/types'; +import DashboardHeader from '@/components/pro/DashboardHeader'; +import useDownloadExample from '@/hooks/useDownloadExample'; +import useSubscription from '@/hooks/useSubscription'; + +import DownloadButton from './download-button'; +import OverviewButton from './overview-button'; +import Tabs from './tabs'; +import VariantSelect from './variant-select'; + +function ProExampleViewer({ + exampleId, + frameworkId, + config, +}: { + exampleId: string; + frameworkId: Framework; + config: ProExampleConfig; +}) { + const [files, setFiles] = useState(null); + const downloadExample = useDownloadExample({ + exampleId, + framework: frameworkId, + }); + const { isSubscribed } = useSubscription(); + const isUnlocked = isSubscribed || config.free; + const isTemplate = config.type === 'template'; + + useEffect(() => { + const loadFiles = async () => { + const sandpackFiles = await downloadExample(); + setFiles(sandpackFiles); + }; + + loadFiles(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return ( +
+ + +
{config.name}
+
+ + {isUnlocked && ( + + )} +
+ + } + description={config.description} + descriptionClassName="max-w-4xl" + className="my-4" + /> + +
+ ); +} + +export default ProExampleViewer; diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/overview-button.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/overview-button.tsx new file mode 100644 index 000000000..143f72807 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/overview-button.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link'; +import { ArrowLongLeftIcon } from '@heroicons/react/20/solid'; + +export default function OverviewButton({ + link = '/examples', + label = 'All Examples', +}: { + link?: string; + label?: React.ReactNode; +}) { + return ( + +
+ +
{label}
+
+ + ); +} diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/editor.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/editor.tsx new file mode 100644 index 000000000..2fef7a2de --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/editor.tsx @@ -0,0 +1,55 @@ +import { aquaBlue } from '@codesandbox/sandpack-themes'; +import { + SandpackProvider, + SandpackCodeEditor, + SandpackFileExplorer, + SandpackLayout, + SandpackFiles, +} from '@codesandbox/sandpack-react'; + +const editorHeight = 650; +const ignoreFiles = [ + '/package-lock.json', + '/public/favicon.ico', + '/src/vite-env.d.ts', + '/.gitignore', + '/config.json', + '/.eslintrc.cjs', + '/vite.config.ts', + '/tsconfig.json', + '/tsconfig.node.json', +]; + +const theme = { + ...aquaBlue, + colors: { ...aquaBlue.colors, accent: 'hsl(var(--primary))' }, +}; + +function ProExampleCodeEditor({ files }: { files: SandpackFiles }) { + const visibleFiles = files + ? Object.keys(files).filter((file) => !ignoreFiles.includes(file)) + : []; + + return ( + + + + + + + ); +} + +export default ProExampleCodeEditor; diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/index.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/index.tsx new file mode 100644 index 000000000..196136130 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/index.tsx @@ -0,0 +1,145 @@ +'use client'; + +import { + Button, + Tabs, + TabsContent, + TabsList, + TabsTrigger, + cn, +} from '@xyflow/xy-ui'; +import { Framework } from '@/types'; + +import PreviewTab from './preview'; +import EditorTab from './editor'; +import MarkdownTab from './markdown'; +import { SandpackFiles } from '@codesandbox/sandpack-react'; +import NotSubscribedNotification from '@/components/pro/Notification/not-subscribed'; +import { + BookOpenIcon, + CodeBracketIcon, + ComputerDesktopIcon, + LockClosedIcon, +} from '@heroicons/react/24/outline'; +import Loader from '@/components/pro/Loader'; + +const TabButton = (props) => { + const isActive = props['data-state'] === 'active'; + const className = cn( + 'border-b-2 border-transparent rounded-none -mb-px', + { + 'border-primary text-primary hover:text-primary': isActive, + }, + props.className, + ); + + return ( + + ); +}; + +const TabContent = (props: { + value: string; + loading?: boolean; + children: React.ReactNode; +}) => { + const children = props.loading ? ( +
+ +
+ ) : ( + props.children + ); + + return {children}; +}; + +export default function ProExampleViewerTabs({ + exampleId, + frameworkId, + files, + isUnlocked, + isTemplate, + previewUrl, +}: { + exampleId: string; + frameworkId: Framework; + files: null | SandpackFiles; + isUnlocked: boolean; + isTemplate?: boolean; + previewUrl?: string; +}) { + // @ts-ignore + const readme = files?.['/README.mdx']?.code || files?.['/README.md']?.code; + const iframePreviewUrl = + previewUrl ?? `${process.env.NEXT_PUBLIC_PRO_EXAMPLES_URL}/${exampleId}`; + + return ( + <> + + + + } + > + Preview + + + + }> + Code + + + + }> + Readme + + + {/* + Installation + + + License + */} + + + {!isUnlocked && ( + + )} + + + + + + {isUnlocked && ( + + + + )} + + {isUnlocked && ( + + + + )} + + {/* + + + + + + */} + + + ); +} diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/markdown.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/markdown.tsx new file mode 100644 index 000000000..3a7ce3121 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/markdown.tsx @@ -0,0 +1,123 @@ +import Markdown from 'react-markdown'; +import PrismLight from 'react-syntax-highlighter/dist/cjs/prism-light'; +import { oneLight as prismTheme } from 'react-syntax-highlighter/dist/cjs/styles/prism'; +import ts from 'react-syntax-highlighter/dist/cjs/languages/prism/typescript'; +import tsx from 'react-syntax-highlighter/dist/cjs/languages/prism/tsx'; +import jsx from 'react-syntax-highlighter/dist/cjs/languages/prism/jsx'; +import js from 'react-syntax-highlighter/dist/cjs/languages/prism/javascript'; +import bash from 'react-syntax-highlighter/dist/cjs/languages/prism/bash'; +import { Text } from '@xyflow/xy-ui'; +import remarkGfm from 'remark-gfm'; + +PrismLight.registerLanguage('js', js); +PrismLight.registerLanguage('ts', ts); +PrismLight.registerLanguage('tsx', tsx); +PrismLight.registerLanguage('jsx', jsx); +PrismLight.registerLanguage('sh', bash); +PrismLight.registerLanguage('bash', bash); + +const markdownComponents = { + h1: (props) => ( +

+ ), + h2: (props) => ( +

+ ), + h3: (props) => ( +

+ ), + h4: (props) => ( +

+ ), + p: (props) => , + a: (props) => , + pre: (props) => ( + + {props.children?.props?.children} + + ), + code: (props) => ( + + ), + ul: (props) => ( +
    + ), + ol: (props) => ( +
      + ), + // There's some gnarly destructuring here but we need to pull things out so we + // can style the table with tailwind properly... + table: ({ children, ...props }) => { + const [ + { + props: { children: theadChildren, ...thead }, + }, + { + props: { children: tbodyChildren, ...tbody }, + }, + ] = children; + + return ( + + + + {theadChildren.props.children.map((th, index) => { + return ( + + ); + })} + + + + {tbodyChildren.map((tr, index) => { + return ( + + {tr.props.children.map((td, index) => { + return ( + + ); + })} + + ); + })} + +
      + {th.props.children} +
      + {td.props.children} +
      + ); + }, +}; + +function MarkdownTab({ markdown }: { markdown: string }) { + return ( + + {markdown} + + ); +} + +export default MarkdownTab; diff --git a/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/preview.tsx b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/preview.tsx new file mode 100644 index 000000000..eebad6065 --- /dev/null +++ b/sites/reactflow.dev/src/components/pro/ProExampleViewer/tabs/preview.tsx @@ -0,0 +1,16 @@ +import { Button } from '@xyflow/xy-ui'; + +function ProExamplePreview({ iframePreviewUrl }: { iframePreviewUrl: string }) { + return ( + <> +
      +