1
0
Fork 0

wip: More account pages

This commit is contained in:
joostdecock 2024-12-23 18:25:48 +01:00
parent 543be68c1f
commit c994e3898f
58 changed files with 1419 additions and 29 deletions

View file

@ -0,0 +1,103 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
import { cloudflareImageUrl } from '@freesewing/utils'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { SaveIcon } from '@freesewing/react/components/Icon'
import { PassiveImageInput } from '@freesewing/react/components/Input'
/*
* Component for the account/bio page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
* @params {function} props.Link - A framework specific Link component for client-side routing
*/
export const Avatar = ({ welcome = false, Link = false }) => {
if (!Link) Link = WebLink
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
// State
const [img, setImg] = useState('')
// Context
const { setLoadingStatus } = useContext(LoadingStatusContext)
// Save handler
const save = async () => {
setLoadingStatus([true, 'Uploading image'])
const [status, body] = await backend.updateAccount({ img })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, 'Avatar saved', true, true])
} else setLoadingStatus([true, 'Failed to save avatar image. Please report this', true, false])
}
// Next page in welcome flow
const nextHref = '/docs/about/guide'
return (
<div className="w-full">
{!welcome || img !== false ? (
<img
alt="img"
src={img || cloudflareImageUrl({ id: `uid-${account.ihash}`, variant: 'public' })}
className="shadow mb-4"
/>
) : null}
<PassiveImageInput
id="account-img"
label="Avatar image"
placeholder={'image'}
update={setImg}
current={img}
valid={(val) => val.length > 0}
/>
{welcome ? (
<>
<button className={`btn btn-secondary mt-4 px-8`} onClick={save} disabled={!img}>
{t('save')}
</button>
<ContinueButton btnProps={{ href: nextHref }} link />
{welcomeSteps[account.control].length > 0 ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={700 / welcomeSteps[account.control].length}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
7 / {welcomeSteps[account.control].length}
</span>
<Icons
done={welcomeSteps[account.control].slice(0, 6)}
todo={welcomeSteps[account.control].slice(7)}
current="img"
/>
</>
) : null}
</>
) : (
<>
<p className="text-right">
<button className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8" onClick={save}>
<SaveIcon /> Save Avatar
</button>
</p>
</>
)}
</div>
)
}

View file

@ -0,0 +1,85 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { SaveIcon } from '@freesewing/react/components/Icon'
import { MarkdownInput } from '@freesewing/react/components/Input'
/*
* Component for the account/bio page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
* @params {function} props.Link - A framework specific Link component for client-side routing
*/
export const Bio = ({ welcome = false, Link = false }) => {
if (!Link) Link = WebLink
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
// State
const [bio, setBio] = useState(account.bio)
// Helper method to save bio
const save = async () => {
setLoadingStatus([true, 'Saving bio'])
const [status, body] = await backend.updateAccount({ bio })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, 'Bio updated', true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
// Next step in the onboarding
const nextHref =
welcomeSteps[account.control].length > 5
? '/welcome/' + welcomeSteps[account.control][6]
: '/docs/about/guide'
return (
<div className="w-full">
<h6>Tell people a little bit about yourself.</h6>
<MarkdownInput id="account-bio" label="Bio" update={setBio} current={bio} placeholder="Bio" />
<p className="text-right">
<button className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8" onClick={save}>
<SaveIcon /> Save Bio
</button>
</p>
{welcome ? (
<>
<ContinueButton btnProps={{ href: nextHref }} link />
{welcomeSteps[account.control].length > 0 ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={600 / welcomeSteps[account.control].length}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
6 / {welcomeSteps[account.control].length}
</span>
<Icons
done={welcomeSteps[account.control].slice(0, 5)}
todo={welcomeSteps[account.control].slice(6)}
current="bio"
/>
</>
) : null}
</>
) : null}
</div>
)
}

View file

@ -88,7 +88,7 @@ export const Bookmarks = () => {
for (const type in types) perType[type] = bookmarks.filter((b) => b.type === type)
return (
<div className="max-w-4xl xl:pl-4">
<div className="w-full">
<p className="text-center md:text-right">
<button
className="daisy-btn daisy-btn-primary capitalize w-full md:w-auto hover:text-primary-content hover:no-underline"
@ -100,7 +100,7 @@ export const Bookmarks = () => {
slideFrom="right"
keepOpenOnClick
>
<div className="w-full max-w-xl">
<div className="w-full">
<h2>New Bookmark</h2>
<NewBookmark onCreated={() => setRefresh(refresh + 1)} />
</div>

View file

@ -0,0 +1,118 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
import { ListInput } from '@freesewing/react/components/Input'
const strings = {
yes: {
title: 'Yes, in case it may help me',
desc:
'Allowing us to compare your measurments to a baseline or others measurements sets ' +
'allows us to detect potential problems in your measurements or patterns.',
},
no: {
title: 'No, never compare',
desc:
'We get it, comparison is the thief of joy. Just be aware that this limits our ability ' +
'to warn you about potential problems in your measurements sets or patterns.',
},
}
/*
* Component for the account/preferences/compare page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
*/
export const Compare = ({ welcome = false }) => {
if (!Link) Link = WebLink
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
// State
const [selection, setSelection] = useState(account?.compare ? 'yes' : 'no')
// Context
const { setLoadingStatus } = useContext(LoadingStatusContext)
// Helper method to update the account
const update = async (val) => {
if (val !== selection) {
setLoadingStatus([true, 'Saving preferences'])
const [status, body] = await backend.updateAccount({
compare: val === 'yes' ? true : false,
})
if (status === 200) {
setLoadingStatus([true, 'Preferences saved', true, true])
setAccount(body.account)
setSelection(val)
} else setLoadingStatus([true, 'An error occured. Please report this.', true, true])
}
}
// Link to the next onboarding step
const nextHref =
welcomeSteps[account?.control].length > 3
? '/welcome/' + welcomeSteps[account?.control][4]
: '/docs/about/guide'
return (
<div className="max-w-xl">
<ListInput
id="account-compare"
label="Are you comfortable with your measurements sets being compared?"
list={['yes', 'no'].map((val) => ({
val,
label: (
<div className="flex flex-row items-center w-full justify-between">
<span>{strings[val].title}</span>
{val === 'yes' ? (
<OkIcon className="w-8 h-8 text-success" stroke={4} />
) : (
<NoIcon className="w-8 h-8 text-error" stroke={3} />
)}
</div>
),
desc: strings[val].desc,
}))}
current={selection}
update={update}
/>
{welcome ? (
<>
<ContinueButton btnProps={{ href: nextHref }} link />
{welcomeSteps[account?.control].length > 0 ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={400 / welcomeSteps[account?.control].length}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
4 / {welcomeSteps[account?.control].length}
</span>
<Icons
done={welcomeSteps[account?.control].slice(0, 3)}
todo={welcomeSteps[account?.control].slice(4)}
current="compare"
/>
</>
) : null}
</>
) : null}
</div>
)
}

View file

@ -0,0 +1,90 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
import { controlDesc } from '@freesewing/config'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
import { useControl } from '@freesewing/react/hooks/useControl'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
import { ListInput } from '@freesewing/react/components/Input'
import { ControlScore } from '@freesewing/react/components/Control'
const strings = {
1: {
title: 'Keep it as simple as possible',
desc:
'Allowing us to compare your measurments to a baseline or others measurements sets ' +
'allows us to detect potential problems in your measurements or patterns.',
},
2: {
title: 'No, never compare',
desc:
'We get it, comparison is the thief of joy. Just be aware that this limits our ability ' +
'to warn you about potential problems in your measurements sets or patterns.',
},
}
/*
* Component for the account/preferences/control page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
*/
export const Control = ({ welcome = false }) => {
// Hooks
const { control, updateControl } = useControl()
// Helper to get the link to the next onboarding step
const nextHref = welcome
? welcomeSteps[control].length > 1
? '/welcome/' + welcomeSteps[control][1]
: '/docs/about/guide'
: false
return (
<div className="w-full">
<ListInput
id="account-control"
label="User Experience"
list={[1, 2, 3, 4, 5].map((val) => ({
val,
label: (
<div className="flex flex-row items-center w-full justify-between">
<span>{controlDesc[val].title}</span>
<ControlScore control={val} />
</div>
),
desc: controlDesc[val].desc,
}))}
current={control}
update={updateControl}
/>
{welcome ? (
<>
<ContinueButton btnProps={{ href: nextHref }} link />
{welcomeSteps[control].length > 1 ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={100 / welcomeSteps[control].length}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
1 / {welcomeSteps[control].length}
</span>
<Icons done={[]} todo={welcomeSteps[control].slice(1)} current="" />
</>
) : null}
</>
) : null}
</div>
)
}

View file

@ -0,0 +1,87 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
import { validateEmail, validateTld } from '@freesewing/utils'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { SaveIcon } from '@freesewing/react/components/Icon'
import { EmailInput } from '@freesewing/react/components/Input'
import { Popout } from '@freesewing/react/components/Popout'
/*
* Component for the account/bio page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
* @params {function} props.Link - A framework specific Link component for client-side routing
*/
export const Email = ({ welcome = false, Link = false }) => {
if (!Link) Link = WebLink
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
// State
const [email, setEmail] = useState(account.email)
const [changed, setChanged] = useState(false)
// Helper method to update account
const save = async () => {
setLoadingStatus([true, 'Updating email address'])
const [status, body] = await backend.updateAccount({ email })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setChanged(true)
setLoadingStatus([true, 'Email change initiated', true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
// Is email valid?
const valid = (validateEmail(email) && validateTld(email)) || false
return (
<div className="w-full">
{changed ? (
<Popout note>
<h3>Please confirm this change</h3>
<p>
We have sent an E-mail to your new address to confirm this change. Please click the link
in that message to finalize this change.
</p>
</Popout>
) : (
<>
<EmailInput
id="account-email"
label="Email Address"
placeholder="example@freesewing.org"
update={setEmail}
labelBL="You will need to confirm that you can receive email at this address"
current={email}
original={account.email}
valid={() => valid}
/>
<p className="text-right">
<button
className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8"
onClick={save}
disabled={!valid || email.toLowerCase() === account.email}
>
<SaveIcon /> Update Email Address
</button>
</p>
</>
)}
</div>
)
}

View file

@ -0,0 +1,61 @@
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { SaveIcon } from '@freesewing/react/components/Icon'
import { StringInput } from '@freesewing/react/components/Input'
/*
* Component for the account/social/github page
*/
export const Github = () => {
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
// State
const [githubUsername, setGithubUsername] = useState(account.data.githubUsername || '')
const [githubEmail, setGithubEmail] = useState(account.data.githubEmail || '')
// Helper method to save changes
const save = async () => {
setLoadingStatus([true, 'Saving bio'])
const [status, body] = await backend.updateAccount({ data: { githubUsername, githubEmail } })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, 'GitHub info updated', true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
return (
<div className="w-full">
<StringInput
id="account-github-email"
label="GitHub Email Address"
current={githubEmail}
update={setGithubEmail}
valid={(val) => val.length > 0}
placeholder={'joost@joost.at'}
/>
<StringInput
id="account-github-username"
label="GitHub Username"
current={githubUsername}
update={setGithubUsername}
valid={(val) => val.length > 0}
placeholder={'joostdecock'}
/>
<p className="text-right">
<button className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8" onClick={save}>
<SaveIcon /> Save
</button>
</p>
</div>
)
}

View file

@ -0,0 +1,61 @@
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { SaveIcon } from '@freesewing/react/components/Icon'
import { StringInput } from '@freesewing/react/components/Input'
/*
* Component for the account/social/github page
*/
export const Instagram = () => {
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
// State
const [githubUsername, setGithubUsername] = useState(account.data.githubUsername || '')
const [githubEmail, setGithubEmail] = useState(account.data.githubEmail || '')
// Helper method to save changes
const save = async () => {
setLoadingStatus([true, 'Saving bio'])
const [status, body] = await backend.updateAccount({ data: { githubUsername, githubEmail } })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, 'GitHub info updated', true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
return (
<div className="w-full">
<StringInput
id="account-github-email"
label="GitHub Email Address"
current={githubEmail}
update={setGithubEmail}
valid={(val) => val.length > 0}
placeholder={'joost@joost.at'}
/>
<StringInput
id="account-github-username"
label="GitHub Username"
current={githubUsername}
update={setGithubUsername}
valid={(val) => val.length > 0}
placeholder={'joostdecock'}
/>
<p className="text-right">
<button className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8" onClick={save}>
<SaveIcon /> Save
</button>
</p>
</div>
)
}

View file

@ -0,0 +1,75 @@
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { SaveIcon } from '@freesewing/react/components/Icon'
import { StringInput } from '@freesewing/react/components/Input'
const labels = {
instagram: 'Instagram',
mastodon: 'Mastodon',
reddit: 'Reddit',
twitch: 'Twitch',
tiktok: 'TikTok',
website: 'Website',
}
export const Instagram = () => <Platform platform="instagram" />
export const Mastodon = () => <Platform platform="mastodon" />
export const Reddit = () => <Platform platform="reddit" />
export const Twitch = () => <Platform platform="twitch" />
export const Tiktok = () => <Platform platform="tiktok" />
export const Website = () => <Platform platform="website" />
/*
* Component for the account/social/[platform] page
*
* @param {object} props - All React props
* @param {string} platform - One of the keys in the labels object above
*/
const Platform = ({ platform = false }) => {
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
// State
const [platformId, setPlatformId] = useState(account.data[platform] || '')
if (!labels || !labels[platform]) return <p>Not a supported platform</p>
// Helper method to save changes
const save = async () => {
setLoadingStatus([true, 'Saving linked identity'])
const data = { data: {} }
data.data[platform] = platformId
const [status, body] = await backend.updateAccount(data)
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, `Saved your ${labels[platform]} info`, true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
return (
<div className="w-full">
<StringInput
id={`account-${platform}`}
label={platform === 'website' ? `Website URL` : `${labels[platform]} account`}
current={platformId}
update={setPlatformId}
valid={(val) => val.length > 0}
placeholder={'joostdecock'}
/>
<p className="text-right">
<button className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8" onClick={save}>
<SaveIcon /> Save
</button>
</p>
</div>
)
}

View file

@ -200,7 +200,7 @@ export const Set = ({ id, publicOnly = false, Link = false }) => {
const [status, body] = await backend.createSet(data)
if (status === 201 && body.result === 'created') {
setLoadingStatus([true, 'Loading newly created set', true, true])
window.location = `/account/set/?id=${body.set.id}`
window.location = `/account/data/sets/set?id=${body.set.id}`
} else setLoadingStatus([true, 'We failed to create this measurements set', true, false])
}

View file

@ -3,6 +3,7 @@ import { measurements } from '@freesewing/config'
import { cloudflareImageUrl, capitalize } from '@freesewing/utils'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
import { ModalContext } from '@freesewing/react/context/Modal'
// Hooks
import React, { useState, useEffect, Fragment, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
@ -10,6 +11,8 @@ import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { NoIcon, OkIcon, PlusIcon, TrashIcon, UploadIcon } from '@freesewing/react/components/Icon'
import { ModalWrapper } from '@freesewing/react/components/Modal'
import { NewSet } from './Set.mjs'
/*
* The component for the an account/sets page
@ -23,13 +26,16 @@ export const Sets = ({ Link = false }) => {
// Hooks
const { control } = useAccount()
const backend = useBackend()
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
// State
const [sets, setSets] = useState([])
const [selected, setSelected] = useState({})
const [refresh, setRefresh] = useState(0)
// Context
const { setLoadingStatus, LoadingProgress } = useContext(LoadingStatusContext)
const { setModal } = useContext(ModalContext)
// Effects
useEffect(() => {
const getSets = async () => {
@ -90,15 +96,19 @@ export const Sets = ({ Link = false }) => {
<UploadIcon />
Import Measurements Sets
</Link>
<Link
<button
className="daisy-btn daisy-btn-primary capitalize w-full md:w-auto hover:no-underline hover:text-primary-content"
bottom
primary
href="/new/set"
onClick={() =>
setModal(
<ModalWrapper keepOpenOnClick>
<NewSet />
</ModalWrapper>
)
}
>
<PlusIcon />
Create a new Measurements Set
</Link>
</button>
</p>
<div className="flex flex-row gap-2 border-b-2 mb-4 pb-4 mt-8 h-14 items-center">
<input
@ -145,7 +155,12 @@ export const Sets = ({ Link = false }) => {
/>
</label>
<div className="w-full">
<MsetCard control={control} href={`/account/set?id=${set.id}`} set={set} size="md" />
<MsetCard
control={control}
href={`/account/data/sets/set?id=${set.id}`}
set={set}
size="md"
/>
</div>
</div>
))}

View file

@ -0,0 +1,117 @@
// Dependencies
import { welcomeSteps } from './shared.mjs'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link as WebLink } from '@freesewing/react/components/Link'
import { NoIcon, OkIcon, SaveIcon } from '@freesewing/react/components/Icon'
import { StringInput } from '@freesewing/react/components/Input'
/*
* Component for the account/username page
*
* @params {object} props - All React props
* @params {bool} props.welcome - Set to true to use this component on the welcome page
* @params {function} props.Link - A framework specific Link component for client-side routing
*/
export const Username = ({ welcome = false, Link = false }) => {
if (!Link) Link = WebLink
// Hooks
const { account, setAccount } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
const [username, setUsername] = useState(account.username)
const [available, setAvailable] = useState(true)
const update = async (value) => {
if (value !== username) {
setUsername(value)
const result = await backend.isUsernameAvailable(value)
setAvailable(result.available ? true : false)
}
}
const save = async () => {
setLoadingStatus([true, 'Saving username'])
const [status, body] = await backend.updateAccount({ username })
if (status === 200 && body.result === 'success') {
setAccount(body.account)
setLoadingStatus([true, 'Username updated', true, true])
} else setLoadingStatus([true, 'Something went wrong. Please report this', true, true])
}
const nextHref =
welcomeSteps[account.control].length > 5
? '/welcome/' + welcomeSteps[account.control][5]
: '/docs/about/guide'
let btnClasses = 'daisy-btn mt-4 capitalize '
if (welcome) btnClasses += 'w-64 daisy-btn-secondary'
else btnClasses += 'w-full daisy-btn-primary'
return (
<div className="w-full">
<StringInput
id="account-username"
label="Username"
current={username}
update={update}
valid={() => available}
placeholder={'Sorcha Ni Dhubghaill'}
labelBL={
<span className="flex flex-row gap-1 items-center">
{available ? (
<>
<OkIcon className="w-4 h-4 text-success" stroke={4} /> Username is available
</>
) : (
<>
<NoIcon className="w-4 h-4 text-error" stroke={3} /> This username is taken
</>
)}
</span>
}
/>
<p className="text-right">
<button
disabled={!available}
className="daisy-btn daisy-btn-primary w-full lg:w-auto mt-8"
onClick={save}
>
<SaveIcon /> Save Username
</button>
</p>
{welcome ? (
<>
<ContinueButton btnProps={{ href: nextHref }} link />
{welcomeSteps[account.control].length > 0 ? (
<>
<progress
className="progress progress-primary w-full mt-12"
value={500 / welcomeSteps[account.control].length}
max="100"
></progress>
<span className="pt-4 text-sm font-bold opacity-50">
5 / {welcomeSteps[account.control].length}
</span>
<Icons
done={welcomeSteps[account.control].slice(0, 4)}
todo={welcomeSteps[account.control].slice(5)}
current="username"
/>
</>
) : null}
</>
) : null}
</div>
)
}

View file

@ -7,6 +7,14 @@ import { Sets, MsetCard } from './Sets.mjs'
import { Patterns } from './Patterns.mjs'
import { Pattern, PatternCard } from './Pattern.mjs'
import { Apikeys } from './Apikeys.mjs'
import { Username } from './Username.mjs'
import { Bio } from './Bio.mjs'
import { Avatar } from './Avatar.mjs'
import { Email } from './Email.mjs'
import { Github } from './Github.mjs'
import { Instagram, Mastodon, Reddit, Twitch, Tiktok, Website } from './Platform.mjs'
import { Compare } from './Compare.mjs'
import { Control } from './Control.mjs'
export {
Bookmarks,
@ -20,4 +28,17 @@ export {
Pattern,
PatternCard,
Apikeys,
Username,
Bio,
Avatar,
Email,
Github,
Instagram,
Mastodon,
Reddit,
Twitch,
Tiktok,
Website,
Compare,
Control,
}

View file

@ -12,6 +12,14 @@ export const DisplayRow = ({ title, children, keyWidth = 'w-24' }) => (
</div>
)
export const welcomeSteps = {
1: [''],
2: ['', 'newsletter', 'units'],
3: ['', 'newsletter', 'units', 'compare', 'username'],
4: ['', 'newsletter', 'units', 'compare', 'username', 'bio', 'img'],
5: [''],
}
/*
import { Spinner } from 'shared/components/spinner.mjs'
import Link from 'next/link'
@ -132,12 +140,5 @@ const icons = {
img: UserIcon,
}
export const welcomeSteps = {
1: [''],
2: ['', 'newsletter', 'units'],
3: ['', 'newsletter', 'units', 'compare', 'username'],
4: ['', 'newsletter', 'units', 'compare', 'username', 'bio', 'img'],
5: [''],
}
*/

View file

@ -51,7 +51,7 @@ export const UsernameSettings = ({ welcome = false }) => {
else btnClasses += 'w-full btn-primary'
return (
<div className="max-w-xl">
<div className="w-full">
<StringInput
id="account-username"
label={t('usernameTitle')}

View file

@ -1,12 +1,11 @@
import React from 'react'
import { controlDesc } from '@freesewing/config'
import { BulletIcon } from '@freesewing/react/components/Icon'
const scores = [1, 2, 3, 4, 5]
export const ControlScore = ({ control, color = 'base-content' }) =>
control ? (
<div className={`flex flex-row items-center text-${color}`}>
{scores.map((score) => (
{Object.keys(controlDesc).map((score) => (
<BulletIcon fill={control >= score ? true : false} className="w-6 h-6 -ml-1" key={score} />
))}
</div>

View file

@ -259,8 +259,12 @@ export const EmailInput = ({
placeholder={placeholder}
value={current}
onChange={(evt) => update(evt.target.value)}
className={`input w-full input-bordered ${
current === original ? 'input-secondary' : valid(current) ? 'input-success' : 'input-error'
className={`daisy-input w-full daisy-input-bordered ${
current === original
? 'daisy-input-secondary'
: valid(current)
? 'daisy-input-success'
: 'daisy-input-error'
}`}
/>
</FormControl>
@ -390,7 +394,7 @@ export const ImageInput = ({
<input
id={id}
type="url"
className="input input-secondary w-full input-bordered"
className="daisy-input daisy-input-secondary w-full daisy-input-bordered"
placeholder="Paste an image URL here"
value={current}
onChange={active ? (evt) => setUrl(evt.target.value) : (evt) => update(evt.target.value)}
@ -475,7 +479,7 @@ export const MarkdownInput = ({
</div>
</Tab>
<Tab key="preview">
<div className="flex flex-row items-center">
<div className="mdx markdown">
<Markdown>{current}</Markdown>
</div>
</Tab>