diff --git a/frontend-next-migration/src/shared/assets/images/PRG_Logo.png b/frontend-next-migration/src/shared/assets/images/PRG_Logo.png new file mode 100644 index 000000000..a706bad45 Binary files /dev/null and b/frontend-next-migration/src/shared/assets/images/PRG_Logo.png differ diff --git a/frontend-next-migration/src/shared/i18n/locales/en/footer.json b/frontend-next-migration/src/shared/i18n/locales/en/footer.json index 02327e170..55bacc667 100644 --- a/frontend-next-migration/src/shared/i18n/locales/en/footer.json +++ b/frontend-next-migration/src/shared/i18n/locales/en/footer.json @@ -3,5 +3,14 @@ "FooterPrivacy": "Privacy", "FooterCookies": "Cookies", "FooterConsent": "Consent", - "FooterEthics": "Ethical guidelines" + "FooterEthics": "Ethical guidelines", + "WorkWithUs": "Apply to work with us", + "WhatIsPRG": "What is PRG", + "AltZoneHistory": "ALT Zone history", + "DevelopersDesigners": "Developers and designers", + "TermsAndPrivacy": "Terms and privacy policy", + "Contact": "Contact", + "ContactInfo": "Contact information", + "EmailLabel": "Email addresses", + "Information": "Information" } diff --git a/frontend-next-migration/src/shared/i18n/locales/fi/footer.json b/frontend-next-migration/src/shared/i18n/locales/fi/footer.json index e6b25fa94..b31a29549 100644 --- a/frontend-next-migration/src/shared/i18n/locales/fi/footer.json +++ b/frontend-next-migration/src/shared/i18n/locales/fi/footer.json @@ -3,5 +3,14 @@ "FooterPrivacy": "Yksityisyys", "FooterCookies": "Evästeet", "FooterConsent": "Suostumus", - "FooterEthics": "Eettiset ohjeet" + "FooterEthics": "Eettiset ohjeet", + "WorkWithUs": "Hae meille töihin", + "WhatIsPRG": "Mikä on PRG", + "AltZoneHistory": "ALT Zonen historia", + "DevelopersDesigners": "Kehittäjät ja suunnittelijat", + "TermsAndPrivacy": "Käyttöehdot ja tietosuoja", + "Contact": "Ota yhteyttä", + "ContactInfo": "Yhteystiedot", + "EmailLabel": "Sähköpostit", + "Information": "Tietoa" } diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.module.scss b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.module.scss new file mode 100644 index 000000000..3cd9f6b73 --- /dev/null +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.module.scss @@ -0,0 +1,92 @@ +/* Contact section of the footer, containing title, email addresses and "Work with us" link. */ + +.FooterContact { + display: flex; + flex-direction: column; + width: 310; + height: 175; + opacity: 1; + gap: 15px; +} + +.Title { + width: 152; + height: 31; + opacity: 1; + font-family: DM Sans; + font-weight: 700; + font-style: Bold; + font-size: 24px; + line-height: 100%; + letter-spacing: 0%; + color: #ffa100; +} + +.EmailLabel { + color: #faf9f6; + font-family: DM Sans; + font-weight: 400; + font-style: Regular; + font-size: 20px; + line-height: 100%; + letter-spacing: 0%; + text-decoration: underline; + text-decoration-style: solid; + text-decoration-thickness: 0%; + width: 203; + height: 26; + opacity: 1; +} + +.EmailList { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.25rem; + opacity: 1; +} + +.Email, +.WorkWithUsLink { + color: #faf9f6; + text-decoration: none; + font: var(--font-dm-s); + transition: + color 0.2s ease, + opacity 0.2s ease; + + &:hover { + color: var(--footer-accent, var(--primary-color)); + opacity: 0.8; + } + + &:focus { + outline: 2px solid var(--footer-accent, var(--primary-color)); + outline-offset: 2px; + } +} + +.WorkWithUsLink { + display: inline-flex; + align-items: center; + gap: 0.25rem; + font-family: DM Sans; + font-weight: 400; + font-style: Regular; + font-size: 20px; + line-height: 100%; + letter-spacing: 0%; +} + +.Email { + text-decoration: underline; + text-underline-offset: 2px; +} + +.ExternalIcon { + width: 0.65rem; + height: 0.65rem; + color: var(--footer-accent, var(--primary-color)); +} diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.tsx b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.tsx new file mode 100644 index 000000000..b64cc5c83 --- /dev/null +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/FooterContact.tsx @@ -0,0 +1,61 @@ +import { memo } from 'react'; +import { classNames } from '@/shared/lib/classNames/classNames'; +import { AppLink } from '@/shared/ui/AppLink/AppLink'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faExternalLink } from '@fortawesome/free-solid-svg-icons'; +import cls from './FooterContact.module.scss'; + +/** Props for the FooterContact component, which displays contact information in the footer. */ +interface FooterContactProps { + className?: string; + title: string; + emailLabel?: string; + emails: string[]; + workWithUsLabel?: string; +} + +const DUUNITORI_JOBS_URL = 'https://duunitori.fi/tyopaikat?haku=Psyche%27s%20Royale%20Gaming'; + +/** Component for displaying contact information in the footer. + * @param props - Props for the FooterContact component, including title, optional email label, list of email addresses, and an optional "Work with us" label. + * @returns A React component that renders the footer contact section with the provided information. + * @remarks This component is memoized for performance optimization, preventing unnecessary re-renders when props do not change. + */ +export const FooterContact = memo((props: FooterContactProps) => { + const { className = '', title, emailLabel, emails, workWithUsLabel } = props; + + /* Render the footer contact section with a title, optional email label, list of email addresses, and a "Work with us" link. */ + return ( +
+

{title}

+ {emailLabel &&

{emailLabel}

} + + {workWithUsLabel && ( + + {workWithUsLabel} + + + )} +
+ ); +}); + +FooterContact.displayName = 'FooterContact'; diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/index.ts b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/index.ts new file mode 100644 index 000000000..2af983bec --- /dev/null +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterContact/index.ts @@ -0,0 +1 @@ +export { FooterContact } from './FooterContact'; diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.module.scss b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.module.scss index 09b0b8d91..a28fb8444 100644 --- a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.module.scss +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.module.scss @@ -1,57 +1,135 @@ .Footer { - font: var(--font-dm-xl); - background: var(--background); + --footer-bg: #1e3544; + --footer-bar-bg: #172f3d; + --footer-accent: #ffa100; + --footer-text: #faf9f6; + --footer-muted: #faf9f6; + + font: var(--font-dm-s); + background: var(--footer-bg); + color: var(--footer-text); display: flex; - flex-direction: row; - justify-content: space-evenly; - align-items: center; - padding: 5px 25px 5px 25px; + flex-direction: column; position: relative; z-index: 20; + opacity: 1; } -.title { - padding-top: 1rem; - color: var(--primary-color); - text-align: center; - font: var(--font-sw-xl); +.SocialBar { + display: flex; + justify-content: center; + align-items: center; + width: 1920px; + height: 100px; + padding-top: 20px; + padding-bottom: 20px; + gap: 22px; + background: var(--footer-bar-bg); + opacity: 1; } .socialSection { - padding-top: 1rem; + display: flex; + justify-content: center; + width: 1920px; + height: 40px; + opacity: 1; +} - /* Footer controls layout now (was previously inside SocialSection component) */ +.socialSection > * { + width: 40px; + height: 40px; + opacity: 1; +} + +.socialSection img { + width: 40px; + height: 40px; + opacity: 1; +} + +.Inner { display: grid; + grid-template-columns: 132px minmax(0, 372px); + align-items: start; + justify-content: center; + gap: 70px; + width: 100%; + min-height: 164px; + padding: 15px 24px 0; + opacity: 1; +} + +.BrandColumn { + display: flex; align-items: center; - justify-items: center; + justify-content: center; + opacity: 1; +} - margin: 0 auto; - max-width: 600px; +.Logo { + width: 275; + height: 270; + opacity: 1; +} - grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); +.ContentGrid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 100px; + padding-top: 24px; + padding-right: 64px; + padding-left: 64px; + opacity: 1; } -.rights { - padding: 1rem 0; - text-align: center; +.contactSection, +.infoSection { + display: flex; + flex-direction: column; + opacity: 1; } -.FooterContainer { - align-self: flex-start; - justify-items: center; +.BottomSection { + display: flex; + justify-content: center; + width: 100%; + padding-bottom: 12px; + opacity: 1; } -.ImageContainer { - justify-items: flex-start; +.Copyright { + gap: 100px; + margin: 2rem 0 0; + color: #ffffff; + font: var(--font-dm-s); + size: 16px; + text-align: center; + width: 400; + line-height: 100%; + letter-spacing: 0%; + opacity: 1; } -.FeedbackContainer { - display: flex; - justify-content: flex-end; +.CopySymbol { + margin-right: 0.25rem; + opacity: 1; } @media (max-width: 1023px) { - .socialSection { - grid-template-columns: repeat(auto-fit, minmax(85px, 1fr)); + .Inner { + grid-template-columns: 120px minmax(0, 1fr); + gap: 48px; + padding-inline: 32px; + } + + .Logo { + width: 275; + height: 270; + opacity: 1; } -} \ No newline at end of file + + .ContentGrid { + gap: 40px; + } +} diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.test.tsx b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.test.tsx index b26c34537..15137fe92 100644 --- a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.test.tsx +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.test.tsx @@ -2,9 +2,16 @@ import { render, screen } from '@testing-library/react'; import { SocialIconLink, Texts } from '@/shared/types/types'; import FooterDesktop from './FooterDesktop'; -jest.mock('@/shared/ui/v2/Feedback'); +jest.mock('next/image', () => ({ + __esModule: true, + default: ({ priority, alt, ...props }: any) => ( + {alt} + ), +})); -// Mock data for social links and texts const mockSocialLinks: SocialIconLink[] = [ { link: 'https://facebook.com', icon: '/icons/facebook.svg', name: 'Facebook' }, { link: 'https://twitter.com', icon: '/icons/twitter.svg', name: 'Twitter' }, @@ -19,47 +26,64 @@ const mockTexts: Texts = { companyName: 'My Company', }; -describe('FooterDesktop', () => { - it('renders without crashing and displays the title', () => { - const title = 'Footer Title'; +const mockFooterLinks = { + workWithUsLabel: 'Apply to us', + whatIsPrgLabel: 'What is PRG', + altZoneHistoryLabel: 'ALT Zone history', + developersDesignersLabel: 'Developers and designers', + termsAndPrivacyLabel: 'Terms and privacy policy', +}; +describe('FooterDesktop', () => { + it('renders the logo, contact section, and info links', () => { render( , ); - // Check if the title is displayed correctly - expect(screen.getByText(title)).toBeInTheDocument(); + expect(screen.getByAltText('PRG Logo')).toBeInTheDocument(); + expect(screen.getByText('Contact information')).toBeInTheDocument(); + expect(screen.getByText('Email addresses')).toBeInTheDocument(); + expect(screen.getByText('hello@example.com')).toBeInTheDocument(); + expect(screen.getByText('Information')).toBeInTheDocument(); + expect(screen.getByText('Apply to us')).toBeInTheDocument(); }); it('renders the SocialSection with correct social links', () => { render( , ); - // Check if the social links are rendered mockSocialLinks.forEach((link) => { - expect(screen.getByAltText(link.name)).toBeInTheDocument(); // Check for images using alt text + expect(screen.getByAltText(link.name)).toBeInTheDocument(); }); }); - it('renders the Rights component with correct texts', () => { + it('renders copyright with the current company', () => { render( , ); - // Check if the Rights component displays the correct year and company name expect( screen.getByText(`${mockTexts.currentYear} ${mockTexts.companyName}`), ).toBeInTheDocument(); diff --git a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.tsx b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.tsx index 2edb38146..24a5bc23e 100644 --- a/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.tsx +++ b/frontend-next-migration/src/shared/ui/v2/Footer/ui/FooterDesktop/FooterDesktop.tsx @@ -1,46 +1,89 @@ import { memo } from 'react'; -import { Container } from '@/shared/ui/Container'; +import Image from 'next/image'; import { SocialIconLink, Texts } from '@/shared/types/types'; -import { Rights } from '@/widgets/Footer/ui/Rights/Rights'; import { SocialSection } from '@/shared/SocialSection/SocialSection'; -import { Title } from '@/widgets/Footer/ui/Title/Title'; +import { FooterContact } from '@/shared/ui/v2/Footer/ui/FooterContact'; +import { FooterInfo } from '@/shared/ui/v2/Footer/ui/FooterInfo'; import cls from './FooterDesktop.module.scss'; import { classNames } from '@/shared/lib/classNames/classNames'; -import { FeedbackCard } from '@/shared/ui/v2/Feedback'; -import { RandomCharacter } from '@/shared/ui/v2/Footer/ui/FooterDesktop/RandomCharacter'; +import prgLogo from '@/shared/assets/images/PRG_Logo.png'; interface Props { - title: string; socialIconLinks: SocialIconLink[]; texts: Texts; + contactTitle: string; + contactEmailLabel?: string; + contactEmails: string[]; + infoTitle: string; + infoLinks?: { + workWithUsLabel: string; + whatIsPrgLabel: string; + altZoneHistoryLabel: string; + developersDesignersLabel: string; + termsAndPrivacyLabel: string; + }; className?: string; } const FooterDesktopComponent = memo((props: Props) => { - const { title, socialIconLinks, texts, className = '' } = props; + const { + socialIconLinks, + texts, + contactTitle, + contactEmailLabel, + contactEmails, + infoTitle, + infoLinks, + className = '', + } = props; return (