-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Detect and upgrade users with lissing metadata
- Loading branch information
1 parent
1a80404
commit c158f3d
Showing
13 changed files
with
401 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -93,6 +93,7 @@ | |
"posttest", | ||
"pptxgen", | ||
"pptxgenjs", | ||
"Preloadable", | ||
"PSED", | ||
"PSHE", | ||
"psql", | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,128 +1,46 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
import { useEffect, useRef } from "react"; | ||
|
||
import { useUser } from "@clerk/nextjs"; | ||
import logger from "@oakai/logger/browser"; | ||
import { | ||
OakBox, | ||
OakFlex, | ||
OakHeading, | ||
OakLink, | ||
OakP, | ||
OakPrimaryButton, | ||
OakSpan, | ||
} from "@oaknational/oak-components"; | ||
import Link from "next/link"; | ||
import { useReloadSession } from "hooks/useReloadSession"; | ||
|
||
import Button from "@/components/Button"; | ||
import CheckBox from "@/components/CheckBox"; | ||
import SignUpSignInLayout from "@/components/SignUpSignInLayout"; | ||
import TermsContent from "@/components/TermsContent"; | ||
import { AcceptTermsForm } from "@/components/Onboarding/AcceptTermsForm"; | ||
import { LegacyUpgradeNotice } from "@/components/Onboarding/LegacyUpgradeNotice"; | ||
import { trpc } from "@/utils/trpc"; | ||
|
||
export const OnBoarding = () => { | ||
const [dropDownOpen, setDropDownOpen] = useState(true); | ||
const [termsAcceptedLocal, setTermsAcceptedLocal] = useState(false); | ||
const [privacyAcceptedLocal, setPrivacyAcceptedLocal] = useState(false); | ||
const acceptTerms = trpc.auth.acceptTerms.useMutation(); | ||
|
||
const handleAcceptTermsOfUse = async () => { | ||
try { | ||
const response = await acceptTerms.mutateAsync({ | ||
termsOfUse: new Date(), | ||
privacyPolicy: privacyAcceptedLocal ? new Date() : false, | ||
}); | ||
|
||
if (!response?.acceptedTermsOfUse) { | ||
throw new Error("Could not accept terms of use"); | ||
const { user } = useUser(); | ||
const reloadSession = useReloadSession(); | ||
const setDemoStatus = trpc.auth.setDemoStatus.useMutation(); | ||
|
||
const userHasAlreadyAcceptedTerms = | ||
user?.publicMetadata?.["labs"]?.["isOnboarded"]; | ||
|
||
// Edge case: Legacy users have already accepted terms but don't have a demo status | ||
const isHandlingLegacyCase = useRef(false); | ||
useEffect(() => { | ||
async function handleDemoStatusSet() { | ||
if (userHasAlreadyAcceptedTerms && !isHandlingLegacyCase.current) { | ||
isHandlingLegacyCase.current = true; | ||
logger.debug("User has already accepted terms"); | ||
await setDemoStatus.mutateAsync(); | ||
logger.debug("Demo status set successfully"); | ||
await reloadSession(); | ||
logger.debug("Session token refreshed successfully. Redirecting"); | ||
window.location.href = "/"; | ||
} | ||
|
||
logger.debug("Terms of use accepted successfully."); | ||
window.location.href = "/"; | ||
} catch (error) { | ||
logger.error(error, "An error occurred while accepting terms of use"); | ||
} | ||
}; | ||
|
||
return ( | ||
<SignUpSignInLayout loaded={true}> | ||
<OakBox | ||
$mh="auto" | ||
$borderRadius="border-radius-m" | ||
$background="white" | ||
$pa="inner-padding-xl2" | ||
$maxWidth={"all-spacing-22"} | ||
> | ||
<OakHeading $font="heading-6" tag="h1"> | ||
This product is experimental and uses AI | ||
</OakHeading> | ||
<OakBox $mt="space-between-s"> | ||
<OakP> | ||
We have worked to ensure that our tools are as high quality and as | ||
safe as possible but we cannot guarantee accuracy. Please use with | ||
caution. | ||
</OakP> | ||
</OakBox> | ||
handleDemoStatusSet(); | ||
}, [userHasAlreadyAcceptedTerms, setDemoStatus, reloadSession]); | ||
|
||
<OakBox $pt="inner-padding-m" $mv="space-between-s"> | ||
<CheckBox | ||
label="Accept" | ||
setValue={setPrivacyAcceptedLocal} | ||
size="base" | ||
> | ||
<OakSpan> | ||
Keep me updated with latest Oak AI experiments, resources and | ||
other helpful content by email. You can unsubscribe at any time. | ||
See our{" "} | ||
<OakLink element={Link} href="/legal/privacy"> | ||
privacy policy | ||
</OakLink> | ||
. | ||
</OakSpan> | ||
</CheckBox> | ||
</OakBox> | ||
if (userHasAlreadyAcceptedTerms) { | ||
return <LegacyUpgradeNotice />; | ||
} | ||
|
||
{termsAcceptedLocal ? ( | ||
<OakFlex $flexDirection="column" $gap="all-spacing-7"> | ||
<p> | ||
Terms accepted, if the page does not reload please refresh and | ||
navigate to home. | ||
</p> | ||
</OakFlex> | ||
) : ( | ||
<OakFlex | ||
$flexDirection="row" | ||
$justifyContent="between" | ||
$gap="all-spacing-4" | ||
$width="100%" | ||
$alignItems="center" | ||
$mt="space-between-l" | ||
> | ||
<Button | ||
variant="text-link" | ||
onClick={() => setDropDownOpen(!dropDownOpen)} | ||
icon={dropDownOpen ? "chevron-down" : "chevron-up"} | ||
> | ||
See terms | ||
</Button> | ||
<OakPrimaryButton | ||
onClick={() => { | ||
handleAcceptTermsOfUse(); | ||
setTermsAcceptedLocal(true); | ||
}} | ||
> | ||
I understand | ||
</OakPrimaryButton> | ||
</OakFlex> | ||
)} | ||
{!dropDownOpen && ( | ||
<OakBox> | ||
<TermsContent /> | ||
</OakBox> | ||
)} | ||
</OakBox> | ||
</SignUpSignInLayout> | ||
); | ||
// For the typical new user, show the accept terms form | ||
return <AcceptTermsForm />; | ||
}; | ||
|
||
export default OnBoarding; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import OnBoarding from "./onboarding"; | ||
|
||
export default function OnBoardingPage() { | ||
export default async function OnBoardingPage() { | ||
return <OnBoarding />; | ||
} |
138 changes: 138 additions & 0 deletions
138
apps/nextjs/src/components/Onboarding/AcceptTermsForm.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
"use client"; | ||
|
||
import { useState } from "react"; | ||
|
||
import { useUser } from "@clerk/nextjs"; | ||
import logger from "@oakai/logger/browser"; | ||
import { | ||
OakBox, | ||
OakFlex, | ||
OakHeading, | ||
OakLink, | ||
OakP, | ||
OakPrimaryButton, | ||
OakSpan, | ||
} from "@oaknational/oak-components"; | ||
import { useReloadSession } from "hooks/useReloadSession"; | ||
import Link from "next/link"; | ||
|
||
import Button from "@/components/Button"; | ||
import CheckBox from "@/components/CheckBox"; | ||
import SignUpSignInLayout from "@/components/SignUpSignInLayout"; | ||
import TermsContent from "@/components/TermsContent"; | ||
import { trpc } from "@/utils/trpc"; | ||
|
||
export const AcceptTermsForm = ({}) => { | ||
const [dropDownOpen, setDropDownOpen] = useState(true); | ||
const { isLoaded } = useUser(); | ||
const reloadSession = useReloadSession(); | ||
|
||
const [termsAcceptedLocal, setTermsAcceptedLocal] = useState(false); | ||
const [privacyAcceptedLocal, setPrivacyAcceptedLocal] = useState(false); | ||
const setDemoStatus = trpc.auth.setDemoStatus.useMutation(); | ||
const acceptTerms = trpc.auth.acceptTerms.useMutation({}); | ||
|
||
const handleAcceptTermsOfUse = async () => { | ||
try { | ||
await setDemoStatus.mutateAsync(); | ||
logger.debug("Demo status set successfully"); | ||
|
||
const response = await acceptTerms.mutateAsync({ | ||
termsOfUse: new Date(), | ||
privacyPolicy: privacyAcceptedLocal ? new Date() : false, | ||
}); | ||
|
||
if (!response?.acceptedTermsOfUse) { | ||
throw new Error("Could not accept terms of use"); | ||
} | ||
logger.debug("Terms of use accepted successfully."); | ||
|
||
await reloadSession(); | ||
logger.debug("Session token refreshed successfully. Redirecting"); | ||
|
||
window.location.href = "/"; | ||
} catch (error) { | ||
logger.error(error, "An error occurred while accepting terms of use"); | ||
} | ||
}; | ||
|
||
return ( | ||
<SignUpSignInLayout loaded={isLoaded}> | ||
<OakBox | ||
$mh="auto" | ||
$borderRadius="border-radius-m" | ||
$background="white" | ||
$pa="inner-padding-xl2" | ||
$maxWidth={"all-spacing-22"} | ||
> | ||
<OakHeading $font="heading-6" tag="h1"> | ||
This product is experimental and uses AI | ||
</OakHeading> | ||
<OakBox $mt="space-between-s"> | ||
<OakP> | ||
We have worked to ensure that our tools are as high quality and as | ||
safe as possible but we cannot guarantee accuracy. Please use with | ||
caution. | ||
</OakP> | ||
</OakBox> | ||
|
||
<OakBox $pt="inner-padding-m" $mv="space-between-s"> | ||
<CheckBox | ||
label="Accept" | ||
setValue={setPrivacyAcceptedLocal} | ||
size="base" | ||
> | ||
<OakSpan> | ||
Keep me updated with latest Oak AI experiments, resources and | ||
other helpful content by email. You can unsubscribe at any time. | ||
See our{" "} | ||
<OakLink element={Link} href="/legal/privacy"> | ||
privacy policy | ||
</OakLink> | ||
. | ||
</OakSpan> | ||
</CheckBox> | ||
</OakBox> | ||
|
||
{termsAcceptedLocal ? ( | ||
<OakFlex $flexDirection="column" $gap="all-spacing-7"> | ||
<p> | ||
Terms accepted, if the page does not reload please refresh and | ||
navigate to home. | ||
</p> | ||
</OakFlex> | ||
) : ( | ||
<OakFlex | ||
$flexDirection="row" | ||
$justifyContent="between" | ||
$gap="all-spacing-4" | ||
$width="100%" | ||
$alignItems="center" | ||
$mt="space-between-l" | ||
> | ||
<Button | ||
variant="text-link" | ||
onClick={() => setDropDownOpen(!dropDownOpen)} | ||
icon={dropDownOpen ? "chevron-down" : "chevron-up"} | ||
> | ||
See terms | ||
</Button> | ||
<OakPrimaryButton | ||
onClick={() => { | ||
handleAcceptTermsOfUse(); | ||
setTermsAcceptedLocal(true); | ||
}} | ||
> | ||
I understand | ||
</OakPrimaryButton> | ||
</OakFlex> | ||
)} | ||
{!dropDownOpen && ( | ||
<OakBox> | ||
<TermsContent /> | ||
</OakBox> | ||
)} | ||
</OakBox> | ||
</SignUpSignInLayout> | ||
); | ||
}; |
28 changes: 28 additions & 0 deletions
28
apps/nextjs/src/components/Onboarding/LegacyUpgradeNotice.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
"use client"; | ||
|
||
import { OakBox, OakFlex, OakHeading } from "@oaknational/oak-components"; | ||
|
||
import LoadingWheel from "@/components/LoadingWheel"; | ||
import SignUpSignInLayout from "@/components/SignUpSignInLayout"; | ||
|
||
export const LegacyUpgradeNotice = ({}) => { | ||
return ( | ||
<SignUpSignInLayout loaded> | ||
<OakBox | ||
$mh="auto" | ||
$ml="space-between-l" | ||
$borderRadius="border-radius-m" | ||
$background="white" | ||
$pa="inner-padding-xl2" | ||
$maxWidth={"all-spacing-22"} | ||
> | ||
<OakHeading $font="heading-6" tag="h1"> | ||
Preparing your account | ||
</OakHeading> | ||
<OakFlex $mt="space-between-s" $width={"100%"} $justifyContent="center"> | ||
<LoadingWheel /> | ||
</OakFlex> | ||
</OakBox> | ||
</SignUpSignInLayout> | ||
); | ||
}; |
Oops, something went wrong.