UI improvements

This commit is contained in:
Estifo77
2026-08-14 15:03:02 +03:00
parent 9a7b93cfb6
commit 7733fe8efd
5 changed files with 322 additions and 109 deletions

View File

@@ -7,15 +7,14 @@ import {
Stack,
Text,
Title,
UnstyledButton,
rem,
useComputedColorScheme,
useMantineColorScheme,
useMantineTheme,
type BoxProps,
} from '@mantine/core';
import { IconCheck, IconMoon, IconSun } from '@tabler/icons-react';
import { IconCheck } from '@tabler/icons-react';
import { useTranslation } from 'react-i18next';
import { ColorSchemeToggle, LanguageSwitcher } from '@ema-platform/ui';
import { useAuthConfig } from '../AuthConfig';
export function BrandMark({ size = 44, ...boxProps }: BoxProps & { size?: number }) {
@@ -33,35 +32,6 @@ export function BrandMark({ size = 44, ...boxProps }: BoxProps & { size?: number
);
}
function ThemeToggle() {
const { t } = useTranslation();
const { setColorScheme } = useMantineColorScheme();
const computed = useComputedColorScheme('light');
const isDark = computed === 'dark';
return (
<UnstyledButton
onClick={() => setColorScheme(isDark ? 'light' : 'dark')}
aria-label={t('authShell.toggleTheme', 'Toggle theme')}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: rem(36),
height: rem(36),
borderRadius: rem(10),
border: '1px solid var(--mantine-color-default-border)',
background: 'var(--mantine-color-body)',
color: 'var(--mantine-color-dimmed)',
cursor: 'pointer',
transition: 'all 150ms ease',
}}
>
{isDark ? <IconSun size={18} /> : <IconMoon size={18} />}
</UnstyledButton>
);
}
interface AuthShellProps {
children: ReactNode;
brandTitle?: string;
@@ -98,8 +68,9 @@ export function AuthShell({ children, brandTitle, brandSubtitle }: AuthShellProp
}}
p="md"
>
{/* Theme toggle — top-right corner */}
<Box
{/* Language switcher & Theme toggle — top-right corner */}
<Group
gap="xs"
style={{
position: 'absolute',
top: rem(16),
@@ -107,8 +78,9 @@ export function AuthShell({ children, brandTitle, brandSubtitle }: AuthShellProp
zIndex: 10,
}}
>
<ThemeToggle />
</Box>
<LanguageSwitcher supportedLanguages={['en', 'am']} />
<ColorSchemeToggle />
</Group>
<Flex
mih="100vh"

View File

@@ -11,8 +11,10 @@ import {
Text,
TextInput,
Title,
UnstyledButton,
} from "@mantine/core";
import {
IconArrowLeft,
IconArrowRight,
IconDeviceMobile,
IconLock,
@@ -50,6 +52,14 @@ export function LoginPage() {
const [meTrigger] = useApiMutation<AuthUser>();
const [profileTrigger] = useApiMutation<{ profile: CurrentProfile | null }>();
const handleBack = () => {
if (window.history.length > 1) {
navigate(-1);
} else {
navigate("/");
}
};
// Built inside the component (not module scope) so validation messages
// pick up the active language — same pattern as ProfilePage's forms.
const schema = z.object({
@@ -149,6 +159,32 @@ export function LoginPage() {
return (
<AuthShell>
<Stack gap="lg">
<UnstyledButton
onClick={handleBack}
style={{
display: "inline-flex",
alignItems: "center",
gap: 6,
fontSize: 14,
fontWeight: 600,
color: "var(--mantine-color-dimmed)",
cursor: "pointer",
width: "fit-content",
transition: "all 150ms ease",
}}
onMouseEnter={(e) => {
e.currentTarget.style.color = "var(--mantine-primary-color-filled)";
e.currentTarget.style.transform = "translateX(-3px)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.color = "var(--mantine-color-dimmed)";
e.currentTarget.style.transform = "translateX(0)";
}}
>
<IconArrowLeft size={18} />
{t("common.back", "Back")}
</UnstyledButton>
<div>
<Title order={2} fz={30}>
{t("login.welcome", { appName, defaultValue: "Welcome to {{appName}}" })}

View File

@@ -11,8 +11,10 @@ import {
Text,
TextInput,
Title,
UnstyledButton,
} from '@mantine/core';
import {
IconArrowLeft,
IconArrowRight,
IconAt,
IconDeviceMobile,
@@ -61,6 +63,14 @@ export function SignupPage() {
}>();
const [meTrigger] = useApiMutation<AuthUser>();
const handleBack = () => {
if (window.history.length > 1) {
navigate(-1);
} else {
navigate('/');
}
};
// Order matches passwordRules' default list: length, lowercase, uppercase,
// number, special character. Shared between the zod schema (field error)
// and the live checklist below, so both agree on the wording.
@@ -155,6 +165,32 @@ export function SignupPage() {
})}
>
<Stack gap="lg">
<UnstyledButton
onClick={handleBack}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: 6,
fontSize: 14,
fontWeight: 600,
color: 'var(--mantine-color-dimmed)',
cursor: 'pointer',
width: 'fit-content',
transition: 'all 150ms ease',
}}
onMouseEnter={(e) => {
e.currentTarget.style.color = 'var(--mantine-primary-color-filled)';
e.currentTarget.style.transform = 'translateX(-3px)';
}}
onMouseLeave={(e) => {
e.currentTarget.style.color = 'var(--mantine-color-dimmed)';
e.currentTarget.style.transform = 'translateX(0)';
}}
>
<IconArrowLeft size={18} />
{t('common.back', 'Back')}
</UnstyledButton>
<div>
<Title order={2} fz={30}>
{t('signup.title', 'Create account')}