diff --git a/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.module.scss b/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.module.scss index 365b9849a..928319d71 100644 --- a/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.module.scss +++ b/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.module.scss @@ -1,34 +1,167 @@ -.baseAuthForm { - @media (max-width: breakpoint(xl)) { - width: 280px; +.baseAuthForm.baseAuthForm { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + max-width: 806px; + margin: 0 auto; + background: var(--base-card-background); + border: 2px solid var(--black); + box-shadow: 6px 12px 0px var(--drop-shadows); + border-radius: 12px; + padding: 44px 80px; + gap: 36px; + box-sizing: border-box; + min-height: 852px; + + input[type='checkbox'] { + appearance: none; + -webkit-appearance: none; + width: 24px; + height: 24px; + padding: 0; + margin-right: 8px; + cursor: pointer; + flex: none; + background: var(--content-primary); + border: 1px solid var(--black); + border-radius: 4px; + box-shadow: 1px 1px 0px var(--drop-shadows); + + &:checked { + background-image: url('../../../../shared/assets/icons/check.svg'); + background-repeat: no-repeat; + background-position: center; + background-size: 16px 14px; + } + } + + .actions button { + background-color: var(--content-primary); + color: var(--drop-shadows); + border: 2px solid var(--drop-shadows); + box-shadow: 2px 4px 0px var(--drop-shadows); + border-radius: 25px; + padding: 12px 24px; + height: 47px; + font-family: var(--font-family-secondary); + font-size: 18px; + font-weight: 500; + line-height: 23px; + + @media (max-width: breakpoint(sm)) { + background-color: #ffb703; + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-300) var(--spacing-400); + height: 53px; + font: var(--font-dm-m); + } + } + + @media (max-width: breakpoint(md)) { + padding: 32px 40px; + } + + @media (max-width: breakpoint(sm)) { + box-shadow: var(--m); + padding: 15px 15px 10px; + gap: 18px; + min-height: 641px; + + h1 { + margin-bottom: 17px; + } + } +} + +.logo { + width: 282px; + height: 238px; + object-fit: contain; + margin: 0 auto; + + @media (max-width: breakpoint(sm)) { + width: 198px; + height: 167px; } } .fields { - gap: 4px; + gap: 24px; + width: 100%; + max-width: 460px; + margin: 0 auto; font: var(--font-dm-bold-s); + @media (max-width: breakpoint(sm)) { + gap: 8px; + margin-bottom: 6px; + } + + label { + font: var(--font-dm-s); + color: var(--white); + margin-bottom: 8px; + padding-left: 8px; + display: flex; + align-items: center; + } + input { - background-color: var(--background-color); - color: white; - border: 2px solid rgba(18, 18, 18, 1); + background-color: #172936; + color: var(--neutral); + border: none; border-radius: 25px; + padding: 12px 24px; + height: 47px; + width: 100%; + box-sizing: border-box; } + input::placeholder { - color: var(--primary-color); + color: var(--neutral); } + input:focus::placeholder { color: transparent; } } + .header { - font: var(--font-dm-bold-m); + font: var(--font-sw-xxxl); display: flex; - font-size: 0.7em; - justify-content: start; - font-weight: 300; + justify-content: center; + align-items: center; + text-align: center; + color: var(--content-primary); + + @media (max-width: breakpoint(sm)) { + font: var(--font-sw-xl); + } } .actions { text-align: center; + gap: 16px; + width: 100%; + max-width: 460px; + margin: 0 auto; + color: var(--white); + + button { + width: 100%; + } + + p { + color: var(--white); + font: var(--font-dm-s); + } + + a { + text-decoration: underline; + color: var(--content-primary); + } } diff --git a/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.tsx b/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.tsx index 3ebf357da..4a4100b5e 100644 --- a/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.tsx +++ b/frontend-next-migration/src/entities/Auth/ui/BaseAuthForm/BaseAuthForm.tsx @@ -1,12 +1,15 @@ import { ReactNode, FC } from 'react'; +import Image from 'next/image'; import { CustomForm } from '@/shared/ui/CustomForm'; import cls from './BaseAuthForm.module.scss'; +import defaultLogo from '@/shared/assets/images/Alt_zone_logo_teksti.png'; type BaseAuthFormProps = { header: ReactNode; fields: ReactNode; actions: ReactNode; onSubmit: () => void; + logo?: string | null; }; const BaseAuthForm: FC & { @@ -15,13 +18,22 @@ const BaseAuthForm: FC & { InputField: typeof CustomForm.InputField; Checkbox: typeof CustomForm.Checkbox; } = (props: BaseAuthFormProps) => { - const { header, fields, actions, onSubmit } = props; + const { header, fields, actions, onSubmit, logo } = props; return ( + {logo !== null && ( + AltZone Logo + )} {header}
{fields}
{actions}
diff --git a/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.test.tsx b/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.test.tsx index e23ec1aab..e5857749e 100644 --- a/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.test.tsx +++ b/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.test.tsx @@ -1,5 +1,6 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { useClientTranslation } from '@/shared/i18n'; +import useIsMobileSize from '@/shared/lib/hooks/useIsMobileSize'; import { useLoginForm } from '../../model/useLoginForm'; import LoginForm from './LoginForm'; @@ -7,19 +8,21 @@ jest.mock('@/shared/i18n', () => ({ useClientTranslation: jest.fn(), })); +jest.mock('@/shared/lib/hooks/useIsMobileSize'); + jest.mock('../../model/useLoginForm', () => ({ useLoginForm: jest.fn(), })); describe('LoginForm', () => { const mockT = jest.fn((key) => key); - const mockHandleSubmit = jest.fn((fn) => fn); const mockOnFormSubmit = jest.fn(); const mockOnSuccessLogin = jest.fn(); const mockErrors = {}; beforeEach(() => { (useClientTranslation as jest.Mock).mockReturnValue({ t: mockT }); + (useIsMobileSize as jest.Mock).mockReturnValue({ isMobileSize: false }); (useLoginForm as jest.Mock).mockReturnValue({ register: jest.fn(), handleSubmit: jest.fn((callback) => () => callback()), @@ -30,30 +33,35 @@ describe('LoginForm', () => { }); const defaultProps = { - toRegisterPage: '/register', onSuccessLogin: mockOnSuccessLogin, - toForgottenPwPage: '/forgot-password', }; it('should render form with username and password fields', () => { render(); expect(screen.getAllByText('log_in').length).toBeGreaterThanOrEqual(1); - expect(screen.getByPlaceholderText('username')).toBeInTheDocument(); - expect(screen.getByPlaceholderText('password')).toBeInTheDocument(); + expect(screen.getByLabelText('username')).toBeInTheDocument(); + expect(screen.getByLabelText('password')).toBeInTheDocument(); expect(screen.getByRole('button', { name: 'log_in' })).toBeInTheDocument(); }); - // todo - // it('should call handleSubmit and onFormSubmit on form submit', () => { - // render(); - // - // const submitButton = screen.getByRole('button', { name: 'send' }); - // fireEvent.click(submitButton); - // - // expect(mockHandleSubmit).toHaveBeenCalledWith(mockOnFormSubmit); - // expect(mockOnFormSubmit).toHaveBeenCalled(); - // }); + it('should render combined username/email label on mobile', () => { + (useIsMobileSize as jest.Mock).mockReturnValue({ isMobileSize: true }); + + render(); + + expect(screen.getByLabelText('username_email')).toBeInTheDocument(); + expect(screen.queryByLabelText('username')).not.toBeInTheDocument(); + }); + + it('should render register in-game link pointing to the app store', () => { + render(); + + const link = screen.getByText('register_in_game_link'); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('target', '_blank'); + expect(link).toHaveAttribute('rel', 'noopener noreferrer'); + }); it('should display validation errors if present', () => { const mockErrorsWithMessages = { @@ -62,7 +70,7 @@ describe('LoginForm', () => { }; (useLoginForm as jest.Mock).mockReturnValue({ register: jest.fn(), - handleSubmit: mockHandleSubmit, + handleSubmit: jest.fn((callback) => () => callback()), onFormSubmit: mockOnFormSubmit, errors: mockErrorsWithMessages, }); diff --git a/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx b/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx index e960dc652..03c3a66e2 100644 --- a/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx +++ b/frontend-next-migration/src/features/AuthByUsername/ui/LoginForm/LoginForm.tsx @@ -3,6 +3,8 @@ import { useClientTranslation } from '@/shared/i18n'; import { useLoginForm } from '../../model/useLoginForm'; import { BaseAuthForm } from '@/entities/Auth'; import { ReactNode } from 'react'; +import { AppExternalLinks } from '@/shared/appLinks/appExternalLinks'; +import useIsMobileSize from '@/shared/lib/hooks/useIsMobileSize'; export interface LoginFormProps { onSuccessLogin?: () => void; @@ -12,6 +14,7 @@ export interface LoginFormProps { const LoginForm = (props: LoginFormProps) => { const { onSuccessLogin = function () {}, extraContent } = props; const { t } = useClientTranslation('auth'); + const { isMobileSize } = useIsMobileSize(); const { register, handleSubmit, onFormSubmit, errors } = useLoginForm({ onSuccessLogin }); const handleFormMouseEvents = (event: React.MouseEvent) => { @@ -19,7 +22,10 @@ const LoginForm = (props: LoginFormProps) => { }; return ( -
+
{ + } actions={ <> {t('log_in')} +

+ {t('register_in_game_prefix')}{' '} + + {t('register_in_game_link')} + +

{extraContent &&
{extraContent}
} } diff --git a/frontend-next-migration/src/preparedPages/AuthPages/ui/AuthLayout/AuthLayout.module.scss b/frontend-next-migration/src/preparedPages/AuthPages/ui/AuthLayout/AuthLayout.module.scss index 5b1aef7b2..0e20133ce 100644 --- a/frontend-next-migration/src/preparedPages/AuthPages/ui/AuthLayout/AuthLayout.module.scss +++ b/frontend-next-migration/src/preparedPages/AuthPages/ui/AuthLayout/AuthLayout.module.scss @@ -2,7 +2,10 @@ display: flex; justify-content: center; align-items: center; + width: 100%; min-height: 80svh; + padding: 0 var(--spacing-md); + box-sizing: border-box; } @media (hover: none) { @@ -10,3 +13,11 @@ min-height: 100lvh; } } + +@media (max-width: breakpoint(sm)) { + .authLayout { + justify-content: flex-start; + align-items: flex-start; + } +} + diff --git a/frontend-next-migration/src/shared/assets/icons/check.svg b/frontend-next-migration/src/shared/assets/icons/check.svg new file mode 100644 index 000000000..609ba8498 --- /dev/null +++ b/frontend-next-migration/src/shared/assets/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend-next-migration/src/shared/assets/images/Alt_zone_logo_teksti.png b/frontend-next-migration/src/shared/assets/images/Alt_zone_logo_teksti.png new file mode 100644 index 000000000..3a636adf0 Binary files /dev/null and b/frontend-next-migration/src/shared/assets/images/Alt_zone_logo_teksti.png differ diff --git a/frontend-next-migration/src/shared/i18n/locales/en/auth.json b/frontend-next-migration/src/shared/i18n/locales/en/auth.json index 45f0cfb51..229b2fe53 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/auth.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/auth.json @@ -2,6 +2,7 @@ "log_in": "Log In", "register": "Register", "username": "Username", + "username_email": "Username/Email", "name": "Name", "backpackCapacity": "Backpack Capacity", "uniqueIdentifier": "Unique Identifier", @@ -38,5 +39,8 @@ "welcome": "Welcome!", "account-created": "Account was created!", "ownProfile": "Own Profile", - "logout": "Logout" + "logout": "Logout", + "register_in_game_prefix": "Don't have an account yet?", + "register_in_game_link": "Register in-game", + "remember_me": "Remember me" } diff --git a/frontend-next-migration/src/shared/i18n/locales/fi/auth.json b/frontend-next-migration/src/shared/i18n/locales/fi/auth.json index c52f2b48d..b7399b82e 100644 --- a/frontend-next-migration/src/shared/i18n/locales/fi/auth.json +++ b/frontend-next-migration/src/shared/i18n/locales/fi/auth.json @@ -1,7 +1,8 @@ { - "log_in": "Kirjaudu", + "log_in": "Kirjaudu sisään", "register": "Rekisteröidy", - "username": "Käyttäjänimi", + "username": "Käyttäjätunnus", + "username_email": "Käyttäjätunnus/Sähköposti", "password": "Salasana", "password_again": "Toista salasana", "send": "Lähetä", @@ -34,5 +35,8 @@ "account-created": "Tili on luotu!", "username-already-taken": "Käyttäjänimi on jo varattu. Valitse jokin toinen käyttäjänimi.", "ownProfile": "Oma Profiili", - "logout": "Kirjaudu Ulos" + "logout": "Kirjaudu Ulos", + "register_in_game_prefix": "Eikö sinulla ole vielä käyttäjätiliä?", + "register_in_game_link": "Rekisteröidy pelissä", + "remember_me": "Muista minut" }