@hexclave/next 1.0.66 → 1.0.68
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{admin-app-impl-2gZ3JWa0.d.ts → admin-app-impl-DFjllAVr.d.ts} +10 -5
- package/dist/admin-app-impl-DFjllAVr.d.ts.map +1 -0
- package/dist/clickmap/clickmap-core.d.ts +1 -1
- package/dist/clickmap/index.d.ts +1 -1
- package/dist/{client-app-BlA5Ptxs.d.ts → client-app-CE8FTKgO.d.ts} +13 -6
- package/dist/client-app-CE8FTKgO.d.ts.map +1 -0
- package/dist/components/selected-team-switcher.d.ts +1 -1
- package/dist/components/team-icon.d.ts +1 -1
- package/dist/components/team-switcher.d.ts +1 -1
- package/dist/components-page/account-settings/payments/payments-page.d.ts +1 -1
- package/dist/components-page/account-settings/teams/leave-team-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-api-keys-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-display-name-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-invitation-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-member-list-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-page.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-image-section.d.ts +1 -1
- package/dist/components-page/account-settings/teams/team-profile-user-section.d.ts +1 -1
- package/dist/components-page/hexclave-handler-client.d.ts +1 -1
- package/dist/components-page/hexclave-handler.d.ts +1 -1
- package/dist/components-page/onboarding.js +11 -1
- package/dist/components-page/onboarding.js.map +1 -1
- package/dist/components-page/onboarding.test.d.ts +1 -0
- package/dist/components-page/onboarding.test.js +151 -0
- package/dist/components-page/onboarding.test.js.map +1 -0
- package/dist/dev-tool/dev-tool-core.d.ts +1 -1
- package/dist/dev-tool/index.d.ts +1 -1
- package/dist/esm/components-page/onboarding.js +11 -1
- package/dist/esm/components-page/onboarding.js.map +1 -1
- package/dist/esm/components-page/onboarding.test.d.ts +1 -0
- package/dist/esm/components-page/onboarding.test.js +150 -0
- package/dist/esm/components-page/onboarding.test.js.map +1 -0
- package/dist/esm/generated/quetzal-translations.d.ts +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +7 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js +22 -5
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.js +97 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.js.map +1 -0
- package/dist/esm/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -4
- package/dist/esm/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.d.ts +1 -0
- package/dist/esm/lib/hexclave-app/teams/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.d.ts +10 -4
- package/dist/esm/lib/hexclave-app/users/index.d.ts.map +1 -1
- package/dist/esm/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/generated/quetzal-translations.d.ts +1 -1
- package/dist/{hexclave-provider-BxrgPM5P.d.ts → hexclave-provider-CVCzN4xG.d.ts} +3 -3
- package/dist/{hexclave-provider-BxrgPM5P.d.ts.map → hexclave-provider-CVCzN4xG.d.ts.map} +1 -1
- package/dist/{hooks-4BBvIKWJ.d.ts → hooks-BNF4UvGF.d.ts} +2 -2
- package/dist/{hooks-4BBvIKWJ.d.ts.map → hooks-BNF4UvGF.d.ts.map} +1 -1
- package/dist/{index-DDICHT5X.d.ts → index-g1JoVgIu.d.ts} +2 -2
- package/dist/{index-DDICHT5X.d.ts.map → index-g1JoVgIu.d.ts.map} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/lib/hexclave-app/apps/implementations/admin-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js +22 -5
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.d.ts +1 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.js +96 -0
- package/dist/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.js.map +1 -0
- package/dist/lib/hexclave-app/apps/implementations/common.js +2 -2
- package/dist/lib/hexclave-app/apps/implementations/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js +1 -4
- package/dist/lib/hexclave-app/apps/implementations/server-app-impl.js.map +1 -1
- package/dist/lib/hexclave-app/apps/index.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/admin-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.d.ts +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/hexclave-app/apps/interfaces/server-app.d.ts +1 -1
- package/dist/lib/hexclave-app/index.d.ts +1 -1
- package/dist/lib/hexclave-app/projects/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.d.ts +1 -1
- package/dist/lib/hexclave-app/teams/index.js.map +1 -1
- package/dist/lib/hexclave-app/users/index.d.ts +1 -1
- package/dist/lib/hexclave-app/users/index.js.map +1 -1
- package/dist/lib/hooks.d.ts +1 -1
- package/dist/providers/hexclave-context.d.ts +1 -1
- package/dist/providers/hexclave-provider-client.d.ts +1 -1
- package/dist/providers/hexclave-provider.d.ts +1 -1
- package/dist/providers/translation-provider.d.ts +1 -1
- package/dist/pushed-config-error-overlay/index.d.ts +1 -1
- package/dist/{quetzal-translations-XeH3zQ9l.d.ts → quetzal-translations-DVNj-zsr.d.ts} +3 -3
- package/dist/{quetzal-translations-XeH3zQ9l.d.ts.map → quetzal-translations-DVNj-zsr.d.ts.map} +1 -1
- package/dist/{translation-provider-WOaZXqAz.d.ts → translation-provider-DPFkLbrw.d.ts} +2 -2
- package/dist/{translation-provider-WOaZXqAz.d.ts.map → translation-provider-DPFkLbrw.d.ts.map} +1 -1
- package/package.json +4 -4
- package/src/components-page/onboarding.test.tsx +182 -0
- package/src/components-page/onboarding.tsx +17 -0
- package/src/integrations/convex/component/README.md +4 -4
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.sign-in-cache-warmup.test.ts +94 -0
- package/src/lib/hexclave-app/apps/implementations/client-app-impl.ts +33 -8
- package/src/lib/hexclave-app/apps/implementations/server-app-impl.ts +1 -10
- package/src/lib/hexclave-app/apps/interfaces/client-app.ts +1 -1
- package/src/lib/hexclave-app/teams/index.ts +1 -0
- package/src/lib/hexclave-app/users/index.ts +9 -4
- package/dist/admin-app-impl-2gZ3JWa0.d.ts.map +0 -1
- package/dist/client-app-BlA5Ptxs.d.ts.map +0 -1
|
@@ -26,7 +26,8 @@ function Onboarding(props) {
|
|
|
26
26
|
runAsynchronously(hexclaveApp.redirectToSignIn());
|
|
27
27
|
return null;
|
|
28
28
|
}
|
|
29
|
-
|
|
29
|
+
const restrictedReason = user.restrictedReason;
|
|
30
|
+
if (restrictedReason?.type === "email_not_verified") {
|
|
30
31
|
if (!!!user.primaryEmail) return /* @__PURE__ */ jsx(AddEmailForm, { fullPage: props.fullPage });
|
|
31
32
|
return /* @__PURE__ */ jsx(VerifyEmailScreen, {
|
|
32
33
|
user,
|
|
@@ -34,6 +35,15 @@ function Onboarding(props) {
|
|
|
34
35
|
fullPage: props.fullPage
|
|
35
36
|
});
|
|
36
37
|
}
|
|
38
|
+
if (restrictedReason?.type === "restricted_by_administrator") return /* @__PURE__ */ jsx(MessageCard$1, {
|
|
39
|
+
title: t("Your account has been restricted"),
|
|
40
|
+
fullPage: !!props.fullPage,
|
|
41
|
+
secondaryButtonText: t("Sign out"),
|
|
42
|
+
secondaryAction: async () => {
|
|
43
|
+
await user.signOut();
|
|
44
|
+
},
|
|
45
|
+
children: /* @__PURE__ */ jsx("p", { children: user.restrictedByAdminReason ?? t("An administrator has restricted your account. Please reach out to support if you believe this is an error.") })
|
|
46
|
+
});
|
|
37
47
|
return /* @__PURE__ */ jsx(MessageCard$1, {
|
|
38
48
|
title: t("Complete your account setup"),
|
|
39
49
|
fullPage: !!props.fullPage,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding.js","names":["MessageCard"],"sources":["../../../src/components-page/onboarding.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { strictEmailSchema, yupObject } from \"@hexclave/shared/dist/schema-fields\";\nimport { runAsynchronously, runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@hexclave/ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp, useUser } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function Onboarding(props: {\n fullPage?: boolean,\n}) {\n const { t } = useTranslation();\n const hexclaveApp = useStackApp();\n const user = useUser({ or: \"return-null\", includeRestricted: true });\n\n // If user is not restricted anymore, redirect to the intended destination\n // redirectToAfterSignIn automatically checks for after_auth_return_to in the URL\n if (user && !user.isRestricted) {\n runAsynchronously(hexclaveApp.redirectToAfterSignIn());\n // TODO: This should return a loading indicator, not just null\n return null;\n }\n\n // If user is anonymous or not logged in, redirect to sign-in\n if (!user || user.isAnonymous) {\n runAsynchronously(hexclaveApp.redirectToSignIn());\n // TODO: This should return a loading indicator, not just null\n return null;\n }\n\n // User is restricted - show appropriate onboarding step based on restricted reason\n const restrictedReason = user.restrictedReason;\n\n if (restrictedReason?.type === \"email_not_verified\") {\n // Check if user has a primary email\n const hasPrimaryEmail = !!user.primaryEmail;\n\n if (!hasPrimaryEmail) {\n // User needs to add an email first\n return <AddEmailForm fullPage={props.fullPage} />;\n }\n\n // User has email but it's not verified\n return <VerifyEmailScreen user={user} email={user.primaryEmail} fullPage={props.fullPage} />;\n }\n\n // Unknown restricted reason - show generic message\n return (\n <MessageCard\n title={t(\"Complete your account setup\")}\n fullPage={!!props.fullPage}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <p>{t(\"You have not yet completed your account setup. Please reach out to support if you believe this is an error.\")}</p>\n </MessageCard>\n );\n}\n\nfunction AddEmailForm(props: {\n fullPage?: boolean,\n onEmailAdded?: () => void,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\", includeRestricted: true });\n const hexclaveApp = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setLoading(true);\n try {\n await user.update({ primaryEmail: data.email });\n props.onEmailAdded?.();\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <MessageCard\n title={t(\"Add your email address\")}\n fullPage={!!props.fullPage}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <Typography className=\"mb-4\">\n {t(\"Please add an email address to complete your account setup. We'll send you a verification email.\")}\n </Typography>\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n className='flex flex-col gap-2'\n >\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter your email\")}\n type=\"email\"\n />\n {errors.email && <FormWarningText text={errors.email.message} />}\n <Button type=\"submit\" loading={loading} className=\"w-full\">\n {t(\"Continue\")}\n </Button>\n </form>\n </MessageCard>\n );\n}\n\nfunction VerifyEmailScreen(props: {\n user: NonNullable<ReturnType<typeof useUser>>,\n email: string,\n fullPage?: boolean,\n}) {\n const { t } = useTranslation();\n const { user, email } = props;\n const [changingEmail, setChangingEmail] = useState(false);\n\n if (changingEmail) {\n return <AddEmailForm fullPage={props.fullPage} onEmailAdded={() => setChangingEmail(false)} />;\n }\n\n return (\n <MessageCard\n title={t(\"Please check your email inbox\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Resend verification email\")}\n primaryAction={async () => {\n await user.sendVerificationEmail();\n }}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <Typography>\n {t(\"Please verify your email address \")}\n <span className=\"font-semibold\">{email}</span>\n {\" (\"}\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() => setChangingEmail(true)}\n >\n {t(\"change\")}\n </button>\n {\"). \"}\n {t(\"Click the button below to resend the verification link.\")}\n </Typography>\n </MessageCard>\n );\n}\n\n"],"mappings":";;;;;;;;;;;;;AAmBA,SAAgB,WAAW,OAExB;CACD,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,cAAc,YAAY;CAChC,MAAM,OAAO,QAAQ;EAAE,IAAI;EAAe,mBAAmB;CAAK,CAAC;CAInE,IAAI,QAAQ,CAAC,KAAK,cAAc;EAC9B,kBAAkB,YAAY,sBAAsB,CAAC;EAErD,OAAO;CACT;CAGA,IAAI,CAAC,QAAQ,KAAK,aAAa;EAC7B,kBAAkB,YAAY,iBAAiB,CAAC;EAEhD,OAAO;CACT;
|
|
1
|
+
{"version":3,"file":"onboarding.js","names":["MessageCard"],"sources":["../../../src/components-page/onboarding.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { strictEmailSchema, yupObject } from \"@hexclave/shared/dist/schema-fields\";\nimport { runAsynchronously, runAsynchronouslyWithAlert } from \"@hexclave/shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@hexclave/ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp, useUser } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function Onboarding(props: {\n fullPage?: boolean,\n}) {\n const { t } = useTranslation();\n const hexclaveApp = useStackApp();\n const user = useUser({ or: \"return-null\", includeRestricted: true });\n\n // If user is not restricted anymore, redirect to the intended destination\n // redirectToAfterSignIn automatically checks for after_auth_return_to in the URL\n if (user && !user.isRestricted) {\n runAsynchronously(hexclaveApp.redirectToAfterSignIn());\n // TODO: This should return a loading indicator, not just null\n return null;\n }\n\n // If user is anonymous or not logged in, redirect to sign-in\n if (!user || user.isAnonymous) {\n runAsynchronously(hexclaveApp.redirectToSignIn());\n // TODO: This should return a loading indicator, not just null\n return null;\n }\n\n // User is restricted - show appropriate onboarding step based on restricted reason\n const restrictedReason = user.restrictedReason;\n\n if (restrictedReason?.type === \"email_not_verified\") {\n // Check if user has a primary email\n const hasPrimaryEmail = !!user.primaryEmail;\n\n if (!hasPrimaryEmail) {\n // User needs to add an email first\n return <AddEmailForm fullPage={props.fullPage} />;\n }\n\n // User has email but it's not verified\n return <VerifyEmailScreen user={user} email={user.primaryEmail} fullPage={props.fullPage} />;\n }\n\n if (restrictedReason?.type === \"restricted_by_administrator\") {\n return (\n <MessageCard\n title={t(\"Your account has been restricted\")}\n fullPage={!!props.fullPage}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n {/* The public reason is set by the project's administrators (sign-up rules or manually), so it's the most\n helpful thing we can show; the generic sentence is only a fallback for when they didn't provide one. */}\n <p>{user.restrictedByAdminReason ?? t(\"An administrator has restricted your account. Please reach out to support if you believe this is an error.\")}</p>\n </MessageCard>\n );\n }\n\n // Unknown restricted reason - show generic message\n return (\n <MessageCard\n title={t(\"Complete your account setup\")}\n fullPage={!!props.fullPage}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <p>{t(\"You have not yet completed your account setup. Please reach out to support if you believe this is an error.\")}</p>\n </MessageCard>\n );\n}\n\nfunction AddEmailForm(props: {\n fullPage?: boolean,\n onEmailAdded?: () => void,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\", includeRestricted: true });\n const hexclaveApp = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setLoading(true);\n try {\n await user.update({ primaryEmail: data.email });\n props.onEmailAdded?.();\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <MessageCard\n title={t(\"Add your email address\")}\n fullPage={!!props.fullPage}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <Typography className=\"mb-4\">\n {t(\"Please add an email address to complete your account setup. We'll send you a verification email.\")}\n </Typography>\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n className='flex flex-col gap-2'\n >\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter your email\")}\n type=\"email\"\n />\n {errors.email && <FormWarningText text={errors.email.message} />}\n <Button type=\"submit\" loading={loading} className=\"w-full\">\n {t(\"Continue\")}\n </Button>\n </form>\n </MessageCard>\n );\n}\n\nfunction VerifyEmailScreen(props: {\n user: NonNullable<ReturnType<typeof useUser>>,\n email: string,\n fullPage?: boolean,\n}) {\n const { t } = useTranslation();\n const { user, email } = props;\n const [changingEmail, setChangingEmail] = useState(false);\n\n if (changingEmail) {\n return <AddEmailForm fullPage={props.fullPage} onEmailAdded={() => setChangingEmail(false)} />;\n }\n\n return (\n <MessageCard\n title={t(\"Please check your email inbox\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Resend verification email\")}\n primaryAction={async () => {\n await user.sendVerificationEmail();\n }}\n secondaryButtonText={t(\"Sign out\")}\n secondaryAction={async () => {\n await user.signOut();\n }}\n >\n <Typography>\n {t(\"Please verify your email address \")}\n <span className=\"font-semibold\">{email}</span>\n {\" (\"}\n <button\n type=\"button\"\n className=\"text-primary hover:underline\"\n onClick={() => setChangingEmail(true)}\n >\n {t(\"change\")}\n </button>\n {\"). \"}\n {t(\"Click the button below to resend the verification link.\")}\n </Typography>\n </MessageCard>\n );\n}\n\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { act } from "react";
|
|
2
|
+
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createRoot } from "react-dom/client";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { Onboarding } from "./onboarding.js";
|
|
6
|
+
import { TranslationProviderClient } from "../providers/translation-provider-client.js";
|
|
7
|
+
//#region src/components-page/onboarding.test.tsx
|
|
8
|
+
// @vitest-environment jsdom
|
|
9
|
+
const appMockState = vi.hoisted(() => ({
|
|
10
|
+
app: null,
|
|
11
|
+
user: null
|
|
12
|
+
}));
|
|
13
|
+
vi.mock("..", () => ({
|
|
14
|
+
useStackApp: () => {
|
|
15
|
+
if (appMockState.app == null) throw new Error("Expected test app to be set before rendering.");
|
|
16
|
+
return appMockState.app;
|
|
17
|
+
},
|
|
18
|
+
useUser: () => appMockState.user
|
|
19
|
+
}));
|
|
20
|
+
vi.mock("@hexclave/ui", async () => {
|
|
21
|
+
await import("react");
|
|
22
|
+
return {
|
|
23
|
+
Button: (props) => /* @__PURE__ */ jsx("button", {
|
|
24
|
+
type: "button",
|
|
25
|
+
onClick: props.onClick,
|
|
26
|
+
children: props.children
|
|
27
|
+
}),
|
|
28
|
+
Input: (props) => /* @__PURE__ */ jsx("input", { ...props }),
|
|
29
|
+
Typography: (props) => /* @__PURE__ */ jsx("div", { children: props.children }),
|
|
30
|
+
cn: (...classes) => classes.filter(Boolean).join(" ")
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
const previousActEnvironment = Reflect.get(globalThis, "IS_REACT_ACT_ENVIRONMENT");
|
|
34
|
+
function createAppTestDouble() {
|
|
35
|
+
return {
|
|
36
|
+
redirectToSignIn: vi.fn(async () => {}),
|
|
37
|
+
redirectToAfterSignIn: vi.fn(async () => {})
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function createUserTestDouble(user) {
|
|
41
|
+
return {
|
|
42
|
+
isAnonymous: false,
|
|
43
|
+
isRestricted: true,
|
|
44
|
+
restrictedReason: null,
|
|
45
|
+
restrictedByAdmin: false,
|
|
46
|
+
restrictedByAdminReason: null,
|
|
47
|
+
primaryEmail: "user@example.com",
|
|
48
|
+
signOut: vi.fn(async () => {}),
|
|
49
|
+
...user
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
let root = null;
|
|
53
|
+
let container = null;
|
|
54
|
+
async function renderOnboarding(options) {
|
|
55
|
+
appMockState.app = options.app;
|
|
56
|
+
appMockState.user = options.user;
|
|
57
|
+
container = document.createElement("div");
|
|
58
|
+
document.body.append(container);
|
|
59
|
+
root = createRoot(container);
|
|
60
|
+
await act(async () => {
|
|
61
|
+
root?.render(/* @__PURE__ */ jsx(TranslationProviderClient, {
|
|
62
|
+
quetzalKeys: /* @__PURE__ */ new Map(),
|
|
63
|
+
quetzalLocale: /* @__PURE__ */ new Map(),
|
|
64
|
+
children: /* @__PURE__ */ jsx(Onboarding, {})
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
return container;
|
|
68
|
+
}
|
|
69
|
+
describe("Onboarding", () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", true);
|
|
72
|
+
});
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
act(() => {
|
|
75
|
+
root?.unmount();
|
|
76
|
+
});
|
|
77
|
+
container?.remove();
|
|
78
|
+
root = null;
|
|
79
|
+
container = null;
|
|
80
|
+
appMockState.app = null;
|
|
81
|
+
appMockState.user = null;
|
|
82
|
+
vi.restoreAllMocks();
|
|
83
|
+
Reflect.set(globalThis, "IS_REACT_ACT_ENVIRONMENT", previousActEnvironment);
|
|
84
|
+
});
|
|
85
|
+
it("redirects signed-out users to sign-in", async () => {
|
|
86
|
+
const app = createAppTestDouble();
|
|
87
|
+
const container = await renderOnboarding({
|
|
88
|
+
app,
|
|
89
|
+
user: null
|
|
90
|
+
});
|
|
91
|
+
expect(app.redirectToSignIn).toHaveBeenCalled();
|
|
92
|
+
expect(container.textContent).toBe("");
|
|
93
|
+
});
|
|
94
|
+
it("redirects anonymous users to sign-in", async () => {
|
|
95
|
+
const app = createAppTestDouble();
|
|
96
|
+
const container = await renderOnboarding({
|
|
97
|
+
app,
|
|
98
|
+
user: createUserTestDouble({
|
|
99
|
+
isAnonymous: true,
|
|
100
|
+
restrictedReason: { type: "anonymous" }
|
|
101
|
+
})
|
|
102
|
+
});
|
|
103
|
+
expect(app.redirectToSignIn).toHaveBeenCalled();
|
|
104
|
+
expect(container.textContent).toBe("");
|
|
105
|
+
});
|
|
106
|
+
it("redirects users that are no longer restricted to their destination", async () => {
|
|
107
|
+
const app = createAppTestDouble();
|
|
108
|
+
await renderOnboarding({
|
|
109
|
+
app,
|
|
110
|
+
user: createUserTestDouble({ isRestricted: false })
|
|
111
|
+
});
|
|
112
|
+
expect(app.redirectToAfterSignIn).toHaveBeenCalled();
|
|
113
|
+
expect(app.redirectToSignIn).not.toHaveBeenCalled();
|
|
114
|
+
});
|
|
115
|
+
it("shows the administrator's public reason to admin-restricted users", async () => {
|
|
116
|
+
const container = await renderOnboarding({
|
|
117
|
+
app: createAppTestDouble(),
|
|
118
|
+
user: createUserTestDouble({
|
|
119
|
+
restrictedReason: { type: "restricted_by_administrator" },
|
|
120
|
+
restrictedByAdmin: true,
|
|
121
|
+
restrictedByAdminReason: "Your sign-up was flagged by our fraud prevention rules."
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
expect(container.textContent).toContain("Your account has been restricted");
|
|
125
|
+
expect(container.textContent).toContain("Your sign-up was flagged by our fraud prevention rules.");
|
|
126
|
+
expect(container.textContent).not.toContain("Complete your account setup");
|
|
127
|
+
});
|
|
128
|
+
it("falls back to a generic restricted message when no public reason is set", async () => {
|
|
129
|
+
const container = await renderOnboarding({
|
|
130
|
+
app: createAppTestDouble(),
|
|
131
|
+
user: createUserTestDouble({
|
|
132
|
+
restrictedReason: { type: "restricted_by_administrator" },
|
|
133
|
+
restrictedByAdmin: true,
|
|
134
|
+
restrictedByAdminReason: null
|
|
135
|
+
})
|
|
136
|
+
});
|
|
137
|
+
expect(container.textContent).toContain("Your account has been restricted");
|
|
138
|
+
expect(container.textContent).toContain("An administrator has restricted your account.");
|
|
139
|
+
});
|
|
140
|
+
it("shows the setup-required message for unknown restricted reasons", async () => {
|
|
141
|
+
expect((await renderOnboarding({
|
|
142
|
+
app: createAppTestDouble(),
|
|
143
|
+
user: createUserTestDouble({ restrictedReason: { type: "some_future_reason" } })
|
|
144
|
+
})).textContent).toContain("Complete your account setup");
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
//#endregion
|
|
148
|
+
export {};
|
|
149
|
+
|
|
150
|
+
//# sourceMappingURL=onboarding.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.test.js","names":[],"sources":["../../../src/components-page/onboarding.test.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY UNLESS YOU ALSO EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n// @vitest-environment jsdom\n\nimport React, { act } from \"react\";\nimport { createRoot, type Root } from \"react-dom/client\";\nimport { afterEach, beforeEach, describe, expect, it, vi } from \"vitest\";\nimport type { CurrentUser } from \"../lib/hexclave-app\";\nimport type { StackClientApp } from \"../lib/hexclave-app/apps/interfaces/client-app\";\nimport { TranslationProviderClient } from \"../providers/translation-provider-client\";\nimport { Onboarding } from \"./onboarding\";\n\nconst appMockState = vi.hoisted(() => ({\n app: null as unknown,\n user: null as unknown,\n}));\n\nvi.mock(\"..\", () => ({\n useStackApp: () => {\n if (appMockState.app == null) {\n throw new Error(\"Expected test app to be set before rendering.\");\n }\n return appMockState.app;\n },\n useUser: () => appMockState.user,\n}));\n\nvi.mock(\"@hexclave/ui\", async () => {\n const React = await import(\"react\");\n return {\n Button: (props: { children: React.ReactNode, onClick?: () => void }) => (\n <button type=\"button\" onClick={props.onClick}>{props.children}</button>\n ),\n Input: (props: Record<string, unknown>) => <input {...props} />,\n Typography: (props: { children: React.ReactNode }) => <div>{props.children}</div>,\n cn: (...classes: (string | false | null | undefined)[]) => classes.filter(Boolean).join(\" \"),\n };\n});\n\nconst previousActEnvironment = Reflect.get(globalThis, \"IS_REACT_ACT_ENVIRONMENT\");\n\nfunction createAppTestDouble() {\n const app = {\n redirectToSignIn: vi.fn(async () => {}),\n redirectToAfterSignIn: vi.fn(async () => {}),\n };\n\n // This test double intentionally implements only the StackClientApp surface that Onboarding touches.\n return app as unknown as StackClientApp<true> & {\n redirectToSignIn: ReturnType<typeof vi.fn>,\n redirectToAfterSignIn: ReturnType<typeof vi.fn>,\n };\n}\n\nfunction createUserTestDouble(user: Partial<CurrentUser>) {\n // Same as the app test double: only the surface Onboarding touches.\n return {\n isAnonymous: false,\n isRestricted: true,\n restrictedReason: null,\n restrictedByAdmin: false,\n restrictedByAdminReason: null,\n primaryEmail: \"user@example.com\",\n signOut: vi.fn(async () => {}),\n ...user,\n } as unknown as CurrentUser;\n}\n\nlet root: Root | null = null;\nlet container: HTMLDivElement | null = null;\n\nasync function renderOnboarding(options: {\n app: StackClientApp<true>,\n user: unknown,\n}) {\n appMockState.app = options.app;\n appMockState.user = options.user;\n container = document.createElement(\"div\");\n document.body.append(container);\n root = createRoot(container);\n await act(async () => {\n root?.render(\n <TranslationProviderClient quetzalKeys={new Map()} quetzalLocale={new Map()}>\n <Onboarding />\n </TranslationProviderClient>\n );\n });\n return container;\n}\n\ndescribe(\"Onboarding\", () => {\n beforeEach(() => {\n Reflect.set(globalThis, \"IS_REACT_ACT_ENVIRONMENT\", true);\n });\n\n afterEach(() => {\n act(() => {\n root?.unmount();\n });\n container?.remove();\n root = null;\n container = null;\n appMockState.app = null;\n appMockState.user = null;\n vi.restoreAllMocks();\n Reflect.set(globalThis, \"IS_REACT_ACT_ENVIRONMENT\", previousActEnvironment);\n });\n\n it(\"redirects signed-out users to sign-in\", async () => {\n const app = createAppTestDouble();\n\n const container = await renderOnboarding({ app, user: null });\n\n expect(app.redirectToSignIn).toHaveBeenCalled();\n expect(container.textContent).toBe(\"\");\n });\n\n it(\"redirects anonymous users to sign-in\", async () => {\n const app = createAppTestDouble();\n\n const container = await renderOnboarding({ app, user: createUserTestDouble({ isAnonymous: true, restrictedReason: { type: \"anonymous\" } }) });\n\n expect(app.redirectToSignIn).toHaveBeenCalled();\n expect(container.textContent).toBe(\"\");\n });\n\n it(\"redirects users that are no longer restricted to their destination\", async () => {\n const app = createAppTestDouble();\n\n await renderOnboarding({ app, user: createUserTestDouble({ isRestricted: false }) });\n\n expect(app.redirectToAfterSignIn).toHaveBeenCalled();\n expect(app.redirectToSignIn).not.toHaveBeenCalled();\n });\n\n it(\"shows the administrator's public reason to admin-restricted users\", async () => {\n const app = createAppTestDouble();\n\n const container = await renderOnboarding({\n app,\n user: createUserTestDouble({\n restrictedReason: { type: \"restricted_by_administrator\" },\n restrictedByAdmin: true,\n restrictedByAdminReason: \"Your sign-up was flagged by our fraud prevention rules.\",\n }),\n });\n\n expect(container.textContent).toContain(\"Your account has been restricted\");\n expect(container.textContent).toContain(\"Your sign-up was flagged by our fraud prevention rules.\");\n expect(container.textContent).not.toContain(\"Complete your account setup\");\n });\n\n it(\"falls back to a generic restricted message when no public reason is set\", async () => {\n const app = createAppTestDouble();\n\n const container = await renderOnboarding({\n app,\n user: createUserTestDouble({\n restrictedReason: { type: \"restricted_by_administrator\" },\n restrictedByAdmin: true,\n restrictedByAdminReason: null,\n }),\n });\n\n expect(container.textContent).toContain(\"Your account has been restricted\");\n expect(container.textContent).toContain(\"An administrator has restricted your account.\");\n });\n\n it(\"shows the setup-required message for unknown restricted reasons\", async () => {\n const app = createAppTestDouble();\n\n const container = await renderOnboarding({\n app,\n // The reason types are a closed union, so an unknown reason can only come from a newer backend.\n user: createUserTestDouble({ restrictedReason: { type: \"some_future_reason\" } as unknown as CurrentUser[\"restrictedReason\"] }),\n });\n\n expect(container.textContent).toContain(\"Complete your account setup\");\n });\n});\n"],"mappings":"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"}
|