2023-04-22 16:41:13 +02:00
|
|
|
// Dependencies
|
2023-04-28 21:23:06 +02:00
|
|
|
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'
|
2023-04-29 08:29:12 +02:00
|
|
|
import orderBy from 'lodash.orderby'
|
2023-04-22 16:41:13 +02:00
|
|
|
// 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'
|
2023-04-28 21:23:06 +02:00
|
|
|
// 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']
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
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()
|
2023-04-28 21:23:06 +02:00
|
|
|
|
|
|
|
// State
|
2023-04-22 16:41:13 +02:00
|
|
|
const [name, setName] = useState('')
|
2023-04-29 08:29:12 +02:00
|
|
|
const [mset, setMset] = useState(false)
|
2023-04-22 16:41:13 +02:00
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
// Helper method to create a new set
|
2023-04-22 16:41:13 +02:00
|
|
|
const createSet = async () => {
|
2023-04-28 21:23:06 +02:00
|
|
|
startLoading()
|
2023-04-22 16:41:13 +02:00
|
|
|
const result = await backend.createSet({
|
|
|
|
name,
|
|
|
|
})
|
|
|
|
if (result.success) {
|
|
|
|
toast.success(<span>{t('nailedIt')}</span>)
|
2023-04-29 08:29:12 +02:00
|
|
|
setMset(result.data.set)
|
2023-04-22 16:41:13 +02:00
|
|
|
oneAdded()
|
|
|
|
} else toast.for.backendError()
|
2023-04-28 21:23:06 +02:00
|
|
|
stopLoading()
|
2023-04-22 16:41:13 +02:00
|
|
|
}
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
// Helper method to clear inputs
|
2023-04-22 16:41:13 +02:00
|
|
|
const clear = () => {
|
2023-04-29 08:29:12 +02:00
|
|
|
setMset(false)
|
2023-04-22 16:41:13 +02:00
|
|
|
setGenerate(false)
|
|
|
|
}
|
2023-04-28 21:23:06 +02:00
|
|
|
|
|
|
|
// 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'}
|
|
|
|
/>
|
|
|
|
<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>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2023-04-29 08:29:12 +02:00
|
|
|
const Row = ({ title, children }) => (
|
|
|
|
<div className="flex flex-row flex-wrap items-center lg:gap-4 my-2">
|
|
|
|
<div className="w-24 text-left md:text-right block md:inline font-bold pr-4">{title}</div>
|
|
|
|
<div className="grow">{children}</div>
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
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',
|
2023-04-29 08:29:12 +02:00
|
|
|
name: t('name'),
|
2023-04-22 16:41:13 +02:00
|
|
|
level: t('keyLevel'),
|
|
|
|
createdAt: t('created'),
|
|
|
|
}
|
|
|
|
|
|
|
|
const remove = async () => {
|
2023-04-28 21:23:06 +02:00
|
|
|
startLoading()
|
2023-04-29 08:29:12 +02:00
|
|
|
const result = await backend.removeSet(mset.id)
|
2023-04-22 16:41:13 +02:00
|
|
|
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()
|
2023-04-28 21:23:06 +02:00
|
|
|
stopLoading()
|
2023-04-22 16:41:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const removeModal = () => {
|
2023-04-28 21:23:06 +02:00
|
|
|
setModal(
|
|
|
|
<ModalWrapper slideFrom="top">
|
2023-04-22 16:41:13 +02:00
|
|
|
<h2>{t('areYouCertain')}</h2>
|
2023-04-29 08:29:12 +02:00
|
|
|
<p>{t('deleteSetWarning')}</p>
|
2023-04-22 16:41:13 +02:00
|
|
|
<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">
|
2023-04-29 08:29:12 +02:00
|
|
|
<span>{mset.name}</span>
|
2023-04-22 16:41:13 +02:00
|
|
|
</div>
|
|
|
|
)
|
|
|
|
|
2023-04-29 08:29:12 +02:00
|
|
|
//return <pre>{JSON.stringify(mset, null ,2)}</pre>
|
2023-04-22 16:41:13 +02:00
|
|
|
return (
|
|
|
|
<Collapse
|
|
|
|
title={[title, null]}
|
2023-04-29 08:29:12 +02:00
|
|
|
valid={true}
|
2023-04-22 16:41:13 +02:00
|
|
|
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>,
|
|
|
|
]}
|
|
|
|
>
|
2023-04-29 08:29:12 +02:00
|
|
|
<pre>{JSON.stringify(mset, null, 2)}</pre>
|
2023-04-22 16:41:13 +02:00
|
|
|
{Object.entries(fields).map(([key, title]) => (
|
|
|
|
<Row title={title} key={key}>
|
2023-04-29 08:29:12 +02:00
|
|
|
{mset[key]}
|
2023-04-22 16:41:13 +02:00
|
|
|
</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
|
2023-04-28 21:23:06 +02:00
|
|
|
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()
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
// State
|
2023-04-22 16:41:13 +02:00
|
|
|
const [sets, setSets] = useState([])
|
|
|
|
const [generate, setGenerate] = useState(false)
|
|
|
|
const [added, setAdded] = useState(0)
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
// 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])
|
|
|
|
|
2023-04-28 21:23:06 +02:00
|
|
|
// 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 ? (
|
2023-04-28 21:23:06 +02:00
|
|
|
<NewSet {...{ t, account, setGenerate, backend, toast, oneAdded }} />
|
2023-04-22 16:41:13 +02:00
|
|
|
) : (
|
|
|
|
<>
|
|
|
|
<h2>{t('sets')}</h2>
|
2023-04-29 08:29:12 +02:00
|
|
|
{orderBy(sets, ['name'], ['asc']).map((mset) => (
|
|
|
|
<MeasurementsSet {...{ app, account, mset, t, backend, oneAdded }} key={mset.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>
|
2023-04-28 21:23:06 +02:00
|
|
|
<BackToAccountButton loading={loading} />
|
2023-04-22 16:41:13 +02:00
|
|
|
</>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
}
|