1
0
Fork 0
freesewing/sites/shared/components/account/sets.mjs
Joost De Cock e47c18177b wip(shared): Removed useApp hook in favor of contexts
This removes the useApp hook from all org pages in favor of various
context. This means there is no longer global state that gets passed
around, instead each component that requires access to something shared
(like account, or navigation) can just use the context instead.

This is a first step, as a lot of shared components stil rely on app not
to mention the dev and lab sites.
2023-04-28 21:23:06 +02:00

274 lines
8 KiB
JavaScript

// Dependencies
import { useState, useEffect, useContext } from 'react'
import { useTranslation } from 'next-i18next'
import { DateTime } from 'luxon'
import { CopyToClipboard } from 'react-copy-to-clipboard'
// Hooks
import { useAccount } from 'shared/hooks/use-account.mjs'
import { useBackend } from 'shared/hooks/use-backend.mjs'
import { useToast } from 'shared/hooks/use-toast.mjs'
import { useRouter } from 'next/router'
// Context
import { LoadingContext } from 'shared/context/loading-context.mjs'
import { ModalContext } from 'shared/context/modal-context.mjs'
// Components
import { BackToAccountButton, Choice } from './shared.mjs'
import { Popout } from 'shared/components/popout.mjs'
import { WebLink } from 'shared/components/web-link.mjs'
import { CopyIcon } from 'shared/components/icons.mjs'
import { Collapse } from 'shared/components/collapse.mjs'
import { TrashIcon } from 'shared/components/icons.mjs'
import { LeftIcon } from 'shared/components/icons.mjs'
import { ModalWrapper } from 'shared/components/wrappers/modal.mjs'
import Markdown from 'react-markdown'
import { Tab } from './bio.mjs'
export const ns = ['account', 'toast']
const NewSet = ({ t, account, setGenerate, oneAdded, backend, toast, standAlone = false }) => {
// Context
const { loading, startLoading, stopLoading } = useContext(LoadingContext)
// Hooks
const router = useRouter()
// State
const [name, setName] = useState('')
const [notes, setNotes] = useState('')
const [set, setSet] = useState(false)
const [activeTab, setActiveTab] = useState('edit')
// Helper method to create a new set
const createSet = async () => {
startLoading()
const result = await backend.createSet({
name,
})
if (result.success) {
toast.success(<span>{t('nailedIt')}</span>)
setSet(result.data.set)
oneAdded()
} else toast.for.backendError()
stopLoading()
}
// Helper method to clear inputs
const clear = () => {
setSet(false)
setGenerate(false)
}
// Shared props for tabs
const tabProps = { activeTab, setActiveTab, t }
return (
<div>
<h2>{t('newSet')}</h2>
<h3>{t('name')}</h3>
<p>{t('setNameDesc')}</p>
<input
value={name}
onChange={(evt) => setName(evt.target.value)}
className="input w-full input-bordered flex flex-row"
type="text"
placeholder={'Georg Cantor'}
/>
{account.control > 2 ? (
<>
<h3>{t('notes')}</h3>
<p>{t('setNotesDesc')}</p>
<div className="tabs w-full">
<Tab id="edit" {...tabProps} />
<Tab id="preview" {...tabProps} />
</div>
<div className="flex flex-row items-center mt-4">
{activeTab === 'edit' ? (
<textarea
rows="5"
className="textarea textarea-bordered textarea-lg w-full"
placeholder={t('placeholder')}
onChange={(evt) => setNotes(evt.target.value)}
value={notes}
/>
) : (
<div className="text-left px-4 border w-full">
<Markdown>{notes}</Markdown>
</div>
)}
</div>
</>
) : null}
<div className="flex flex-row gap-2 items-center w-full my-8">
<button
className="btn btn-primary grow capitalize"
disabled={name.length < 1}
onClick={createSet}
>
{t('newSet')}
</button>
<button
className="btn btn-primary btn-outline capitalize"
onClick={() => (standAlone ? router.push('/account/sets') : setGenerate(false))}
>
{t('cancel')}
</button>
</div>
</div>
)
}
const MeasurementsSet = ({ apikey, t, account, backend, oneAdded }) => {
// Context
const { loading, startLoading, stopLoading } = useContext(LoadingContext)
const { setModal } = useContext(ModalContext)
// Hooks
const toast = useToast()
const fields = {
id: 'ID',
name: t('keyName'),
level: t('keyLevel'),
expiresAt: t('expires'),
createdAt: t('created'),
}
const expired = DateTime.fromISO(apikey.expiresAt).valueOf() < DateTime.now().valueOf()
const remove = async () => {
startLoading()
const result = await backend.removeApikey(apikey.id)
if (result) toast.success(t('gone'))
else toast.for.backendError()
// This just forces a refresh of the list from the server
// We obviously did not add a key here, but rather removed one
oneAdded()
stopLoading()
}
const removeModal = () => {
setModal(
<ModalWrapper slideFrom="top">
<h2>{t('areYouCertain')}</h2>
<p>{t('deleteKeyWarning')}</p>
<p className="flex flex-row gap-4 items-center justify-center">
<button className="btn btn-neutral btn-outline px-8">{t('cancel')}</button>
<button className="btn btn-error px-8" onClick={remove}>
{t('delete')}
</button>
</p>
</ModalWrapper>
)
}
const title = (
<div className="flex flex-row gap-2 items-center inline-block justify-around w-full">
<span>{apikey.name}</span>
<span className="font-normal">
{t('expires')}: <b>{DateTime.fromISO(apikey.expiresAt).toLocaleString()}</b>
</span>
<span className="opacity-50">|</span>
<span className="font-normal">
{t('keyLevel')}: <b>{apikey.level}</b>
</span>
</div>
)
return (
<Collapse
title={[title, null]}
valid={!expired}
buttons={[
<button
key="button1"
className="z-10 btn btn-sm mr-4 bg-base-100 text-error hover:bg-error hover:text-error-content border-0"
onClick={account.control > 4 ? remove : removeModal}
>
<TrashIcon key="button2" />
</button>,
]}
>
{expired ? (
<Popout warning compact>
<b>{t('keyExpired')}</b>
</Popout>
) : null}
{Object.entries(fields).map(([key, title]) => (
<Row title={title} key={key}>
{apikey[key]}
</Row>
))}
</Collapse>
)
}
// Component for the 'new/apikey' page
//export const NewApikey = ({ app, standAlone = false }) => {
// const { account, token } = useAccount()
// const backend = useBackend(token)
// const { t } = useTranslation(ns)
// const toast = useToast()
//
// const [keys, setKeys] = useState([])
// const [generate, setGenerate] = useState(false)
// const [added, setAdded] = useState(0)
//
// const oneAdded = () => setAdded(added + 1)
//
// return (
// <div className="max-w-xl xl:pl-4">
// <NewKey {...{ app, t, account, setGenerate, backend, toast, oneAdded, standAlone }} />
// </div>
// )
//}
// Component for the account/sets page
export const Sets = () => {
// Context
const { loading, startLoading, stopLoading } = useContext(LoadingContext)
// Hooks
const { account, token } = useAccount()
const backend = useBackend(token)
const { t } = useTranslation(ns)
const toast = useToast()
// State
const [sets, setSets] = useState([])
const [generate, setGenerate] = useState(false)
const [added, setAdded] = useState(0)
// Effects
useEffect(() => {
const getSets = async () => {
const result = await backend.getSets()
if (result.success) setSets(result.data.sets)
}
getSets()
}, [added])
// Helper method to force a refresh
const oneAdded = () => setAdded(added + 1)
return (
<div className="max-w-xl xl:pl-4">
{generate ? (
<NewSet {...{ t, account, setGenerate, backend, toast, oneAdded }} />
) : (
<>
<h2>{t('sets')}</h2>
{sets.map((set) => (
<Set {...{ account, apikey, t, backend, oneAdded }} key={apikey.id} />
))}
<button
className="btn btn-primary w-full capitalize mt-4"
onClick={() => setGenerate(true)}
>
{t('newSet')}
</button>
<BackToAccountButton loading={loading} />
</>
)}
</div>
)
}