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,6 @@
"use client";
import { useI18n } from "@/components/I18nProvider";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { ArrowRight, Compass, Mail, ShieldCheck } from "lucide-react";
@@ -13,6 +15,7 @@ export function SignIn({
next?: string;
initialError?: string;
}) {
const { t } = useI18n();
const [mode, setMode] = useState<Mode>("login"),
[email, setEmail] = useState(""),
[name, setName] = useState(""),
@@ -77,40 +80,41 @@ export function SignIn({
return (
<div className="auth-layout">
<aside className="auth-story">
<span className="eyebrow">YOUR NEXT EXPEDITION</span>
<span className="eyebrow">{t("YOUR NEXT EXPEDITION")}</span>
<h1>
A familiar world.
{t("A familiar world.")}
<br />
Your own perspective.
{t("Your own perspective.")}
</h1>
<p>
Keep your journeys, share the places you know, and help build the next
map.
{t(
"Keep your journeys, share the places you know, and help build the next map.",
)}
</p>
<div className="auth-story-map" aria-hidden="true">
<Compass size={52} />
<span>Every world has a story.</span>
<span>{t("Every world has a story.")}</span>
</div>
<div className="auth-benefits">
<span>
<Compass size={18} />
Save your expeditions
{t("Save your expeditions")}
</span>
<span>
<ShieldCheck size={18} />
Keep control of your maps
{t("Keep control of your maps")}
</span>
</div>
</aside>
<section className="auth-card">
<div className="auth-tabs" aria-label="Account options">
<div className="auth-tabs" aria-label={t("Account options")}>
<button
type="button"
aria-pressed={mode === "login"}
disabled={busy}
onClick={() => changeMode("login")}
>
Log in
{t("Log in")}
</button>
<button
type="button"
@@ -118,24 +122,26 @@ export function SignIn({
disabled={busy}
onClick={() => changeMode("signup")}
>
Create account
{t("Create account")}
</button>
</div>
<span className="eyebrow">YOUR CARTOGRAPHER ACCOUNT</span>
<h2>{title}</h2>
<span className="eyebrow">{t("YOUR CARTOGRAPHER ACCOUNT")}</span>
<h2>{t(title)}</h2>
<p className="muted">
{mode === "signup"
? "Choose the name people will see on your maps."
? t("Choose the name people will see on your maps.")
: mode === "recover"
? "We’ll email you a link to set a new password."
? t("We’ll email you a link to set a new password.")
: mode === "magic"
? "For existing accounts. Open the link on this device to continue."
: "Log in with your email and password."}
? t(
"For existing accounts. Open the link on this device to continue.",
)
: t("Log in with your email and password.")}
</p>
<form onSubmit={submit} aria-busy={busy}>
{mode === "signup" && (
<label className="field">
Display name
{t("Display name")}
<input
name="displayName"
autoComplete="nickname"
@@ -143,15 +149,17 @@ export function SignIn({
maxLength={32}
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="How should we call you?"
placeholder={t("How should we call you?")}
required
disabled={busy}
/>
<small>2–32 characters. You can change it in settings.</small>
<small>
{t("2–32 characters. You can change it in settings.")}
</small>
</label>
)}
<label className="field">
Email address
{t("Email address")}
<input
name="email"
type="email"
@@ -165,7 +173,7 @@ export function SignIn({
</label>
{(mode === "login" || mode === "signup") && (
<label className="field">
Password
{t("Password")}
<input
name="password"
type="password"
@@ -180,14 +188,16 @@ export function SignIn({
/>
{mode === "signup" && (
<small>
Use at least 12 characters. A unique passphrase works well.
{t(
"Use at least 12 characters. A unique passphrase works well.",
)}
</small>
)}
</label>
)}
{mode === "signup" && (
<label className="field">
Confirm password
{t("Confirm password")}
<input
name="confirmPassword"
type="password"
@@ -206,17 +216,17 @@ export function SignIn({
disabled={busy}
onClick={() => changeMode("recover")}
>
Forgot password?
{t("Forgot password?")}
</button>
)}
{error && (
<p role="alert" className="error-message">
{error}
{t(error)}
</p>
)}
{message && (
<p role="status" className="success-message">
{message}
{t(message)}
</p>
)}
<button className="button primary full" disabled={busy}>
@@ -226,25 +236,25 @@ export function SignIn({
<ArrowRight size={17} />
)}{" "}
{busy
? "Please wait…"
? t("Please wait…")
: {
login: "Log in",
signup: "Create account",
recover: "Send reset link",
magic: "Send sign-in link",
login: t("Log in"),
signup: t("Create account"),
recover: t("Send reset link"),
magic: t("Send sign-in link"),
}[mode]}
</button>
</form>
{mode === "login" && (
<div className="auth-alternative">
<span>Already use email links?</span>
<span>{t("Already use email links?")}</span>
<button
type="button"
className="text-link"
disabled={busy}
onClick={() => changeMode("magic")}
>
Send me a sign-in link
{t("Send me a sign-in link")}
</button>
</div>
)}
@@ -255,11 +265,13 @@ export function SignIn({
disabled={busy}
onClick={() => changeMode("login")}
>
← Back to log in
{t("← Back to log in")}
</button>
)}
<p className="auth-footnote">
Your email stays private. Your display name appears with your maps.
{t(
"Your email stays private. Your display name appears with your maps.",
)}
</p>
</section>
</div>