Improve MineGeo interface and functionality

This commit is contained in:
2026-10-06 19:24:41 +05:00
parent 7404f1dc1c
commit 233e4d57f0
67 changed files with 3053 additions and 643 deletions

View File

@@ -1,4 +1,7 @@
"use client";
import { statusMessage } from "@/shared/i18n";
import { useI18n } from "@/components/I18nProvider";
import { useCallback, useEffect, useState } from "react";
import Link from "next/link";
import { createBrowserClient } from "@supabase/ssr";
@@ -11,6 +14,7 @@ type Draft = MapListing & {
upload_jobs: ProcessingJob | null;
};
export function Dashboard() {
const { t } = useI18n();
const [maps, setMaps] = useState<Draft[]>([]),
[error, setError] = useState(""),
[tokens, setTokens] = useState<
@@ -77,33 +81,34 @@ export function Dashboard() {
<>
<div className="dashboard-toolbar">
<p className="muted">
Your maps start privately. You decide when they’re ready for review.
Contributions are hidden here after five days. Their panoramas and
direct links stay available.
{t(
"Your maps start privately. You decide when they’re ready for review. Contributions are hidden here after five days. Their panoramas and direct links stay available.",
)}
</p>
<div className="inline-actions">
<Link className="button" href="/settings">
Account settings
{t("Account settings")}
</Link>
<button className="button" onClick={logout}>
Sign out
{t("Sign out")}
</button>
</div>
</div>
{error && (
<p role="alert" className="error-message">
{error}
{t(error)}
</p>
)}
{!maps.length && (
<div className="empty-state">
<h2>A blank map is a beginning.</h2>
<h2>{t("A blank map is a beginning.")}</h2>
<p>
Install the Fabric mod to capture while you explore, or the server
plugin to export your world.
{t(
"Install the Fabric mod to capture while you explore, or the server plugin to export your world.",
)}
</p>
<Link className="button primary" href="/downloads">
Get the capture tools →
{t("Get the capture tools →")}
</Link>
</div>
)}
@@ -111,24 +116,34 @@ export function Dashboard() {
<article className="draft-row" key={m.id}>
<div>
<span className="eyebrow">
{m.source === "plugin" ? "SERVER EXPORT" : "PLAYER CAPTURE"}
{m.source === "plugin" ? t("SERVER EXPORT") : t("PLAYER CAPTURE")}
</span>
<h3>{m.name}</h3>
<span className="badge">
{m.contribution_status
? `Contribution: ${m.contribution_status}`
? t("Contribution: {status}", {
status: t(statusMessage(m.contribution_status)),
})
: m.update_state
? `Update: ${m.update_state}`
: m.state}
? t("Update: {status}", {
status: t(statusMessage(m.update_state)),
})
: t(statusMessage(m.state))}
</span>{" "}
<span className="muted">{m.panorama_count} locations</span>
<span className="muted">
{m.panorama_count} {t("locations")}
</span>
{m.banned_at ? (
<p className="error-message">
Map banned after three confirmed reports. Open your map to
appeal.
{t(
"Map banned after three confirmed reports. Open your map to appeal.",
)}
</p>
) : m.report_strikes > 0 ? (
<p className="muted">{m.report_strikes}/3 confirmed reports</p>
<p className="muted">
{m.report_strikes}
{t("/3 confirmed reports")}
</p>
) : null}
<ProcessingProgress job={m.upload_jobs} />
{m.rejection_reason && (
@@ -139,38 +154,42 @@ export function Dashboard() {
)}
</div>
<Link href={"/dashboard/" + m.id} className="button">
{m.parent_dataset_id ? "Open contribution →" : "Open map →"}
{m.parent_dataset_id ? t("Open contribution →") : t("Open map →")}
</Link>
</article>
))}
<section className="token-box">
<span className="eyebrow">FOR SERVER ADMINISTRATORS</span>
<h2>Link your server plugin.</h2>
<span className="eyebrow">{t("FOR SERVER ADMINISTRATORS")}</span>
<h2>{t("Link your server plugin.")}</h2>
<p className="muted">
Create a contributor token, then run{" "}
<code>/minegeo link &lt;token&gt;</code> in your server console.
Fabric uses browser pairing instead.
{t("Create a contributor token, then run")}{" "}
<code>/minegeo link &lt;token&gt;</code>{" "}
{t("in your server console. Fabric uses browser pairing instead.")}
</p>
<button className="button" disabled={busy} onClick={newToken}>
Generate plugin token
{t("Generate plugin token")}
</button>
{token && (
<>
<p className="success-message">
Copy this token now. It is shown once. Keep it out of public chat.
{t(
"Copy this token now. It is shown once. Keep it out of public chat.",
)}
</p>
<code>{token}</code>
</>
)}
{tokens.map((t) => (
<div className="draft-row" key={t.id}>
{tokens.map((entry) => (
<div className="draft-row" key={entry.id}>
<span>
{t.label}{" "}
<small className="muted">{t.revoked ? "· revoked" : ""}</small>
{entry.label}{" "}
<small className="muted">
{entry.revoked ? t("· revoked") : ""}
</small>
</span>
{!t.revoked && (
<button className="button" onClick={() => revoke(t.id)}>
Revoke
{!entry.revoked && (
<button className="button" onClick={() => revoke(entry.id)}>
{t("Revoke")}
</button>
)}
</div>