// Dependencies import { useState, useEffect, useContext } from 'react' import { useTranslation } from 'next-i18next' import { DateTime } from 'luxon' import { CopyToClipboard } from 'react-copy-to-clipboard' import { shortDate, formatNumber } from 'shared/utils.mjs' // Context import { ModalContext } from 'shared/context/modal-context.mjs' // Hooks import { useAccount } from 'shared/hooks/use-account.mjs' import { useBackend } from 'shared/hooks/use-backend.mjs' import { useRouter } from 'next/router' import { useLoadingStatus } from 'shared/hooks/use-loading-status.mjs' // Components import { BackToAccountButton, Choice } from './shared.mjs' import { Popout } from 'shared/components/popout/index.mjs' import { LeftIcon, PlusIcon, CopyIcon, RightIcon, TrashIcon } from 'shared/components/icons.mjs' import { Collapse, useCollapseButton } from 'shared/components/collapse.mjs' import { ModalWrapper } from 'shared/components/wrappers/modal.mjs' import { PageLink, Link, WebLink } from 'shared/components/link.mjs' export const ns = ['account', 'status'] const ExpiryPicker = ({ t, expires, setExpires }) => { const router = useRouter() const { locale } = router const [months, setMonths] = useState(1) // Run update when component mounts useEffect(() => update(months), []) const update = (evt) => { const value = typeof evt === 'number' ? evt : evt.target.value setExpires(DateTime.now().plus({ months: value })) setMonths(value) } return ( <>
{t('keyExpiresDesc')} {shortDate(locale, expires)} ) } const CopyInput = ({ text }) => { const { t } = useTranslation(['status']) const { setLoadingStatus, LoadingStatus } = useLoadingStatus() const [copied, setCopied] = useState(false) const showCopied = () => { setCopied(true) setLoadingStatus([true, t('copiedToClipboard'), true, true]) window.setTimeout(() => setCopied(false), 2000) } return (
) } export const Row = ({ title, children }) => (
{title}
{children}
) export const Apikey = ({ apikey }) => { const { t } = useTranslation(ns) const router = useRouter() const { locale } = router return apikey ? (
{apikey.name} {shortDate(locale, apikey.createdAt)} {shortDate(locale, apikey.expiresAt)} {apikey.key}
) : null } const ShowKey = ({ apikey, t, clear }) => { const router = useRouter() const { locale } = router return (
{t('keySecretWarning')} {apikey.name} {shortDate(locale, apikey.createdAt)} {shortDate(locale, apikey.expiresAt)}
) } const NewKey = ({ t, account, setGenerate, backend, title = true }) => { const [name, setName] = useState('') const [level, setLevel] = useState(1) const [expires, setExpires] = useState(Date.now()) const [apikey, setApikey] = useState(false) const { setLoadingStatus, LoadingStatus } = useLoadingStatus() const levels = account.role === 'admin' ? [0, 1, 2, 3, 4, 5, 6, 7, 8] : [0, 1, 2, 3, 4] const createKey = async () => { setLoadingStatus([true, 'processingUpdate']) const result = await backend.createApikey({ name, level, expiresIn: Math.floor((expires.valueOf() - Date.now().valueOf()) / 1000), }) if (result.success) { setLoadingStatus([true, 'nailedIt', true, true]) setApikey(result.data.apikey) } else setLoadingStatus([true, 'backendError', true, false]) } const clear = () => { setApikey(false) setGenerate(false) setName('') setLevel(1) } return (
{apikey ? ( ) : ( <>
{t('keyName')}

{t('keyNameDesc')}

setName(evt.target.value)} className="input w-full input-bordered flex flex-row" type="text" placeholder={'Alicia key'} />
{t('keyExpires')}
{t('keyLevel')}
{levels.map((l) => ( {t(`keyLevel${l}`)} ))}
)}
) } // Component for the 'new/apikey' page export const NewApikey = () => { // Hooks const { setLoadingStatus, LoadingStatus } = useLoadingStatus() const { account } = useAccount() const backend = useBackend() const { t } = useTranslation(ns) // State const [generate, setGenerate] = useState(false) const [added, setAdded] = useState(0) // Helper method to force refresh const keyAdded = () => setAdded(added + 1) return (
) } // Component for the account/apikeys page export const Apikeys = () => { const router = useRouter() const { locale } = router // Hooks const { account } = useAccount() const backend = useBackend() const { t } = useTranslation(ns) const { setLoadingStatus, LoadingStatus, LoadingProgress } = useLoadingStatus() // State const [keys, setKeys] = useState([]) const [selected, setSelected] = useState({}) const [refresh, setRefresh] = useState(0) // Helper var to see how many are selected const selCount = Object.keys(selected).length // Effects useEffect(() => { const getApikeys = async () => { const result = await backend.getApikeys() if (result.success) setKeys(result.data.apikeys) } getApikeys() }, [refresh]) // Helper method to toggle single selection const toggleSelect = (id) => { const newSelected = { ...selected } if (newSelected[id]) delete newSelected[id] else newSelected[id] = 1 setSelected(newSelected) } // Helper method to toggle select all const toggleSelectAll = () => { if (selCount === keys.length) setSelected({}) else { const newSelected = {} for (const key of keys) newSelected[key.id] = 1 setSelected(newSelected) } } // Helper to delete one or more apikeys const removeSelectedApikeys = async () => { let i = 0 for (const key in selected) { i++ await backend.removeApikey(key) setLoadingStatus([ true, , ]) } setSelected({}) setRefresh(refresh + 1) setLoadingStatus([true, 'nailedIt', true, true]) } return (

{t('newApikey')}

{selCount ? ( ) : null} {keys.map((apikey, i) => ( ))}
{t('keyName')} {t('keyLevel')} 🔐 {t('keyExpires')} {t('apiCalls')}
toggleSelect(apikey.id)} /> {apikey.level} ({t(`keyLevel${apikey.level}`)}) {shortDate(locale, apikey.expiresAt, false)} {formatNumber(apikey.calls)}
{account.control < 5 ? (
{t('keyDocsTitle')}

{t('keyDocsMsg')}

FreeSewing.dev

) : null}
) }