Redesign login, register, me pages with premium layout and i18n
Deploy / release (push) Skipped
Deploy / deploy (push) Successful in 2m7s

This commit is contained in:
openhands committed 2026-07-22 14:42:01 +02:00
1 parent 063ac9ec40
commit 1ffd46713a
7 files changed
+565 -223

No files matched your search

File diff suppressed because it is too large. Load diff
+10 -7
View File
@@ -2,11 +2,6 @@
import { useState } from "react"; import { useState } from "react";
/**
* Copies the user's referral link to the clipboard. Mirrors AtomCMS's
* copyCode() helper on the /me page, but resolves the link to an absolute URL
* (the server only knows a relative path, so we prepend the current origin).
*/
export default function CopyReferralButton({ value }: { value: string }) { export default function CopyReferralButton({ value }: { value: string }) {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
@@ -18,7 +13,6 @@ export default function CopyReferralButton({ value }: { value: string }) {
try { try {
await navigator.clipboard.writeText(absolute); await navigator.clipboard.writeText(absolute);
} catch { } catch {
// Older browsers / insecure contexts: fall back to a hidden textarea.
const ta = document.createElement("textarea"); const ta = document.createElement("textarea");
ta.value = absolute; ta.value = absolute;
ta.style.position = "fixed"; ta.style.position = "fixed";
@@ -37,7 +31,16 @@ export default function CopyReferralButton({ value }: { value: string }) {
} }
return ( return (
<button type="button" className="btn btn-secondary" onClick={copy}> <button
type="button"
onClick={copy}
className="rounded-xl font-bold text-sm px-5 py-2.5 transition-all duration-200 hover:scale-[1.02] active:scale-95 shrink-0"
style={{
background: "color-mix(in srgb, var(--color-primary) 12%, transparent)",
color: "var(--color-primary-readable, var(--color-primary))",
border: `2px solid color-mix(in srgb, var(--color-primary) 20%, transparent)`,
}}
>
{copied ? "Copied!" : "Copy link"} {copied ? "Copied!" : "Copy link"}
</button> </button>
); );
+199 -104
View File
File diff suppressed because it is too large. Load diff
+89 -38
View File
File diff suppressed because it is too large. Load diff
+10 -16
View File
@@ -10,7 +10,8 @@ import { captchaConfig } from "@/lib/services/captcha";
import { siteSettings } from "@/lib/services/site-settings"; import { siteSettings } from "@/lib/services/site-settings";
export default async function RegisterPage() { export default async function RegisterPage() {
const t = await getTranslations("header"); const th = await getTranslations("header");
const tpr = await getTranslations("pages.register");
const [hotelName, imagerRaw, cfg] = await Promise.all([ const [hotelName, imagerRaw, cfg] = await Promise.all([
siteSettings.get("hotel_name", "Hotel"), siteSettings.get("hotel_name", "Hotel"),
siteSettings.get( siteSettings.get(
@@ -67,26 +68,19 @@ export default async function RegisterPage() {
}} }}
> >
<span className="w-2 h-2 rounded-full bg-green-500" /> <span className="w-2 h-2 rounded-full bg-green-500" />
{online.toLocaleString()} online {tpr("usersOnline", { count: online })}
</div> </div>
<h1 <h1
className="text-xl font-black" className="text-xl font-black"
style={{ color: "var(--color-text-readable)" }} style={{ color: "var(--color-text-readable)" }}
> >
Join{" "} {tpr("title")} — {hotelName}
<span
style={{
color: "var(--color-primary-readable, var(--color-primary))",
}}
>
{hotelName}
</span>
</h1> </h1>
<p <p
className="text-xs mt-1 mx-auto max-w-[200px] leading-relaxed" className="text-xs mt-1 mx-auto max-w-[200px] leading-relaxed"
style={{ color: "var(--color-text-muted)" }} style={{ color: "var(--color-text-muted)" }}
> >
{t("tagline")} {th("tagline")}
</p> </p>
</div> </div>
@@ -103,7 +97,7 @@ export default async function RegisterPage() {
className="text-xs font-bold uppercase tracking-widest mb-3" className="text-xs font-bold uppercase tracking-widest mb-3"
style={{ color: "var(--color-text-muted)" }} style={{ color: "var(--color-text-muted)" }}
> >
Who&apos;s online {tpr("whoIsOnline")}
</h3> </h3>
<div className="grid grid-cols-4 gap-1.5"> <div className="grid grid-cols-4 gap-1.5">
{recentUsers.map((u) => ( {recentUsers.map((u) => (
@@ -143,7 +137,7 @@ export default async function RegisterPage() {
className="text-xs text-center" className="text-xs text-center"
style={{ color: "var(--color-text-muted)" }} style={{ color: "var(--color-text-muted)" }}
> >
Already have an account?{" "} {tpr("alreadyHaveAccountPre")}{" "}
<Link <Link
href="/login" href="/login"
className="font-extrabold hover:underline" className="font-extrabold hover:underline"
@@ -151,7 +145,7 @@ export default async function RegisterPage() {
color: "var(--color-primary-readable, var(--color-primary))", color: "var(--color-primary-readable, var(--color-primary))",
}} }}
> >
Sign in {tpr("alreadyHaveAccountLink")}
</Link> </Link>
</p> </p>
</div> </div>
@@ -163,13 +157,13 @@ export default async function RegisterPage() {
className="text-2xl font-black tracking-tight" className="text-2xl font-black tracking-tight"
style={{ color: "var(--color-text-readable)" }} style={{ color: "var(--color-text-readable)" }}
> >
Create your account {tpr("title")}
</h2> </h2>
<p <p
className="text-sm mt-0.5" className="text-sm mt-0.5"
style={{ color: "var(--color-text-muted)" }} style={{ color: "var(--color-text-muted)" }}
> >
Free and takes less than a minute {tpr("subtitle")}
</p> </p>
</div> </div>
<RegisterForm <RegisterForm
File diff suppressed because it is too large. Load diff
+18 -1
View File
@@ -234,7 +234,18 @@
"noArticles": "No articles yet.", "noArticles": "No articles yet.",
"noPhotos": "No photos yet.", "noPhotos": "No photos yet.",
"noUsers": "No users online", "noUsers": "No users online",
"latestArticle": "Latest Article" "latestArticle": "Latest Article",
"statsOnline": "Online",
"statsCitizens": "Citizens",
"statsRooms": "Rooms",
"featuresAvatar": "Avatar",
"featuresAvatarDesc": "Create your unique look",
"featuresFurniture": "Furniture",
"featuresFurnitureDesc": "Decorate rooms with items",
"featuresCommunity": "Community",
"featuresCommunityDesc": "Make friends and chat",
"featuresGames": "Games",
"featuresGamesDesc": "Compete and have fun"
}, },
"news": { "news": {
"title": "News", "title": "News",
@@ -894,6 +905,8 @@
"title": "Sign in", "title": "Sign in",
"subtitle": "Welcome back — log in to enter the hotel.", "subtitle": "Welcome back — log in to enter the hotel.",
"subtitle2fa": "Enter the 6-digit code from your authenticator.", "subtitle2fa": "Enter the 6-digit code from your authenticator.",
"welcomeBack": "Welcome back",
"welcomeBackSub": "Sign in to continue to {hotelName}",
"usernamePlaceholder": "Username", "usernamePlaceholder": "Username",
"passwordPlaceholder": "Password", "passwordPlaceholder": "Password",
"codePlaceholder": "2FA code", "codePlaceholder": "2FA code",
@@ -927,7 +940,11 @@
"createAccount": "Create account", "createAccount": "Create account",
"creatingAccount": "Creating account...", "creatingAccount": "Creating account...",
"alreadyHaveAccount": "Already have an account? Login!", "alreadyHaveAccount": "Already have an account? Login!",
"alreadyHaveAccountPre": "Already have an account?",
"alreadyHaveAccountLink": "Sign in",
"register": "Register", "register": "Register",
"whoIsOnline": "Who's online",
"usersOnline": "{count} online",
"termsError": "You must accept the terms & rules to register.", "termsError": "You must accept the terms & rules to register.",
"usernameError": "Username is required.", "usernameError": "Username is required.",
"emailError": "Valid email is required.", "emailError": "Valid email is required.",