1
0
Fork 0

wip(org): Work on welcome pages

This commit is contained in:
Joost De Cock 2023-01-23 20:23:53 +01:00
parent e75d88c67f
commit b997a07f0e
15 changed files with 564 additions and 131 deletions

View file

@ -0,0 +1,10 @@
title: Are you comfortable with measurements being compared?
yes: Yes, in case it may help me
yesd: |
We will occasionally show how you or your people compare to other people.
This allows us to detect potential problems in your measurements or patterns.
no: No, never compare
nod: |
We will never compare you or your people to other people.
This will limit our ability to warn you about potential problems in your measurements or patterns.
continue: Continue

View file

@ -0,0 +1,81 @@
import { useState } from 'react'
import { useTranslation } from 'next-i18next'
import useBackend from 'site/hooks/useBackend.js'
import Link from 'next/link'
import { Choice } from '../shared.js'
export const namespaces = ['compare']
const welcomeSteps = {
1: {
href: '/docs/guide',
steps: 0,
},
2: {
href: '/docs/guide',
steps: 3,
},
3: {
href: '/welcome/username',
steps: 5,
},
4: {
href: '/welcome/username',
steps: 7,
},
5: {
href: '/',
steps: 0,
},
}
export const CompareSettings = ({ app, title = false, welcome = false }) => {
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const [selection, setSelection] = useState(app.account?.compare ? 'yes' : 'no')
const update = async (val) => {
if (val !== selection) {
const result = await backend.updateAccount({ compare: val === 'yes' ? true : false })
if (result) setSelection(val)
}
}
return (
<>
{title ? <h1 className="text-4xl">{t('title')}</h1> : null}
{['yes', 'no'].map((val) => (
<Choice val={val} t={t} update={update} current={selection} bool>
<span className="block text-lg leading-5">
{selection === 1 && val === 2 ? t('showMore') : t(`${val}`)}
</span>
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
</Choice>
))}
{welcome ? (
<>
<Link
href={welcomeSteps[app.account.control].href}
className="btn btn-primary w-full mt-12"
>
{t('continue')}
</Link>
{welcomeSteps[app.account.control].steps ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={400 / welcomeSteps[app.account.control].steps}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
4 / {welcomeSteps[app.account.control].steps}
</span>
</>
) : null}
</>
) : null}
</>
)
}
export default CompareSettings

View file

@ -9,10 +9,5 @@
5t: Get out of my way
5d: Reveals all features, removes all handrails and safety checks.
showMore: Show more options
makeItYours: Make it yours
toYourAccount: To your account
enableMfa: Enable multi-factor authentication
setUnits: Choose units
setUsername: Choose a username
gettingStarted: "To the <em>Getting Started</em> documentation"
toHome: To the home page
continue: Continue
title: What do you prefer?

View file

@ -2,117 +2,34 @@ import { useState } from 'react'
import { useTranslation } from 'next-i18next'
import useBackend from 'site/hooks/useBackend.js'
import Link from 'next/link'
const Button = ({ val, update, t, current }) => {
const active = val === current
if (current === 1 && val > 2) return null
if (current === 2 && val > 3) return null
if (current === 3 && val > 4) return null
return (
<button
className={`
btn w-full mt-2 btn-secondary
flex flex-row flex-nowrap items-center gap-4 py-4 h-auto
border border-secondary
${
active
? ''
: 'hover:bg-opacity-20 hover:bg-secondary btn-ghost border border-secondary hover:border hover:border-secondary'
}
${current > 1 ? 'justify-start text-left' : 'justify-center text-center'}
`}
onClick={() => update(val)}
>
{current > 1 ? (
<span
className={`
p-4 w-8 h-8 shrink-0 rounded-full text-center p-0 py-2
${active ? 'bg-base-100 text-secondary' : 'bg-secondary text-secondary-content'}
`}
>
{val}
</span>
) : null}
<div
className={`normal-case
${active ? 'text-secondary-content' : 'text-base-content'}
`}
>
<span className="block text-lg leading-5">
{current === 1 && val === 2 ? t('showMore') : t(`${val}t`)}
</span>
{current > 1 ? (
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
) : null}
</div>
</button>
)
}
import { Choice } from '../shared.js'
export const namespaces = ['control']
const NextSteps = ({ val, t }) => {
if (val === 1)
return (
<Link href="/" className="btn btn-primary w-full mt-12">
{t('toHome')}
</Link>
)
if (val === 2)
return [
<Link key={1} href="/welcome/units" className="btn btn-primary w-full mt-12">
{t('setUnits')}
</Link>,
<Link key={2} href="/docs/guide" className="btn btn-primary btn-outline w-full mt-2">
<span dangerouslySetInnerHTML={{ __html: t('gettingStarted') }} className="case-normal" />
</Link>,
<Link key={3} href="/" className="btn btn-ghost w-full mt-2">
{t('toHome')}
</Link>,
]
if (val === 3)
return [
<Link key={1} href="/welcome/units" className="btn btn-primary w-full mt-12">
{t('setUsername')}
</Link>,
<Link key={2} href="/welcome/units" className="btn btn-primary btn-outline w-full mt-2">
{t('setUnits')}
</Link>,
<Link key={3} href="/docs/guide" className="btn btn-ghost w-full mt-2">
<span dangerouslySetInnerHTML={{ __html: t('gettingStarted') }} className="case-normal" />
</Link>,
]
if (val === 4)
return [
<Link key={1} href="/welcome/mfa" className="btn btn-primary w-full mt-12 normal-case">
{t('enableMfa')}
</Link>,
<div className="flex flex-row gap-2" key={2}>
<Link href="/welcome/units" className="btn btn-primary btn-outline grow mt-2">
{t('setUnits')}
</Link>
<Link href="/welcome/username" className="btn btn-primary btn-outline grow mt-2">
{t('setUsername')}
</Link>
</div>,
<Link key={3} href="/docs/guide" className="btn btn-ghost w-full mt-2">
<span dangerouslySetInnerHTML={{ __html: t('gettingStarted') }} className="case-normal" />
</Link>,
]
if (val === 5)
return [
<Link href="/account" className="btn btn-primary w-full mt-12">
{t('toYourAccount')}
</Link>,
<Link key={3} href="/docs/guide" className="btn btn-ghost w-full mt-2">
<span dangerouslySetInnerHTML={{ __html: t('gettingStarted') }} className="case-normal" />
</Link>,
]
const welcomeSteps = {
1: {
href: '/docs/guide/',
steps: 0,
},
2: {
href: '/welcome/newsletter',
steps: 3,
},
3: {
href: '/welcome/newsletter',
steps: 5,
},
4: {
href: '/welcome/newsletter',
steps: 7,
},
5: {
href: '/',
steps: 0,
},
}
export const ControlSettings = ({ app }) => {
export const ControlSettings = ({ app, title = false, welcome = false }) => {
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const [selection, setSelection] = useState(app.account.control || 2)
@ -126,10 +43,43 @@ export const ControlSettings = ({ app }) => {
return (
<>
{[1, 2, 3, 4, 5].map((val) => (
<Button val={val} t={t} update={update} current={selection} />
))}
<NextSteps val={selection} t={t} />
{title ? <h1 className="text-4xl">{t('title')}</h1> : null}
{[1, 2, 3, 4, 5].map((val) => {
if (selection === 1 && val > 2) return null
if (selection === 2 && val > 3) return null
if (selection === 3 && val > 4) return null
if (selection === 5 && val < 4) return null
else
return (
<Choice val={val} t={t} update={update} current={selection}>
<span className="block text-lg leading-5">
{selection === 1 && val === 2 ? t('showMore') : t(`${val}t`)}
</span>
{selection > 1 ? (
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
) : null}
</Choice>
)
})}
{welcome ? (
<>
<Link href={welcomeSteps[selection].href} className="btn btn-primary w-full mt-12">
{t('continue')}
</Link>
{welcomeSteps[selection].steps ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={100 / welcomeSteps[selection].steps}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
1 / {welcomeSteps[selection].steps}
</span>
</>
) : null}
</>
) : null}
</>
)
}

View file

@ -0,0 +1,81 @@
import { useState } from 'react'
import { useTranslation } from 'next-i18next'
import useBackend from 'site/hooks/useBackend.js'
import Link from 'next/link'
import { Choice } from '../shared.js'
export const namespaces = ['newsletter']
const welcomeSteps = {
1: {
href: '/docs/guide/',
steps: 0,
},
2: {
href: '/welcome/units',
steps: 3,
},
3: {
href: '/welcome/units',
steps: 5,
},
4: {
href: '/welcome/units',
steps: 7,
},
5: {
href: '/',
steps: 0,
},
}
export const NewsletterSettings = ({ app, title = false, welcome = false }) => {
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const [selection, setSelection] = useState(app.account?.newsletter ? 'yes' : 'no')
const update = async (val) => {
if (val !== selection) {
const result = await backend.updateAccount({ newsletter: val === 'yes' ? true : false })
if (result) setSelection(val)
}
}
return (
<>
{title ? <h1 className="text-4xl">{t('title')}</h1> : null}
{['yes', 'no'].map((val) => (
<Choice val={val} t={t} update={update} current={selection} bool>
<span className="block text-lg leading-5">
{selection === 1 && val === 2 ? t('showMore') : t(`${val}`)}
</span>
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
</Choice>
))}
{welcome ? (
<>
<Link
href={welcomeSteps[app.account.control].href}
className="btn btn-primary w-full mt-12"
>
{t('continue')}
</Link>
{welcomeSteps[app.account.control].steps ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={200 / welcomeSteps[app.account.control].steps}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
2 / {welcomeSteps[app.account.control].steps}
</span>
</>
) : null}
</>
) : null}
</>
)
}
export default NewsletterSettings

View file

@ -0,0 +1,6 @@
title: Would you like to reveice the FreeSewing newsletter?
yes: Yes, I would like to receive the newsletter
yesd: Once every 3 months you'll receive an email from us with honest wholesome content. No tracking, no ads, no nonsense.
no: No thanks
nod: You can always change your mind later. But until you do, we will not send you any newsletters.
continue: Continue

View file

@ -0,0 +1,27 @@
const btnClasses = {
dflt:
'btn w-full mt-2 btn-secondary ' +
'flex flex-row flex-nowrap items-center gap-4 py-4 h-auto ' +
'border border-secondary justify-start text-left bg-opacity-30',
active:
'btn-ghost bg-secondary hover:bg-secondary ' + 'hover:bg-opacity-30 hover:border-secondary',
inactive:
'hover:bg-opacity-20 hover:bg-secondary btn-ghost ' +
'border border-secondary hover:border hover:border-secondary',
}
const spanClasses =
'p-4 w-8 h-8 shrink-0 rounded-full text-center p-0 py-2 bg-secondary text-secondary-content'
export const Choice = ({ val, update, t, current, children, bool = false }) => {
const active = val === current
return (
<button
className={`${btnClasses.dflt} ${active ? btnClasses.active : btnClasses.inactive}`}
onClick={() => update(val)}
>
<span className={spanClasses}>{bool ? (val === 'yes' ? 1 : 2) : val}</span>
<div className={`normal-case text-base-content`}>{children}</div>
</button>
)
}

View file

@ -0,0 +1,81 @@
import { useState } from 'react'
import { useTranslation } from 'next-i18next'
import useBackend from 'site/hooks/useBackend.js'
import Link from 'next/link'
import { Choice } from '../shared.js'
export const namespaces = ['units']
const welcomeSteps = {
1: {
href: '/docs/guide/',
steps: 0,
},
2: {
href: '/docs/guide/',
steps: 3,
},
3: {
href: '/welcome/compare',
steps: 5,
},
4: {
href: '/welcome/compare',
steps: 7,
},
5: {
href: '/',
steps: 0,
},
}
const UnitsSettings = ({ app, title = false, welcome = false }) => {
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const [selection, setSelection] = useState(app.account?.imperial === true ? 'imperial' : 'metric')
const update = async (val) => {
if (val !== selection) {
const result = await backend.updateAccount({ imperial: val === 'imperial' ? true : false })
if (result) setSelection(val)
}
}
return (
<>
{title ? <h1 className="text-4xl">{t('title')}</h1> : null}
{['metric', 'imperial'].map((val) => (
<Choice val={val} t={t} update={update} current={selection} bool>
<span className="block text-lg leading-5">
{selection === 1 && val === 2 ? t('showMore') : t(`${val}`)}
</span>
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
</Choice>
))}
{welcome ? (
<>
<Link
href={welcomeSteps[app.account.control].href}
className="btn btn-primary w-full mt-12"
>
{t('continue')}
</Link>
{welcomeSteps[app.account.control].steps ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={300 / welcomeSteps[app.account.control].steps}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
3 / {welcomeSteps[app.account.control].steps}
</span>
</>
) : null}
</>
) : null}
</>
)
}
export default UnitsSettings

View file

@ -0,0 +1,6 @@
metric: Metric units (cm)
metricd: Pick this if you prefer centimeters over inches.
imperial: Imperial units (inch)
imperiald: Pick this if you prefer inches over centimeters.
title: Which units do you prefer?
continue: Continue

View file

@ -0,0 +1,81 @@
import { useState } from 'react'
import { useTranslation } from 'next-i18next'
import useBackend from 'site/hooks/useBackend.js'
import Link from 'next/link'
import { Choice } from '../shared.js'
export const namespaces = ['compare']
const welcomeSteps = {
1: {
href: '/docs/guide',
steps: 0,
},
2: {
href: '/docs/guide',
steps: 3,
},
3: {
href: '/welcome/username',
steps: 5,
},
4: {
href: '/welcome/username',
steps: 7,
},
5: {
href: '/',
steps: 0,
},
}
export const CompareSettings = ({ app, title = false, welcome = false }) => {
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const [selection, setSelection] = useState(app.account?.compare ? 'yes' : 'no')
const update = async (val) => {
if (val !== selection) {
const result = await backend.updateAccount({ compare: val === 'yes' ? true : false })
if (result) setSelection(val)
}
}
return (
<>
{title ? <h1 className="text-4xl">{t('title')}</h1> : null}
{['yes', 'no'].map((val) => (
<Choice val={val} t={t} update={update} current={selection} bool>
<span className="block text-lg leading-5">
{selection === 1 && val === 2 ? t('showMore') : t(`${val}`)}
</span>
<span className="block text-xs font-light normal-case pt-1">{t(`${val}d`)}</span>
</Choice>
))}
{welcome ? (
<>
<Link
href={welcomeSteps[app.account.control].href}
className="btn btn-primary w-full mt-12"
>
{t('continue')}
</Link>
{welcomeSteps[app.account.control].steps ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={400 / welcomeSteps[app.account.control].steps}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
4 / {welcomeSteps[app.account.control].steps}
</span>
</>
) : null}
</>
) : null}
</>
)
}
export default CompareSettings

View file

@ -26,13 +26,13 @@ export const SignupLinkExpired = () => {
const { t } = useTranslation('confirm')
return (
<>
<div className="p-8 max-w-md">
<h1 className="text-center">{t('signupLinkExpired')}</h1>
<Robot pose="shrug" className="w-full" embed />
<Link className="btn btn-primary btn-lg w-full" href="/signup">
{t('signupAgain')}
</Link>
</>
</div>
)
}
@ -58,7 +58,7 @@ const Checkbox = ({ value, name, setter, label, children = null }) => (
const ConfirmSignUpPage = (props) => {
const app = useApp(props)
const backend = useApp(app)
const backend = useBackend(app)
const { t } = useTranslation(namespaces)
const router = useRouter()
@ -81,7 +81,6 @@ const ConfirmSignUpPage = (props) => {
if (consent > 0 && id) {
const data = await backend.confirmSignup({ consent, id, ...app.loadHelpers })
if (data?.token && data?.account) {
console.log(data)
app.setToken(data.token)
app.setAccount(data.account)
router.push('/welcome')

View file

@ -0,0 +1,40 @@
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useTranslation } from 'next-i18next'
import Layout from 'site/components/layouts/bare'
import Link from 'next/link'
import { useState } from 'react'
import AuthWrapper, { namespaces as authNs } from 'site/components/wrappers/auth/index.js'
import Spinner from 'shared/components/icons/spinner.js'
import CompareSettings, { namespaces as compareNs } from 'site/components/account/compare/index.js'
// Translation namespaces used on this page
const namespaces = [...compareNs, ...authNs]
const ComparePage = (props) => {
const app = useApp(props)
const { t } = useTranslation(namespaces)
const loadingClasses = app.loading ? 'opacity-50' : ''
return (
<Page app={app} title={t('title')} layout={Layout} footer={false}>
<AuthWrapper app={app}>
<div className="m-auto max-w-lg text-center lg:mt-12 p-8">
<CompareSettings app={app} title welcome />
</div>
</AuthWrapper>
</Page>
)
}
export default ComparePage
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
},
}
}

View file

@ -7,28 +7,22 @@ import Link from 'next/link'
import { useState } from 'react'
import AuthWrapper, { namespaces as authNs } from 'site/components/wrappers/auth/index.js'
import Spinner from 'shared/components/icons/spinner.js'
import ControlSettings from 'site/components/account/control/index.js'
import ControlSettings, { namespaces as controlNs } from 'site/components/account/control/index.js'
// Translation namespaces used on this page
const namespaces = ['welcome', ...authNs]
const namespaces = [...controlNs, ...authNs]
const WelcomePage = (props) => {
const app = useApp(props)
const { t } = useTranslation(namespaces)
//const [email, setEmail] = useState('')
//const [emailValid, setEmailValid] = useState(false)
//const [result, setResult] = useState(false)
//const [error, setError] = useState(null)
const loadingClasses = app.loading ? 'opacity-50' : ''
return (
<Page app={app} title={t('joinFreeSewing')} layout={Layout} footer={false}>
<Page app={app} title={t('title')} layout={Layout} footer={false}>
<AuthWrapper app={app}>
<div className="m-auto max-w-lg text-center lg:mt-12 p-8">
<h1>What do you prefer?</h1>
<ControlSettings app={app} />
<ControlSettings app={app} title welcome />
</div>
</AuthWrapper>
</Page>

View file

@ -0,0 +1,42 @@
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useTranslation } from 'next-i18next'
import Layout from 'site/components/layouts/bare'
import Link from 'next/link'
import { useState } from 'react'
import AuthWrapper, { namespaces as authNs } from 'site/components/wrappers/auth/index.js'
import Spinner from 'shared/components/icons/spinner.js'
import NewsletterSettings, {
namespaces as newsletterNs,
} from 'site/components/account/newsletter/index.js'
// Translation namespaces used on this page
const namespaces = [...newsletterNs, ...authNs]
const WelcomePage = (props) => {
const app = useApp(props)
const { t } = useTranslation(namespaces)
const loadingClasses = app.loading ? 'opacity-50' : ''
return (
<Page app={app} title={t('title')} layout={Layout} footer={false}>
<AuthWrapper app={app}>
<div className="m-auto max-w-lg text-center lg:mt-12 p-8">
<NewsletterSettings app={app} title welcome />
</div>
</AuthWrapper>
</Page>
)
}
export default WelcomePage
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
},
}
}

View file

@ -0,0 +1,40 @@
import Page from 'site/components/wrappers/page.js'
import useApp from 'site/hooks/useApp.js'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { useTranslation } from 'next-i18next'
import Layout from 'site/components/layouts/bare'
import Link from 'next/link'
import { useState } from 'react'
import AuthWrapper, { namespaces as authNs } from 'site/components/wrappers/auth/index.js'
import Spinner from 'shared/components/icons/spinner.js'
import UnitsSettings, { namespaces as unitsNs } from 'site/components/account/units/index.js'
// Translation namespaces used on this page
const namespaces = [...unitsNs, ...authNs]
const UnitsPage = (props) => {
const app = useApp(props)
const { t } = useTranslation(namespaces)
const loadingClasses = app.loading ? 'opacity-50' : ''
return (
<Page app={app} title={t('title')} layout={Layout} footer={false}>
<AuthWrapper app={app}>
<div className="m-auto max-w-lg text-center lg:mt-12 p-8">
<UnitsSettings app={app} title welcome />
</div>
</AuthWrapper>
</Page>
)
}
export default UnitsPage
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale)),
},
}
}