Moved from next-localization to next-i18next

Removed unneeded pages
dependabot/npm_and_yarn/typescript-eslint/parser-4.33.0
Guus van Meerveld 3 years ago
parent 07e8106807
commit 04139e543d

@ -1,52 +1,9 @@
{
"root": true,
"env": {
"node": true,
"es6": true
},
"parserOptions": {
"ecmaVersion": 8
},
"plugins": ["prettier"],
"ignorePatterns": ["node_modules/*", ".next/*", ".out/*", "!.prettierrc.js"],
"extends": ["eslint:recommended"],
"overrides": [
{
"files": ["**/*.ts", "**/*.tsx"],
"parser": "@typescript-eslint/parser",
"settings": {
"react": {
"version": "detect"
}
},
"env": {
"browser": true,
"node": true,
"es6": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended"
],
"rules": {
"prettier/prettier": "error",
"react/prop-types": "off",
"react/react-in-jsx-scope": "off",
"jsx-a11y/anchor-is-valid": "off",
"jsx-a11y/no-autofocus": "off",
"@typescript-eslint/no-unused-vars": ["error"],
"@typescript-eslint/explicit-function-return-type": [
"warn",
{
"allowExpressions": true,
"allowConciseArrowFunctionExpressionsStartingWithVoid": true,
"allowTypedFunctionExpressions": true
}
]
}
}
]
}
"plugins": [
"prettier"
],
"extends": "next/core-web-vitals",
"rules": {
"prettier/prettier": "error"
}
}

4
next-env.d.ts vendored

@ -1,2 +1,6 @@
/// <reference types="next" />
/// <reference types="next/types/global" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

@ -0,0 +1,6 @@
module.exports = {
i18n: {
locales: ['en', 'nl'],
defaultLocale: 'en',
},
};

@ -1,6 +1,5 @@
const { i18n } = require('./next-i18next.config');
module.exports = {
i18n: {
locales: ['en', 'nl'],
defaultLocale: 'en',
},
}
i18n,
};

@ -7,34 +7,25 @@
"build": "next build",
"start": "next start",
"prettify": "prettier --write .",
"lint": "eslint src"
"lint": "next lint"
},
"dependencies": {
"luxon": "^2.0.1",
"milligram": "^1.4.1",
"next": "^10.2.3",
"next-localization": "^0.11.0",
"next-themes": "^0.0.14",
"next": "^11.1.2",
"next-i18next": "^8.8.0",
"next-themes": "^0.0.15",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-google-recaptcha": "^2.1.0",
"react-icons": "^4.2.0",
"sass": "^1.34.0",
"swr": "^0.5.6"
"sass": "^1.42.1"
},
"devDependencies": {
"@types/luxon": "^1.27.1",
"@types/node": "^15.6.1",
"@types/react": "^17.0.6",
"@types/react-google-recaptcha": "^2.1.0",
"@typescript-eslint/eslint-plugin": "^4.25.0",
"@typescript-eslint/parser": "^4.25.0",
"eslint": "^7.27.0",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-config-next": "11.1.2",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-react-hooks": "^4.2.0",
"prettier": "^2.3.0",
"typescript": "^4.2.4"
"typescript": "^4.4.3"
}
}

@ -0,0 +1,5 @@
{
"title": "Page not found",
"description": "This page either doesn't exist or has been deleted",
"back": "Go back"
}

@ -0,0 +1,9 @@
{
"title": "Home",
"description": "A simple portfolio website to display my projects.",
"subtitle": "Hi, I am a full stack web developer",
"projects": {
"title": "Projects",
"button": "Check out my projects"
}
}

@ -0,0 +1,6 @@
{
"title": "Portfolio",
"projects": "Projects",
"contact": "Contact",
"github": "Source code"
}

@ -0,0 +1,5 @@
{
"title": "Pagina niet gevonden",
"description": "Deze pagina bestaat niet of is verwijderd",
"back": "Ga terug"
}

@ -0,0 +1,9 @@
{
"title": "Thuis",
"description": "Een simpele portfolio website om mijn projecten te bekijken.",
"subtitle": "Hoi, Ik ben een full stack web developer",
"projects": {
"title": "Projecten",
"button": "Bekijk mijn projecten"
}
}

@ -0,0 +1,6 @@
{
"title": "Portfolio",
"projects": "Projecten",
"contact": "Contact",
"github": "Bron code"
}

@ -1,2 +1,2 @@
User-agent: *
Disallow: /shifts
Disallow:

@ -29,6 +29,8 @@
.select {
width: 7.5rem;
margin-bottom: 0;
background-color: $bg-primary;
}
.items {

@ -1,30 +1,28 @@
import { useI18n } from 'next-localization';
import { useRouter } from 'next/router';
import { useTheme } from 'next-themes';
import Link from 'next/link';
import React, { ChangeEvent, FC } from 'react';
import { BiMoon } from 'react-icons/bi';
import { ImSun } from 'react-icons/im';
import { FC } from 'react';
import styles from './Navbar.module.scss';
import { useTranslation } from 'next-i18next';
const LanguageSelector: FC = () => {
const router = useRouter();
const path = router.pathname;
const locale = router.locale;
const updateLocale = (e: ChangeEvent<HTMLSelectElement>) => {
router.push('', '', { locale: e.target.value });
};
return (
<select className={styles.select} defaultValue={router.locale}>
<select className={styles.select} onChange={updateLocale} defaultValue={locale}>
{router.locales.map((locale) => (
<option
key={locale}
onClick={() => {
router.push(path, path, { locale });
}}
value={locale}
>
<option key={locale} value={locale}>
{locale.toUpperCase()}
</option>
))}
@ -33,7 +31,7 @@ const LanguageSelector: FC = () => {
};
const Navbar: FC = () => {
const { t } = useI18n();
const { t } = useTranslation('nav');
const { theme, setTheme } = useTheme();
const switchTheme = (): void => setTheme(theme == 'dark' ? 'light' : 'dark');
@ -42,16 +40,16 @@ const Navbar: FC = () => {
<nav className={styles.bar}>
<div className="container">
<div className={styles.content}>
<div className={styles.header}>Portfolio</div>
<div className={styles.header}>{t('title')}</div>
<div className={styles.items}>
<Link href="/#projects">
<a>{t('nav.projects')}</a>
<a>{t('projects')}</a>
</Link>
<Link href="/contact">
<a>{t('nav.contact')}</a>
<a>{t('contact')}</a>
</Link>
<Link href="https://github.com/guusvanmeerveld/portfolio">
<a>{t('nav.github')}</a>
<a>{t('github')}</a>
</Link>
<BiMoon onClick={switchTheme} className={styles.moon} />

@ -4,14 +4,19 @@ import { FC } from 'react';
import styles from './PageBuilder.module.scss';
const PageBuilder: FC<{ header: string; subtitle: string }> = ({ header, subtitle, children }) => (
const PageBuilder: FC<{ header: string; subtitle: string; button: string }> = ({
header,
subtitle,
button,
children,
}) => (
<div className={styles.body}>
<div>
{children}
<div className={styles.header}>{header}</div>
<div className={styles.subtitle}>{subtitle}</div>
<Link href="/">
<a className={styles.link + ' button'}>Go back</a>
<a className={styles.link + ' button'}>{button}</a>
</Link>
</div>
</div>

@ -1,42 +0,0 @@
@import '../styles/colors.scss';
.lds {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
div {
box-sizing: border-box;
display: block;
position: absolute;
width: 64px;
height: 64px;
margin: 8px;
border: 8px solid $text;
border-radius: 50%;
animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
border-color: $text transparent transparent transparent;
&:nth-child(1) {
animation-delay: -0.45s;
}
&:nth-child(2) {
animation-delay: -0.3s;
}
&:nth-child(3) {
animation-delay: -0.15s;
}
}
}
@keyframes lds-ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

@ -1,14 +0,0 @@
import { FC } from 'react';
import styles from './Spinner.module.scss';
const Spinner: FC = () => (
<div className={styles.lds}>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
);
export default Spinner;

@ -1,9 +0,0 @@
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const { default: lngDict = {} } = await import(`./locales/${locale}.json`);
return {
props: { lngDict },
};
};

@ -1,17 +0,0 @@
{
"pages": {
"home": {
"title": "Home",
"description": "A simple portfolio website to display my projects.",
"subtitle": "Hi, I am a full stack web developer",
"projects": {
"button": "Check out my projects"
}
}
},
"nav": {
"projects": "Projects",
"contact": "Contact",
"github": "Source code"
}
}

@ -1,17 +0,0 @@
{
"pages": {
"home": {
"title": "Thuis",
"description": "Een simpele portfolio website om mijn projecten te bekijken.",
"subtitle": "Hoi, Ik ben een full stack web developer",
"projects": {
"button": "Bekijk mijn projecten"
}
}
},
"nav": {
"projects": "Projecten",
"contact": "Contact",
"github": "Bron code"
}
}

@ -1,20 +1,29 @@
import { FC } from 'react';
import { GetStaticProps, NextPage } from 'next';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Page from '@components/Page';
import Layout from '@components/Layout';
import PageBuilder from '@components/PageBuilder';
export { getStaticProps } from '../locales';
const NotFound: NextPage = () => {
const { t } = useTranslation('404');
const NotFound: FC = () => (
<Page title="Page not found" description="This page either doesn't exist or has been deleted">
<Layout>
<PageBuilder
header="Not found"
subtitle="This page either doesn't exist or has been deleted"
/>
</Layout>
</Page>
);
return (
<Page title={t('title')} description={t('description')}>
<Layout>
<PageBuilder button={t('back')} header={t('title')} subtitle={t('description')} />
</Layout>
</Page>
);
};
export const getStaticProps: GetStaticProps = async ({ locale }) => {
return {
props: {
...(await serverSideTranslations(locale, ['404', 'nav'])),
},
};
};
export default NotFound;

@ -1,28 +1,21 @@
import { ThemeProvider } from 'next-themes';
import { I18nProvider } from 'next-localization';
import { useRouter } from 'next/router';
import type { AppProps } from 'next/app';
import { appWithTranslation } from 'next-i18next';
import 'milligram';
import '@styles/raleway.css';
import '@styles/roboto.css';
import '@styles/montserrat.css';
import '@styles/globals.scss';
const App = ({ Component, pageProps }: AppProps): JSX.Element => {
const router = useRouter();
const { lngDict, ...rest } = pageProps;
return (
<I18nProvider lngDict={lngDict} locale={router.locale}>
<ThemeProvider>
<Component {...rest} />
</ThemeProvider>
</I18nProvider>
<ThemeProvider>
<Component {...pageProps} />
</ThemeProvider>
);
};
export default App;
export default appWithTranslation(App);

@ -1,27 +0,0 @@
import Document, {
Html,
Head,
Main,
NextScript,
DocumentInitialProps,
DocumentContext,
} from 'next/document';
export default class AppDocument extends Document {
static async getInitialProps(ctx: DocumentContext): Promise<DocumentInitialProps> {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render(): JSX.Element {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}

@ -1,21 +0,0 @@
.header {
font-size: 4rem;
text-align: center;
margin-bottom: 3rem;
padding-top: 4rem;
}
.textarea {
resize: none;
height: 10rem;
}
.submit {
margin-top: 1.5rem;
}
.contact {
height: 100vh;
padding-top: 10rem;
}

@ -1,75 +0,0 @@
import { NextPage } from 'next';
import { useTheme } from 'next-themes';
import ReCAPTCHA from 'react-google-recaptcha';
import Page from '@components/Page';
import Layout from '@components/Layout';
export { getStaticProps } from '../locales';
import styles from './contact.module.scss';
const formURL = 'https://forms.guusvanmeerveld.dev/portfolio';
const Contact: NextPage = () => {
const { theme } = useTheme();
return (
<Page description="Contact me" title="Contact">
<Layout>
<div className={styles.contact}>
<div className="container">
<div className={styles.header} id="contact">
Contact
</div>
<form
encType="application/x-www-form-urlencoded"
action={formURL}
className="content"
method="POST"
name="contact"
>
<label htmlFor="email">Email</label>
<input
className={styles.input}
name="email"
type="email"
required
placeholder="Your email address"
id="email"
/>
<label htmlFor="type">Message type</label>
<select className={styles.input} name="type[]" id="type">
<option value="bug">Bug</option>
<option value="question">Question</option>
<option value="suggestion">Suggestion</option>
<option value="other">Other</option>
</select>
<label htmlFor="message">Message</label>
<textarea
className={styles.textarea}
required
name="message"
placeholder="Your message"
></textarea>
<ReCAPTCHA
theme={theme as 'light' | 'dark'}
sitekey={process.env.NEXT_PUBLIC_CAPTCHA_KEY}
/>
<button className={styles.submit + ' button'} type="submit">
Send
</button>
</form>
</div>
</div>
</Layout>
</Page>
);
};
export default Contact;

@ -1,22 +1,23 @@
import { GetStaticProps, NextPage } from 'next';
import { useI18n } from 'next-localization';
import Image from 'next/image';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import Layout from '@components/Layout';
import Page from '@components/Page';
import Project from '@components/Project';
import projects from '@config/projects.json';
import styles from './home.module.scss';
import styles from './Index.module.scss';
const Home: NextPage = () => {
const { t } = useI18n();
const { t } = useTranslation('home');
return (
<Page description={t('pages.home.description')} title={t('pages.home.title')}>
<Page description={t('description')} title={t('title')}>
<Layout>
<div className={styles.body}>
<div className={styles.content + ' container'}>
@ -25,10 +26,10 @@ const Home: NextPage = () => {
</div>
<h1>Guus van Meerveld</h1>
<h4 className={styles.subtitle}>{t('pages.home.subtitle')}</h4>
<h4 className={styles.subtitle}>{t('subtitle')}</h4>
<a href="#projects" className="button">
{t('pages.home.projects.button')}
{t('projects.button')}
</a>
</div>
</div>
@ -36,7 +37,7 @@ const Home: NextPage = () => {
<div className={styles.projects}>
<div className="container">
<h1 className={styles.projectsHeader} id="projects">
{t('nav.projects')}
{t('projects.title')}
</h1>
{projects.map((project, i) => {
@ -51,10 +52,10 @@ const Home: NextPage = () => {
};
export const getStaticProps: GetStaticProps = async ({ locale }) => {
const { default: lngDict = {} } = await import(`../locales/${locale}.json`);
return {
props: { lngDict },
props: {
...(await serverSideTranslations(locale, ['home', 'nav'])),
},
};
};

@ -1,40 +0,0 @@
.body {
padding-top: 6rem;
}
.content {
height: calc(100vh - 6rem);
padding-top: 5rem;
text-align: center;
display: flex;
justify-content: center;
}
.table {
background-color: var(--secondary);
padding: 3rem;
font-family: 'Roboto';
border-radius: 7.5px;
border: var(--borders) 1px solid;
margin: 0;
}
.tableHeader {
font-family: 'Raleway';
text-align: left;
}
.weekDays {
text-align: center;
}
.error {
button {
margin-right: 2rem;
}
}

@ -1,116 +0,0 @@
import { NextPage } from 'next';
import Link from 'next/link';
import { Info, DateTime } from 'luxon';
import useSWR from 'swr';
import { FC } from 'react';
import { stringToTime } from '@utils/date';
import Spinner from '@components/Spinner';
import Layout from '@components/Layout';
import Page from '@components/Page';
import Shift from '@models/shifts';
export { getStaticProps } from '../locales';
import styles from './shifts.module.scss';
interface APIResponse {
updated: Date;
parsed: Shift[];
}
const Error: FC = () => (
<>
<div className={styles.error}>
<h1>Error retrieving shift information</h1>
<button className="button">Retry</button>
<Link href="/">
<a>
<button className="button">Go back</button>
</a>
</Link>
</div>
</>
);
const Table: FC = () => {
const { data, error } = useSWR<APIResponse>('https://api.g-vm.nl/appie');
if (error) {
return <Error />;
}
if (!data) {
return (
<>
<Spinner />
<h1>Retrieving shift information...</h1>
</>
);
}
if (data) {
const weeks = new Map<number, Record<string, Shift>>();
data.parsed.forEach((shift) => {
const weekNumber = DateTime.fromISO(shift.start).weekNumber;
const dayName = DateTime.fromISO(shift.start).weekdayLong;
const current = weeks.get(weekNumber);
weeks.set(weekNumber, { ...current, [dayName]: shift });
});
return (
<table className={styles.table}>
<caption>
<h1 className={styles.tableHeader}>Work timesheet</h1>
</caption>
<thead>
<tr className={styles.weekDays}>
<th></th>
{Info.weekdays().map((day) => (
<th key={day}>{day}</th>
))}
</tr>
</thead>
<tbody>
{Array.from(weeks).map(([weekNumber, week], i) => (
<tr key={i}>
<th>{weekNumber}</th>
{Info.weekdays().map((weekDay) => {
week[weekDay] ? (
<td key={week[weekDay].start}>
{stringToTime(week[weekDay].start)} - {stringToTime(week[weekDay].end)}
</td>
) : (
<td></td>
);
})}
</tr>
))}
</tbody>
</table>
);
}
};
const Shifts: NextPage = () => (
<Page description="Check mijn AH rooster" title="Shifts">
<Layout>
<div className={styles.body}>
<div className={styles.content}>
<div>
<Table />
</div>
</div>
</div>
</Layout>
</Page>
);
export default Shifts;

@ -1,3 +0,0 @@
.icon {
font-size: 10rem;
}

@ -1,25 +0,0 @@
import { NextPage } from 'next';
import Page from '@components/Page';
import Layout from '@components/Layout';
import PageBuilder from '@components/PageBuilder';
export { getStaticProps } from '../locales';
import styles from './thanks.module.scss';
const Thanks: NextPage = () => (
<Page title="Thanks!" description="Thanks for submitting your contact form!">
<Layout>
<PageBuilder header="Thank you!" subtitle="Your submission is greatly appreciated!">
<div className={styles.icon}>
<span role="img" aria-label="heart emoji">
</span>
</div>
</PageBuilder>
</Layout>
</Page>
);
export default Thanks;

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -3,5 +3,5 @@ body {
background-color: $bg-primary;
color: $text;
font-family: 'Raleway';
font-family: 'Montserrat';
}

@ -1,19 +0,0 @@
input,
textarea,
select {
&:focus {
border-color: $primary !important;
}
color: $text;
border-color: $borders !important;
font-family: 'Roboto';
font-size: 1.5rem;
}
select {
&:focus {
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 8" width="30"><path fill="%23388e3c" d="M0,0l6,8l6-8"/></svg>');
}
}

@ -22,6 +22,5 @@
@import './global/body.scss';
@import './global/link.scss';
@import './global/input.scss';
@import './global/table.scss';
@import './global/button.scss';

@ -1,9 +1,10 @@
/* latin */
@font-face {
font-family: 'Raleway';
font-family: 'Montserrat';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(./fonts/raleway.woff2) format('woff2');
src: url('./fonts/montserrat.woff2') format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@ -1,8 +0,0 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
src: url(./fonts/roboto.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F,
U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@ -1,3 +0,0 @@
import { DateTime } from 'luxon';
export const stringToTime = (date: string): string => DateTime.fromISO(date).toFormat('T');

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save