mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
UI improvements
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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}}" })}
|
||||
|
||||
@@ -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')}
|
||||
|
||||
Reference in New Issue
Block a user