feat: localize auth and portal pages
This commit is contained in:
+57
-14
@@ -1,12 +1,34 @@
|
||||
import { login } from "@/app/login/actions";
|
||||
import { AuthPageShell } from "@/components/auth/auth-page-shell";
|
||||
import { ErrorToaster } from "@/components/error-toaster";
|
||||
import { LocaleSelectForm } from "@/components/i18n/locale-select-form";
|
||||
import { LockKeyhole, LogIn, Mail } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Input, Label } from "poyraz-ui/atoms";
|
||||
import { Alert, AlertDescription } from "poyraz-ui/molecules";
|
||||
import { SubmitButton } from "@/components/auth/submit-button";
|
||||
import { getPublicBranding } from "@/server/branding/runtime";
|
||||
import { getSqliteConnection } from "@/server/db/client";
|
||||
import { ContentTranslationService } from "@/server/i18n/content";
|
||||
import { resolveRequestLocale } from "@/server/i18n/resolver";
|
||||
import { createTranslator } from "@/server/i18n/translator";
|
||||
import type { TranslationValues } from "@/lib/i18n";
|
||||
|
||||
function firstParam(value: string | string[] | undefined): string | null {
|
||||
if (Array.isArray(value)) return value[0] ?? null;
|
||||
return value ?? null;
|
||||
}
|
||||
|
||||
function resolveAuthMessage(
|
||||
code: string | null,
|
||||
fallback: string | null,
|
||||
t: (key: string, values?: TranslationValues) => string,
|
||||
): string | null {
|
||||
if (!code) return fallback;
|
||||
const key = code.startsWith("auth.") ? code : `auth.${code}`;
|
||||
const message = t(key);
|
||||
return message === key ? fallback : message;
|
||||
}
|
||||
|
||||
export default async function LoginPage({
|
||||
searchParams,
|
||||
@@ -14,39 +36,60 @@ export default async function LoginPage({
|
||||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||
}) {
|
||||
const resolvedParams = await searchParams;
|
||||
const error = resolvedParams?.error;
|
||||
const message = resolvedParams?.message;
|
||||
const error = firstParam(resolvedParams?.error);
|
||||
const code = firstParam(resolvedParams?.code);
|
||||
const rawMessage = firstParam(resolvedParams?.message);
|
||||
const branding = getPublicBranding();
|
||||
const locale = await resolveRequestLocale();
|
||||
const t = createTranslator(locale.locale, ["auth"]).t;
|
||||
const localization = new ContentTranslationService(getSqliteConnection().db).getPublicLocalizationContext();
|
||||
const message = resolveAuthMessage(code, rawMessage, t);
|
||||
const marketing = {
|
||||
headline: t("auth.marketing.headline"),
|
||||
description: t("auth.marketing.description", { app: branding.organizationName ?? branding.applicationName }),
|
||||
openSource: t("auth.marketing.openSource"),
|
||||
github: t("auth.marketing.github"),
|
||||
via: t("auth.marketing.via"),
|
||||
builtBy: t("auth.marketing.builtBy"),
|
||||
highlights: [
|
||||
t("auth.highlights.clients"),
|
||||
t("auth.highlights.calendar"),
|
||||
t("auth.highlights.finance"),
|
||||
t("auth.highlights.reports"),
|
||||
] as [string, string, string, string],
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{error && message && <ErrorToaster message={String(message)} />}
|
||||
{error && message ? <ErrorToaster message={message} /> : null}
|
||||
<AuthPageShell
|
||||
branding={{
|
||||
applicationName: branding.organizationName ?? branding.applicationName,
|
||||
lightLogoUrl: branding.lightLogoUrl,
|
||||
darkLogoUrl: branding.darkLogoUrl,
|
||||
}}
|
||||
title="Giriş yap"
|
||||
description="Neta çalışma alanına erişmek için hesabına giriş yap."
|
||||
title={t("auth.login.title")}
|
||||
description={t("auth.login.description")}
|
||||
marketing={marketing}
|
||||
form={
|
||||
<form className="space-y-6">
|
||||
{!error && message ? (
|
||||
<Alert variant="success" appearance="soft">
|
||||
<AlertDescription>{String(message)}</AlertDescription>
|
||||
<AlertDescription>{message}</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
<LocaleSelectForm label={t("auth.language")} value={locale.locale} locales={localization.locales} />
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="flex items-center gap-2">
|
||||
<Mail className="h-4 w-4 text-muted-foreground" />
|
||||
E-posta
|
||||
{t("auth.login.email")}
|
||||
</Label>
|
||||
<Input
|
||||
id="email"
|
||||
name="email"
|
||||
type="email"
|
||||
placeholder="ornek@mail.com"
|
||||
placeholder={t("auth.login.emailPlaceholder")}
|
||||
required
|
||||
className="h-11"
|
||||
/>
|
||||
@@ -56,13 +99,13 @@ export default async function LoginPage({
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<Label htmlFor="password" className="flex items-center gap-2">
|
||||
<LockKeyhole className="h-4 w-4 text-muted-foreground" />
|
||||
Şifre
|
||||
{t("auth.login.password")}
|
||||
</Label>
|
||||
<Link
|
||||
href="/forgot-password"
|
||||
className="text-sm font-medium text-primary transition-colors hover:text-primary-hover"
|
||||
>
|
||||
Şifremi unuttum
|
||||
{t("auth.login.forgotPassword")}
|
||||
</Link>
|
||||
</div>
|
||||
<Input
|
||||
@@ -75,21 +118,21 @@ export default async function LoginPage({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SubmitButton size="lg" formAction={login} className="w-full gap-2" pendingText="Giriş yapılıyor...">
|
||||
<SubmitButton size="lg" formAction={login} className="w-full gap-2" pendingText={t("auth.login.pending")}>
|
||||
<LogIn className="h-4 w-4" />
|
||||
Giriş yap
|
||||
{t("auth.login.submit")}
|
||||
</SubmitButton>
|
||||
</form>
|
||||
}
|
||||
secondaryAction={null}
|
||||
footer={
|
||||
<div className="text-center text-sm">
|
||||
İlk kurulumu yapmadın mı?{" "}
|
||||
{t("auth.login.setupPrompt")}{" "}
|
||||
<Link
|
||||
href="/register"
|
||||
className="font-medium text-primary transition-colors hover:text-primary-hover"
|
||||
>
|
||||
Admin hesabını oluştur
|
||||
{t("auth.login.createAdmin")}
|
||||
</Link>
|
||||
</div>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user