// 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"
/>
Email |
Language |
Unsubscribe |
{hits.map((hit, i) => (
{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 (
)
}