feat(client/theme): add theme switcher to landing page
This commit is contained in:
parent
1facd2ad11
commit
8f5632c5ad
|
@ -1,70 +1,51 @@
|
||||||
import { Language } from '@mui/icons-material';
|
import { Language } from '@mui/icons-material';
|
||||||
import { IconButton, Popover } from '@mui/material';
|
import { IconButton, Menu, MenuItem } from '@mui/material';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { MouseEvent, useState } from 'react';
|
import { MouseEvent, useState } from 'react';
|
||||||
|
|
||||||
import { languages } from '@/config/languages';
|
import { languages } from '@/config/languages';
|
||||||
import { useAppDispatch } from '@/store/hooks';
|
import { TRANSLATE_URL } from '@/constants/index';
|
||||||
import { setResumeState } from '@/store/resume/resumeSlice';
|
|
||||||
|
|
||||||
import styles from './LanguageSwitcher.module.scss';
|
|
||||||
|
|
||||||
const LanguageSwitcher = () => {
|
const LanguageSwitcher = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
|
|
||||||
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
|
||||||
|
|
||||||
const handleClick = (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget);
|
const handleClick = (event: MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget);
|
||||||
|
|
||||||
const handleClose = () => setAnchorEl(null);
|
const handleClose = () => setAnchorEl(null);
|
||||||
|
|
||||||
const handleChangeLanguage = (locale: string) => {
|
const handleChange = (locale: string) => {
|
||||||
const { pathname, asPath, query } = router;
|
const { pathname, asPath, query } = router;
|
||||||
|
|
||||||
|
handleClose();
|
||||||
|
|
||||||
document.cookie = `NEXT_LOCALE=${locale}; path=/; expires=2147483647`;
|
document.cookie = `NEXT_LOCALE=${locale}; path=/; expires=2147483647`;
|
||||||
dispatch(setResumeState({ path: 'metadata.locale', value: locale }));
|
|
||||||
|
|
||||||
router.push({ pathname, query }, asPath, { locale });
|
router.push({ pathname, query }, asPath, { locale });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleAddLanguage = () => window.open(TRANSLATE_URL, '_blank');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<IconButton onClick={handleClick}>
|
<IconButton onClick={handleClick}>
|
||||||
<Language />
|
<Language />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
||||||
<Popover
|
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)} onClose={handleClose}>
|
||||||
anchorEl={anchorEl}
|
{languages.map(({ code, name, localName }) => (
|
||||||
open={Boolean(anchorEl)}
|
<MenuItem key={code} onClick={() => handleChange(code)}>
|
||||||
onClose={handleClose}
|
{name} {localName && `(${localName})`}
|
||||||
anchorOrigin={{
|
</MenuItem>
|
||||||
vertical: 'top',
|
))}
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
transformOrigin={{
|
|
||||||
vertical: 'bottom',
|
|
||||||
horizontal: 'right',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className={styles.popover}>
|
|
||||||
<div className={styles.container}>
|
|
||||||
{languages.map(({ code, name, localName }) => (
|
|
||||||
<p key={code} className={styles.language} onClick={() => handleChangeLanguage(code)}>
|
|
||||||
{name} {localName && `(${localName})`}
|
|
||||||
</p>
|
|
||||||
))}
|
|
||||||
|
|
||||||
<a href="https://translate.rxresu.me" target="_blank" rel="noreferrer" className={styles.language}>
|
<MenuItem>
|
||||||
{t('common.footer.language.missing')}
|
<span className="font-bold" onClick={handleAddLanguage}>
|
||||||
</a>
|
Add your language
|
||||||
</div>
|
</span>
|
||||||
</div>
|
</MenuItem>
|
||||||
</Popover>
|
</Menu>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,6 +10,8 @@ export const FILENAME_TIMESTAMP = 'DDMMYYYYHHmmss';
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
export const DONATION_URL = 'https://paypal.me/RajaRajanA';
|
||||||
|
export const TRANSLATE_URL = 'https://translate.rxresu.me/';
|
||||||
|
export const DIGITALOCEAN_URL = 'https://pillai.xyz/digitalocean';
|
||||||
export const GITHUB_URL = 'https://github.com/AmruthPillai/Reactive-Resume';
|
export const GITHUB_URL = 'https://github.com/AmruthPillai/Reactive-Resume';
|
||||||
export const PRODUCT_HUNT_URL = 'https://www.producthunt.com/posts/reactive-resume-v3';
|
export const PRODUCT_HUNT_URL = 'https://www.producthunt.com/posts/reactive-resume-v3';
|
||||||
export const GITHUB_ISSUES_URL = 'https://github.com/AmruthPillai/Reactive-Resume/issues/new/choose';
|
export const GITHUB_ISSUES_URL = 'https://github.com/AmruthPillai/Reactive-Resume/issues/new/choose';
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Link as LinkIcon } from '@mui/icons-material';
|
import { DarkMode, LightMode, Link as LinkIcon } from '@mui/icons-material';
|
||||||
import { Masonry } from '@mui/lab';
|
import { Masonry } from '@mui/lab';
|
||||||
import { Button } from '@mui/material';
|
import { Button, IconButton } from '@mui/material';
|
||||||
import type { GetStaticProps, NextPage } from 'next';
|
import type { GetStaticProps, NextPage } from 'next';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
@ -15,11 +15,12 @@ import NoSSR from '@/components/shared/NoSSR';
|
||||||
import { screenshots } from '@/config/screenshots';
|
import { screenshots } from '@/config/screenshots';
|
||||||
import testimonials from '@/data/testimonials';
|
import testimonials from '@/data/testimonials';
|
||||||
import { logout } from '@/store/auth/authSlice';
|
import { logout } from '@/store/auth/authSlice';
|
||||||
|
import { setTheme } from '@/store/build/buildSlice';
|
||||||
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
import { useAppDispatch, useAppSelector } from '@/store/hooks';
|
||||||
import { setModalState } from '@/store/modal/modalSlice';
|
import { setModalState } from '@/store/modal/modalSlice';
|
||||||
import styles from '@/styles/pages/Home.module.scss';
|
import styles from '@/styles/pages/Home.module.scss';
|
||||||
|
|
||||||
import { DONATION_URL, GITHUB_URL } from '../constants';
|
import { DIGITALOCEAN_URL, DONATION_URL, GITHUB_URL } from '../constants';
|
||||||
|
|
||||||
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
export const getStaticProps: GetStaticProps = async ({ locale = 'en' }) => {
|
||||||
return {
|
return {
|
||||||
|
@ -34,12 +35,15 @@ const Home: NextPage = () => {
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
|
const theme = useAppSelector((state) => state.build.theme);
|
||||||
const isLoggedIn = useAppSelector((state) => state.auth.isLoggedIn);
|
const isLoggedIn = useAppSelector((state) => state.auth.isLoggedIn);
|
||||||
|
|
||||||
const handleLogin = () => dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
const handleLogin = () => dispatch(setModalState({ modal: 'auth.login', state: { open: true } }));
|
||||||
|
|
||||||
const handleRegister = () => dispatch(setModalState({ modal: 'auth.register', state: { open: true } }));
|
const handleRegister = () => dispatch(setModalState({ modal: 'auth.register', state: { open: true } }));
|
||||||
|
|
||||||
|
const handleToggle = () => dispatch(setTheme({ theme: theme === 'light' ? 'dark' : 'light' }));
|
||||||
|
|
||||||
const handleLogout = () => dispatch(logout());
|
const handleLogout = () => dispatch(logout());
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -175,7 +179,7 @@ const Home: NextPage = () => {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className={styles.section}>
|
<section className={styles.section}>
|
||||||
<a href="https://pillai.xyz/digitalocean" target="_blank" rel="noreferrer">
|
<a href={DIGITALOCEAN_URL} target="_blank" rel="noreferrer">
|
||||||
<Image src="/images/sponsors/digitalocean.svg" alt="Powered By DigitalOcean" width={200} height={40} />
|
<Image src="/images/sponsors/digitalocean.svg" alt="Powered By DigitalOcean" width={200} height={40} />
|
||||||
</a>
|
</a>
|
||||||
</section>
|
</section>
|
||||||
|
@ -187,7 +191,11 @@ const Home: NextPage = () => {
|
||||||
<div>v{process.env.appVersion}</div>
|
<div>v{process.env.appVersion}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<LanguageSwitcher />
|
<div className={styles.actions}>
|
||||||
|
<IconButton onClick={handleToggle}>{theme === 'dark' ? <DarkMode /> : <LightMode />}</IconButton>
|
||||||
|
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,9 +7,6 @@
|
||||||
},
|
},
|
||||||
"footer": {
|
"footer": {
|
||||||
"credit": "A passion project by <1>Amruth Pillai</1>",
|
"credit": "A passion project by <1>Amruth Pillai</1>",
|
||||||
"language": {
|
|
||||||
"missing": "Missing your language?"
|
|
||||||
},
|
|
||||||
"license": "By the community, for the community."
|
"license": "By the community, for the community."
|
||||||
},
|
},
|
||||||
"markdown": {
|
"markdown": {
|
||||||
|
|
|
@ -2,7 +2,11 @@
|
||||||
@apply m-6 grid gap-8 text-center md:m-8 md:text-left;
|
@apply m-6 grid gap-8 text-center md:m-8 md:text-left;
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
@apply flex items-end justify-between;
|
@apply flex flex-col gap-8 items-center sm:items-end justify-between sm:flex-row sm:gap-0;
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
@apply flex gap-2;
|
||||||
|
}
|
||||||
|
|
||||||
.version > div {
|
.version > div {
|
||||||
@apply text-xs font-medium opacity-50 mt-3;
|
@apply text-xs font-medium opacity-50 mt-3;
|
||||||
|
|
Loading…
Reference in New Issue