1
0
Fork 0
freesewing/sites/shared/components/account/sets.mjs

275 lines
8 KiB
JavaScript
Raw Normal View History

2023-04-22 16:41:13 +02:00
// Dependencies
import { useState, useEffect, useContext } from 'react'
2023-04-22 16:41:13 +02:00
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'
2023-04-22 16:41:13 +02:00
// 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
2023-04-22 16:41:13 +02:00
const router = useRouter()
// State
2023-04-22 16:41:13 +02:00
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
2023-04-22 16:41:13 +02:00
const createSet = async () => {
startLoading()
2023-04-22 16:41:13 +02:00
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()
2023-04-22 16:41:13 +02:00
}
// Helper method to clear inputs
2023-04-22 16:41:13 +02:00
const clear = () => {
setSet(false)
setGenerate(false)
}
// Shared props for tabs
2023-04-22 16:41:13 +02:00
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
2023-04-22 16:41:13 +02:00
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()
2023-04-22 16:41:13 +02:00
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()
2023-04-22 16:41:13 +02:00
}
const removeModal = () => {
setModal(
<ModalWrapper slideFrom="top">
2023-04-22 16:41:13 +02:00
<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
2023-04-22 16:41:13 +02:00
const { account, token } = useAccount()
const backend = useBackend(token)
const { t } = useTranslation(ns)
const toast = useToast()
// State
2023-04-22 16:41:13 +02:00
const [sets, setSets] = useState([])
const [generate, setGenerate] = useState(false)
const [added, setAdded] = useState(0)
// Effects
2023-04-22 16:41:13 +02:00
useEffect(() => {
const getSets = async () => {
const result = await backend.getSets()
if (result.success) setSets(result.data.sets)
}
getSets()
}, [added])
// Helper method to force a refresh
2023-04-22 16:41:13 +02:00
const oneAdded = () => setAdded(added + 1)
return (
<div className="max-w-xl xl:pl-4">
{generate ? (
<NewSet {...{ t, account, setGenerate, backend, toast, oneAdded }} />
2023-04-22 16:41:13 +02:00
) : (
<>
<h2>{t('sets')}</h2>
{sets.map((set) => (
<Set {...{ account, apikey, t, backend, oneAdded }} key={apikey.id} />
2023-04-22 16:41:13 +02:00
))}
<button
className="btn btn-primary w-full capitalize mt-4"
onClick={() => setGenerate(true)}
>
{t('newSet')}
</button>
<BackToAccountButton loading={loading} />
2023-04-22 16:41:13 +02:00
</>
)}
</div>
)
}