Add live account appeal updates and restricted status settings
This commit is contained in:
@@ -1,4 +1,15 @@
|
||||
/* Shared account and authentication surfaces, layered after the Minecraft theme. */
|
||||
.account-status-card {
|
||||
margin-bottom: 32px;
|
||||
gap: 18px;
|
||||
}
|
||||
.account-status-card h2 {
|
||||
margin: 0;
|
||||
}
|
||||
.account-status-reason {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.auth-page {
|
||||
padding-top: 48px;
|
||||
padding-bottom: 72px;
|
||||
@@ -284,10 +295,34 @@
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
.legacy-password-dialog { width: min(540px, calc(100vw - 24px)); }
|
||||
.legacy-password-dialog > svg { color: var(--green); }
|
||||
.legacy-password-dialog h2 { margin-top: 16px; }
|
||||
.legacy-password-dialog .field input { width: 100%; font: 14px monospace; padding: 12px; background: var(--bg); color: var(--text); border: 1px solid var(--line); }
|
||||
.legacy-password-dialog > .text-link { margin-top: 18px; }
|
||||
.password-saved { display: flex; gap: 10px; align-items: flex-start; margin: 20px 0; }
|
||||
.password-saved input { flex-shrink: 0; margin-top: 5px; accent-color: var(--green); }
|
||||
.legacy-password-dialog {
|
||||
width: min(540px, calc(100vw - 24px));
|
||||
}
|
||||
.legacy-password-dialog > svg {
|
||||
color: var(--green);
|
||||
}
|
||||
.legacy-password-dialog h2 {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.legacy-password-dialog .field input {
|
||||
width: 100%;
|
||||
font: 14px monospace;
|
||||
padding: 12px;
|
||||
background: var(--bg);
|
||||
color: var(--text);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
.legacy-password-dialog > .text-link {
|
||||
margin-top: 18px;
|
||||
}
|
||||
.password-saved {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.password-saved input {
|
||||
flex-shrink: 0;
|
||||
margin-top: 5px;
|
||||
accent-color: var(--green);
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ export default async function AdminPage() {
|
||||
"Manage accounts, site settings, integrations and community reports.",
|
||||
)}
|
||||
</p>
|
||||
<p>
|
||||
<div className="staff-page-switch">
|
||||
<Link className="button" href="/moderation">
|
||||
{t("Open Moderation Tools")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
<Moderation mode="admin" />
|
||||
</main>
|
||||
<Footer />
|
||||
|
||||
@@ -25,8 +25,15 @@
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
margin-top: 20px;
|
||||
gap: 20px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
.refreshActions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
margin: 24px 0 20px;
|
||||
}
|
||||
.list {
|
||||
display: grid;
|
||||
|
||||
@@ -17,11 +17,11 @@ export default async function Review() {
|
||||
{t("Review pending maps, map updates and account ban appeals.")}
|
||||
</p>
|
||||
{isAdministrator(staff.role) && (
|
||||
<p>
|
||||
<div className="staff-page-switch">
|
||||
<Link className="button" href="/admin">
|
||||
{t("Open Admin Tools")}
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<Moderation />
|
||||
</main>
|
||||
|
||||
@@ -245,6 +245,12 @@ html[lang="ru"] .mode-row h3 span {
|
||||
.moderation-page :is(.tool-card, .draft-row) {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
.staff-page-switch {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20px;
|
||||
margin: 24px 0 32px;
|
||||
}
|
||||
.moderator-setting-row {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
@@ -3,9 +3,10 @@ import Link from "next/link";
|
||||
import { redirect } from "next/navigation";
|
||||
import { Header, Footer } from "@/components/Shell";
|
||||
import { AccountSettings } from "@/components/AccountSettings";
|
||||
import { user } from "@/lib/supabase";
|
||||
import { rawUser } from "@/lib/supabase";
|
||||
import { accountProfile } from "@/lib/profile";
|
||||
import { Username } from "@/components/Username";
|
||||
import { ShieldCheck } from "lucide-react";
|
||||
export async function generateMetadata() {
|
||||
const { t } = await getI18n();
|
||||
return { title: t("Account settings") };
|
||||
@@ -16,18 +17,30 @@ export default async function Settings({
|
||||
searchParams: Promise<{ password?: string }>;
|
||||
}) {
|
||||
const { t } = await getI18n();
|
||||
const account = await user();
|
||||
const account = await rawUser();
|
||||
if (!account) redirect("/log-in?next=%2Fsettings");
|
||||
const [profile, query] = await Promise.all([
|
||||
accountProfile(account),
|
||||
searchParams,
|
||||
]);
|
||||
const active = profile.account_status === "active";
|
||||
const statusLabel =
|
||||
profile.account_status === "banned"
|
||||
? "Banned"
|
||||
: profile.account_status === "deactivated"
|
||||
? "Deactivated"
|
||||
: profile.account_status === "deleting"
|
||||
? "Deletion pending"
|
||||
: "Active";
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className="section page-content">
|
||||
<Link className="text-link" href="/dashboard">
|
||||
{t("← My account")}
|
||||
<Link
|
||||
className="text-link"
|
||||
href={active ? "/dashboard" : "/ban/appeal"}
|
||||
>
|
||||
{t(active ? "← My account" : "← Account access & appeals")}
|
||||
</Link>
|
||||
<div className="account-page-heading">
|
||||
<span className="eyebrow">{t("YOUR CARTOGRAPHER ACCOUNT")}</span>
|
||||
@@ -37,12 +50,44 @@ export default async function Settings({
|
||||
{t("Your name, your sign-in, your next expedition.")}
|
||||
</p>
|
||||
</div>
|
||||
<AccountSettings
|
||||
email={account.email ?? ""}
|
||||
displayName={profile.display_name}
|
||||
appearance={profile}
|
||||
passwordUpdated={query.password === "updated"}
|
||||
/>
|
||||
<section
|
||||
className="tool-card account-status-card"
|
||||
aria-labelledby="account-status-title"
|
||||
>
|
||||
<ShieldCheck size={24} aria-hidden="true" />
|
||||
<h2 id="account-status-title">{t("Account status")}</h2>
|
||||
<p>
|
||||
<span
|
||||
className="badge"
|
||||
data-account-status={profile.account_status}
|
||||
>
|
||||
{t(statusLabel)}
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
{t(
|
||||
active
|
||||
? "Your account is active. You can use MineGeo normally."
|
||||
: "Account changes are disabled while access is restricted.",
|
||||
)}
|
||||
</p>
|
||||
{!active && profile.account_reason && (
|
||||
<p className="account-status-reason">{profile.account_reason}</p>
|
||||
)}
|
||||
{!active && (
|
||||
<Link className="button" href="/ban/appeal">
|
||||
{t("View account status & appeal")}
|
||||
</Link>
|
||||
)}
|
||||
</section>
|
||||
{active && (
|
||||
<AccountSettings
|
||||
email={account.email ?? ""}
|
||||
displayName={profile.display_name}
|
||||
appearance={profile}
|
||||
passwordUpdated={query.password === "updated"}
|
||||
/>
|
||||
)}
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user