import {RefreshCcw, Save} from 'lucide-react'; import {FormEvent, useEffect, useState} from 'react'; import { listProfileInviteCodes, upsertProfileInviteCode, } from '../api/adminApiClient'; import type { AdminUpsertProfileInviteCodeRequest, ProfileInviteCodeAdminResponse, } from '../api/adminApiTypes'; import {useAdminWriteConfirm} from '../components/useAdminWriteConfirm'; import {handlePageError} from './pageUtils'; interface AdminInviteCodePageProps { token: string; result: ProfileInviteCodeAdminResponse | null; onUnauthorized: (message?: string) => void; onResultChange: (result: ProfileInviteCodeAdminResponse) => void; } export function AdminInviteCodePage({ token, result, onUnauthorized, onResultChange, }: AdminInviteCodePageProps) { const [inviteCode, setInviteCode] = useState(''); const [startsAt, setStartsAt] = useState(''); const [expiresAt, setExpiresAt] = useState(''); const [grantedTagsText, setGrantedTagsText] = useState(''); const [metadataText, setMetadataText] = useState('{}'); const [errorMessage, setErrorMessage] = useState(''); const [listErrorMessage, setListErrorMessage] = useState(''); const [entries, setEntries] = useState([]); const [isSaving, setIsSaving] = useState(false); const [isLoading, setIsLoading] = useState(false); const {confirmWrite, confirmDialog} = useAdminWriteConfirm(); useEffect(() => { void refreshInviteCodes(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [token]); async function refreshInviteCodes() { setIsLoading(true); setListErrorMessage(''); try { const response = await listProfileInviteCodes(token); setEntries(response.entries); } catch (error: unknown) { handlePageError(error, onUnauthorized, setListErrorMessage); } finally { setIsLoading(false); } } async function handleSave(event: FormEvent) { event.preventDefault(); if (isSaving) { return; } setErrorMessage(''); const validityError = validateValidityWindow(startsAt, expiresAt); if (validityError) { setErrorMessage(validityError); return; } const confirmed = await confirmWrite({ action: '保存邀请码', target: inviteCode.trim(), }); if (!confirmed) { return; } setIsSaving(true); try { const metadata = withMetadataUserTags( parseMetadata(metadataText), parseUserTags(grantedTagsText), ); const payload: AdminUpsertProfileInviteCodeRequest = { inviteCode: inviteCode.trim(), metadata, startsAt: startsAt ? toIsoDateTime(startsAt) : null, expiresAt: expiresAt ? toIsoDateTime(expiresAt) : null, }; const response = await upsertProfileInviteCode(token, payload); onResultChange(response); upsertEntry(response); fillForm(response); } catch (error: unknown) { handlePageError(error, onUnauthorized, setErrorMessage); } finally { setIsSaving(false); } } function upsertEntry(next: ProfileInviteCodeAdminResponse) { setEntries((current) => { const rest = current.filter((entry) => entry.inviteCode !== next.inviteCode); return [...rest, next].sort((left, right) => { const leftUpdatedAt = Date.parse(left.updatedAt); const rightUpdatedAt = Date.parse(right.updatedAt); if (Number.isFinite(leftUpdatedAt) && Number.isFinite(rightUpdatedAt)) { const updatedCompare = rightUpdatedAt - leftUpdatedAt; if (updatedCompare !== 0) { return updatedCompare; } } return left.inviteCode.localeCompare(right.inviteCode); }); }); } function fillForm(entry: ProfileInviteCodeAdminResponse) { setInviteCode(entry.inviteCode); setStartsAt(toDateTimeLocalValue(entry.startsAt)); setExpiresAt(toDateTimeLocalValue(entry.expiresAt)); setGrantedTagsText(metadataUserTags(entry.metadata).join('、')); setMetadataText(JSON.stringify(entry.metadata, null, 2)); } const validityError = validateValidityWindow(startsAt, expiresAt); return (

邀请码

注册链路预置码

{listErrorMessage ? (
{listErrorMessage}
) : null}