Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .cursor/rules/components.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -328,10 +328,9 @@ import { combineClassNames } from "@/submodules/javascript-functions/general";
```typescript
// pages/settings.tsx
<button
className="link disabled:opacity-50 disabled:cursor-not-allowed"
data-testid="forgot-password"
disabled={backButtonDisabled}
onClick={() => router.push("/cognition")}
className="link disabled:opacity-50 disabled:cursor-not-allowed"
disabled={backButtonDisabled}
onClick={() => router.push("/cognition")}
>
{t('back')}
</button>
Expand Down
2 changes: 1 addition & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function MyApp({ Component, pageProps }: AppProps) {
<link rel="icon" type="image/x-icon"
href="https://uploads-ssl.webflow.com/61e47fafb12bd56b40022a49/62349d6d1d8f3f519b8fad79_kern-favicon.png"></link>
</Head>
<div data-testid="app-react">
<div>
<ThemeProvider theme={theme}>
<GlobalStyle />
<Component {...pageProps} />
Expand Down
27 changes: 27 additions & 0 deletions pages/error.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { NextPage } from "next"
import Head from "next/head"
import { KernLogo } from "@/pkg/ui/Icons"

const ErrorPage: NextPage = () => {

return (
<>
<Head>
<title>Error</title>
</Head>

<div className="app-container">
<KernLogo />
<div id="invite">
<h2 className="title">An error occurred</h2>

<p>Please contact support if the problem persists.</p>
</div>
</div>
<div className="img-container">
</div>
</>
)
}

export default ErrorPage
83 changes: 83 additions & 0 deletions pages/invite.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { RecoveryFlow, UpdateRecoveryFlowBody } from "@ory/client"
import type { NextPage } from "next"
import Head from "next/head"
import { useRouter } from "next/router"
import { useEffect, useState } from "react"

import { Flow } from "../pkg"
import { handleFlowError } from "../pkg/errors"
import ory from "../pkg/sdk"
import { KernLogo } from "@/pkg/ui/Icons"

const InvitePage: NextPage = () => {
const router = useRouter()
const { flow: flowId } = router.query

const [flow, setFlow] = useState<RecoveryFlow | null>(null)

useEffect(() => {
if (!router.isReady || !flowId) return
ory
.getRecoveryFlow({ id: String(flowId) })
.then(({ data }) => {
Comment thread
JWittmeyer marked this conversation as resolved.
data.ui.nodes.forEach((node) => {
Comment thread
JWittmeyer marked this conversation as resolved.
const attrs = node.attributes as { name?: string }
if (attrs.name === "code") {
node.meta.label = { text: "Enter the code from your email", id: 0, type: "info" }
Comment thread
JWittmeyer marked this conversation as resolved.
}
if (attrs.name === "email") {
node.meta.label = { text: "Your email address", id: 0, type: "info" }
}
})
setFlow(data)
})
.catch((err) => handleFlowError(router, "recovery", setFlow)(err))
}, [router.isReady, flowId])

Comment thread
JWittmeyer marked this conversation as resolved.
const onSubmit = (values: UpdateRecoveryFlowBody) => {
if (!flow?.id) return
return router
.push(`/invite?flow=${flow?.id}`, undefined, { shallow: true })
.then(() =>
Comment thread
lumburovskalina marked this conversation as resolved.
ory
.updateRecoveryFlow({
flow: String(flow?.id),
updateRecoveryFlowBody: values,
})
.then(({ data }) => {
setFlow(data)
})
.catch(handleFlowError(router, "recovery", setFlow))
Comment thread
JWittmeyer marked this conversation as resolved.
Comment thread
JWittmeyer marked this conversation as resolved.
.catch((err: any) => {
if (err.response?.status === 400) {
setFlow(err.response?.data)
return
}
throw err
})
)
}

if (!flow) return null

return (
<>
<Head>
<title>Accept Invitation</title>
</Head>

<div className="app-container">
<KernLogo />
<div id="invite">
<h2 className="title">Accept Your Invitation</h2>

<Flow onSubmit={onSubmit} flow={flow} />
</div>
</div>
<div className="img-container">
</div>
</>
)
}

export default InvitePage
6 changes: 3 additions & 3 deletions pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ const Login: NextPage = () => {
<div id="login">
<h2 className="title">Sign in to your account</h2>
<p className="text-paragraph">Or
<a className="link" data-testid="cta-link" href="/auth/registration"> Register account </a> -
<a className="link" href="/auth/registration"> Register account </a> -
no credit card required!
</p>
<div className="ui-container">
Expand All @@ -159,9 +159,9 @@ const Login: NextPage = () => {
{
!isAccLinkageRequested ?
<>
{displayMailForm ? <a className="link" data-testid="forgot-password" href="/auth/recovery">Forgot your password?</a> : null}
{displayMailForm ? <a className="link" href="/auth/recovery">Forgot your password?</a> : null}
</>
: <a className="link" data-testid="back-to-login" href="/auth/login">Go back to login</a>
: <a className="link" href="/auth/login">Go back to login</a>
}
</div>
</div>
Expand Down
129 changes: 64 additions & 65 deletions pages/recovery.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { RecoveryFlow, UpdateRecoveryFlowBody } from "@ory/client"
import { AxiosError } from "axios"
import type { NextPage } from "next"
import Head from "next/head"
import { useRouter } from "next/router"
Expand All @@ -11,99 +10,99 @@ import ory from "../pkg/sdk"
import { KernLogo } from "@/pkg/ui/Icons"

const Recovery: NextPage = () => {
const [initialFlow, setInitialFlow]: any = useState<RecoveryFlow>()
const [changedFlow, setChangedFlow] = useState<RecoveryFlow>()

// Get ?flow=... from the URL
const router = useRouter()
const { flow: flowId, return_to: returnTo } = router.query
const { flow: flowId } = router.query

useEffect(() => {
// If the router is not ready yet, or we already have a flow, do nothing.
if (!router.isReady || initialFlow) {
return
}
const [flow, setFlow] = useState<RecoveryFlow>()

// If ?flow=.. was in the URL, we fetch it
if (flowId) {
ory
.getRecoveryFlow({ id: String(flowId) })
.then(({ data }) => {
setInitialFlow(data)
})
.catch(handleFlowError(router, "recovery", setInitialFlow))
return
}
useEffect(() => {
if (!router.isReady) return

// Otherwise we initialize it
ory
.createBrowserRecoveryFlow()
.then(({ data }) => {
setInitialFlow(data)
})
.catch(handleFlowError(router, "recovery", setInitialFlow))
.catch((err: AxiosError) => {
// If the previous handler did not catch the error it's most likely a form validation error
if (err.response?.status === 400) {
// Yup, it is!
setInitialFlow(err.response?.data)
return
const fetchFlow = async () => {
try {
let data: RecoveryFlow
if (flowId) {
const res = await ory.getRecoveryFlow({ id: String(flowId) })
data = res.data
Comment thread
JWittmeyer marked this conversation as resolved.
} else {
const res = await ory.createBrowserRecoveryFlow({
returnTo: "/settings",
})
data = res.data
Comment thread
JWittmeyer marked this conversation as resolved.
}

return Promise.reject(err)
})
}, [flowId, router, router.isReady, returnTo, initialFlow])
data.ui.nodes.forEach((node) => {
const attrs = node.attributes as { name?: string; value?: string; disabled?: boolean; type?: string; required?: boolean }
if (attrs.name === "code") {
node.meta.label = {
text: "Enter your recovery code",
Comment thread
JWittmeyer marked this conversation as resolved.
id: 0,
type: "info",
}
attrs.required = false
}
if (attrs.name === "email") {
node.meta.label = { text: "Your email address", id: 0, type: "info" }
}
if (attrs.type === "button" || (attrs.name === "method" && attrs.value === "link")) {
attrs.disabled = false
}
})

useEffect(() => {
if (!initialFlow) return
initialFlow.ui.nodes[1].meta.label = { text: "Email address", id: 0, type: "info" }
if (initialFlow.ui.nodes[2].meta.label) {
initialFlow.ui.nodes[2].meta.label.text = "Send reset code to mail"
setFlow(data)
} catch (err: any) {
handleFlowError(router, "recovery", setFlow)(err)
Comment thread
JWittmeyer marked this conversation as resolved.
}
}
Comment thread
JWittmeyer marked this conversation as resolved.
setChangedFlow(initialFlow)
}, [initialFlow])

fetchFlow()
Comment thread
JWittmeyer marked this conversation as resolved.
}, [router.isReady, flowId])

const onSubmit = (values: UpdateRecoveryFlowBody) =>
router
Comment thread
JWittmeyer marked this conversation as resolved.
// On submission, add the flow ID to the URL but do not navigate. This prevents the user loosing
// his data when she/he reloads the page.
.push(`/recovery?flow=${initialFlow?.id}`, undefined, { shallow: true })
.push(`${router.pathname}?flow=${flow?.id}`, undefined, {
shallow: true,
})
.then(() =>
ory
.updateRecoveryFlow({
flow: String(initialFlow?.id),
flow: String(flow?.id),
updateRecoveryFlowBody: values,
})
.then(({ data }) => {
// Form submission was successful, show the message to the user!
setInitialFlow(data)
setFlow(data)
})
.catch(handleFlowError(router, "recovery", setInitialFlow))
.catch((err: AxiosError) => {
switch (err.response?.status) {
case 400:
// Status code 400 implies the form validation had an error
setInitialFlow(err.response?.data)
return
.catch(handleFlowError(router, "recovery", setFlow))
.catch((err: any) => {
Comment thread
JWittmeyer marked this conversation as resolved.
if (err.response?.status === 400) {
setFlow(err.response?.data)
return
}

throw err
}),
})
)

if (!flow) return null

return (
<>
<Head>
<title>Recovery</title>
<meta name="description" content="NextJS + React + Vercel + Ory" />
</Head>

<div className="app-container">
<KernLogo />
<div id="verification">
<h2 className="title">Recover your account</h2>
<Flow onSubmit={onSubmit} flow={changedFlow} />
<div id="recovery">
<h2 className="title">
Recover your account
</h2>

<Flow onSubmit={onSubmit} flow={flow} />

<div className="link-container">
<a className="link" data-testid="forgot-password" href="/auth/login">Go back to login</a>
<a className="link" href="/auth/login">
Comment thread
lumburovskalina marked this conversation as resolved.
Go back to login
</a>
</div>
</div>
</div>
Expand All @@ -113,4 +112,4 @@ const Recovery: NextPage = () => {
)
}

export default Recovery
export default Recovery
2 changes: 1 addition & 1 deletion pages/registration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const Registration: NextPage = () => {
</div>

<div className="link-container">
<a className="link" data-testid="forgot-password" href="/auth/login">Go back to login</a>
<a className="link" href="/auth/login">Go back to login</a>
</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion pages/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ const Settings: NextPage = () => {
</div>) : (<> </>)}

<div className="link-container">
<button className="link disabled:opacity-50 disabled:cursor-not-allowed" data-testid="forgot-password" disabled={backButtonDisabled} onClick={() => {
<button className="link disabled:opacity-50 disabled:cursor-not-allowed" disabled={backButtonDisabled} onClick={() => {
router.push("/cognition")
}}>{t('back')}</button>
</div>
Expand Down
Loading