username change admin center
This commit is contained in:
@@ -24,7 +24,7 @@ export function AdminCenter() {
|
||||
const { t } = useT();
|
||||
const [allowed, setAllowed] = useState<boolean | null>(null);
|
||||
const [tab, setTab] = useState<Tab>("accounts");
|
||||
const [settings, setSettings] = useState<{ registrationEnabled: boolean; accessRequestEnabled: boolean; ocrEnabled: boolean; reportsEnabled: boolean } | null>(null);
|
||||
const [settings, setSettings] = useState<{ registrationEnabled: boolean; accessRequestEnabled: boolean; ocrEnabled: boolean; reportsEnabled: boolean; usernameChangeEnabled: boolean } | null>(null);
|
||||
const [error, setError] = useState("");
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [openAccessCount, setOpenAccessCount] = useState(0);
|
||||
@@ -100,7 +100,7 @@ export function AdminCenter() {
|
||||
});
|
||||
}, [allowed]);
|
||||
|
||||
async function toggle(patch: { registrationEnabled?: boolean; accessRequestEnabled?: boolean; ocrEnabled?: boolean; reportsEnabled?: boolean }) {
|
||||
async function toggle(patch: { registrationEnabled?: boolean; accessRequestEnabled?: boolean; ocrEnabled?: boolean; reportsEnabled?: boolean; usernameChangeEnabled?: boolean }) {
|
||||
setSaving(true);
|
||||
setError("");
|
||||
try {
|
||||
@@ -251,6 +251,13 @@ export function AdminCenter() {
|
||||
</div>
|
||||
<Switch label={t("center.reportsTitle")} checked={settings?.reportsEnabled ?? false} onChange={(v) => toggle({ reportsEnabled: v })} />
|
||||
</div>
|
||||
<div className="flex items-center gap-4 rounded-3xl border border-stone-200/70 bg-white p-5 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-medium">{t("center.usernameChangeTitle")}</p>
|
||||
<p className="text-sm opacity-60">{t("center.usernameChangeDesc")}</p>
|
||||
</div>
|
||||
<Switch label={t("center.usernameChangeTitle")} checked={settings?.usernameChangeEnabled ?? false} onChange={(v) => toggle({ usernameChangeEnabled: v })} />
|
||||
</div>
|
||||
{saving && <p className="flex items-center gap-2 text-sm opacity-60"><Spinner size={14} /> {t("common.saving")}</p>}
|
||||
{!settings && !error && <p className="flex items-center gap-2 text-sm opacity-60"><Spinner size={14} /> {t("common.loading")}</p>}
|
||||
</section>
|
||||
|
||||
@@ -417,6 +417,7 @@ export function AdminPanel({ open, onClose, bare }: { open: boolean; onClose: ()
|
||||
|
||||
function EditUserDialog({ user, onClose, onSaved }: { user: User | null; onClose: () => void; onSaved: () => void }) {
|
||||
const { t } = useT();
|
||||
const [username, setUsername] = useState("");
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
const [avatarUrl, setAvatarUrl] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
@@ -425,6 +426,7 @@ function EditUserDialog({ user, onClose, onSaved }: { user: User | null; onClose
|
||||
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
setUsername(user.username);
|
||||
setDisplayName(user.displayName);
|
||||
setAvatarUrl(user.avatarUrl);
|
||||
setEmail(user.email);
|
||||
@@ -437,7 +439,7 @@ function EditUserDialog({ user, onClose, onSaved }: { user: User | null; onClose
|
||||
e.preventDefault();
|
||||
if (!user) return;
|
||||
try {
|
||||
await api.adminPatch(user.id, { displayName: displayName.trim(), avatarUrl: avatarUrl.trim(), email: email.trim(), theme });
|
||||
await api.adminPatch(user.id, { username: username.trim().toLowerCase(), displayName: displayName.trim(), avatarUrl: avatarUrl.trim(), email: email.trim(), theme });
|
||||
onSaved();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
@@ -448,6 +450,9 @@ function EditUserDialog({ user, onClose, onSaved }: { user: User | null; onClose
|
||||
return (
|
||||
<Modal open={user !== null} onClose={onClose} title={t("admin.editUserTitle", { user: user?.username ?? "" })} icon={Pencil}>
|
||||
<form onSubmit={save} className="space-y-4">
|
||||
<Field label={t("settings.username")} hint={t("settings.usernameHint")}>
|
||||
<Input value={username} onChange={(e) => setUsername(e.target.value)} maxLength={32} required autoComplete="username" disabled={user?.username === "admin"} />
|
||||
</Field>
|
||||
<Field label={t("common.displayName")}><Input value={displayName} onChange={(e) => setDisplayName(e.target.value)} maxLength={60} required /></Field>
|
||||
<Field label={t("admin.avatarUrl")}><Input value={avatarUrl} onChange={(e) => setAvatarUrl(e.target.value)} placeholder="https://…" /></Field>
|
||||
<Field label={t("admin.email")}><Input value={email} onChange={(e) => setEmail(e.target.value)} placeholder="[email protected]" inputMode="email" /></Field>
|
||||
|
||||
@@ -259,6 +259,7 @@ export function ShareModal({ conv, onClose }: { conv: Conversation | null; onClo
|
||||
|
||||
export function SettingsModal({
|
||||
user,
|
||||
usernameChangeEnabled = true,
|
||||
onClose,
|
||||
onSaved,
|
||||
onKeyRotated,
|
||||
@@ -267,6 +268,7 @@ export function SettingsModal({
|
||||
onProvidersChanged,
|
||||
}: {
|
||||
user: User | null;
|
||||
usernameChangeEnabled?: boolean;
|
||||
onClose: () => void;
|
||||
onSaved: (u: User) => void;
|
||||
onKeyRotated?: () => void;
|
||||
@@ -533,8 +535,8 @@ export function SettingsModal({
|
||||
/>
|
||||
{uploadError && <p className="mt-1.5 text-sm text-red-600 dark:text-red-400">{uploadError}</p>}
|
||||
</div>
|
||||
<Field label={t("settings.username")} hint={t("settings.usernameHint")}>
|
||||
<Input value={username} onChange={(e) => setUsername(e.target.value)} maxLength={32} required autoComplete="username" disabled={user?.username === "admin"} />
|
||||
<Field label={t("settings.username")} hint={usernameChangeEnabled ? t("settings.usernameHint") : t("settings.usernameDisabled")}>
|
||||
<Input value={username} onChange={(e) => setUsername(e.target.value)} maxLength={32} required autoComplete="username" disabled={user?.username === "admin" || !usernameChangeEnabled} />
|
||||
</Field>
|
||||
<Field label={t("common.displayName")}>
|
||||
<Input value={displayName} onChange={(e) => setDisplayName(e.target.value)} maxLength={60} required autoComplete="nickname" />
|
||||
|
||||
Reference in New Issue
Block a user