// Dependencies import { userAvatarUrl } from '@freesewing/utils' // Hooks import React, { useState, useContext } from 'react' import { useAccount } from '@freesewing/react/hooks/useAccount' import { useBackend } from '@freesewing/react/hooks/useBackend' // Context import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus' import { ModalContext } from '@freesewing/react/context/Modal' // Components import { Spinner } from '@freesewing/react/components/Spinner' //import { Hits } from 'shared/components/admin.mjs' import { Link as WebLink } from '@freesewing/react/components/Link' import { SearchIcon } from '@freesewing/react/components/Icon' import { KeyVal } from '@freesewing/react/components/KeyVal' import { ModalWrapper } from '@freesewing/react/components/Modal' import { AccountStatus, UserRole } from '@freesewing/react/components/Account' /** * A component to manage FreeSewing newsletter subscribers (requires admin role) * * @component * @returns {JSX.Element} */ export const SubscriberAdministration = () => { const [subscribers, setSubscribers] = useState() const [q, setQ] = useState() const [hits, setHits] = useState([]) const backend = useBackend() const loadSubscribers = async () => { const [status, body] = await backend.adminLoadSubscribers() if (status === 200 && body.subscribers) setSubscribers(body.subscribers) } const search = async () => { if (!subscribers) await loadSubscribers() const found = [] for (const lang in subscribers) { found.push( ...subscribers[lang] .filter((sub) => sub.email.toLowerCase().includes(q.toLowerCase())) .map((sub) => ({ ...sub, lang })) ) } setHits(found) } const unsubscribe = async (ehash) => { await backend.newsletterUnsubscribe(ehash) await loadSubscribers() await search() } return ( <> {subscribers ? ( <>
Search subscribers
setQ(evt.target.value)} className="tw:daisy-input tw:w-full tw:daisy-input-bordered tw:flex tw:flex-row" type="text" placeholder="Username, ID, or E-mail address" />
{hits.map((hit, i) => ( ))}
Email Language Unsubscribe
{hit.email} {hit.lang.toUpperCase()}
) : ( )} ) } /** * A component to manage FreeSewing users (requires the admin role) * * @component * @param {object} props - All component props * @param {React.Component} props.Link - A framework specific Link component for client-side routing * @returns {JSX.Element} */ export const UserAdministration = ({ Link = false }) => { const backend = useBackend() const [q, setQ] = useState('') const [results, setResults] = useState() const [loading, setLoading] = useState(false) const search = async () => { /* * Search backend */ setLoading(true) const [status, body] = await backend.adminSearchUsers(q) if (status === 200 && body.result === 'success' && body.users) { setResults(body.users) } setLoading(false) } return ( <>
Search users
setQ(evt.target.value)} className="tw:daisy-input tw:w-full tw:daisy-input-bordered tw:flex tw:flex-row" type="text" placeholder="Username, ID, or E-mail address" />
{loading ? : }
) } const Hits = ({ results, Link = false }) => { if (!Link) Link = WebLink return ( <> {results && results.username && results.username.length > 0 && ( <>

Results based on username

{results.username.map((user) => ( ))} )} {results && results.email && results.email.length > 0 && ( <>

Results based on E-mail address

{results.email.map((user) => ( ))} )} ) } const User = ({ user, Link }) => { const { setModal } = useContext(ModalContext) const { setLoadingStatus } = useContext(LoadingStatusContext) const backend = useBackend() /* * We had a bug with the signUp flow where consent was * not set. Users cannot get out of this, so this allows * admins to grant consent on their behalf. */ const setConsent = async () => { setLoadingStatus([true, 'Contacting backend']) const [status, body] = await backend.adminUpdateUser({ id: user.id, data: { consent: 2 } }) if (status === 200 && body.result === 'success') { setLoadingStatus([true, 'Consent updated', true, true]) } else setLoadingStatus([true, 'An error occured', true, false]) } /* * Disable MFA for users who locked themselves out */ const disableMfa = async () => { setLoadingStatus([true, 'Contacting backend']) const [status, body] = await backend.adminUpdateUser({ id: user.id, data: { mfaEnabled: false }, }) if (status === 200 && body.result === 'success') { setLoadingStatus([true, 'MFA disabled', true, true]) } else setLoadingStatus([true, 'An error occured', true, false]) } return (
{user.username}
{user.mfaEnabled ? ( ) : null} {user.consent < 1 ? ( ) : null}
) } const ImpersonateButton = ({ userId }) => { const backend = useBackend() const { setLoadingStatus } = useContext(LoadingStatusContext) const { impersonate } = useAccount() if (!userId) return null const impersonateUser = async () => { setLoadingStatus([true, 'Contacting backend']) const [status, body] = await backend.adminImpersonateUser(userId) if (status === 200 && body.result === 'success') { impersonate(body) setLoadingStatus([true, 'Now impersonating', true, true]) } else setLoadingStatus([true, 'An error occured', true, false]) } return ( ) }