mirror of
https://github.com/theoludwig/theoludwig.git
synced 2025-05-29 22:37:44 +02:00
feat: add divlo.fr
This commit is contained in:
23
pages/404.tsx
Normal file
23
pages/404.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { ErrorPage } from 'components/ErrorPage'
|
||||
import Head from 'components/Head'
|
||||
|
||||
const Error404: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='Divlo - 404' />
|
||||
|
||||
<ErrorPage statusCode={404} message={t('errors:notFound')} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
return { props: {} }
|
||||
}
|
||||
|
||||
export default Error404
|
23
pages/500.tsx
Normal file
23
pages/500.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { ErrorPage } from 'components/ErrorPage'
|
||||
import Head from 'components/Head'
|
||||
|
||||
const Error500: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title='Divlo - 500' />
|
||||
|
||||
<ErrorPage statusCode={500} message={t('errors:serverError')} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
return { props: {} }
|
||||
}
|
||||
|
||||
export default Error500
|
51
pages/_app.tsx
Normal file
51
pages/_app.tsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { AppProps } from 'next/app'
|
||||
import Router from 'next/router'
|
||||
import NProgress from 'nprogress'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
import UniversalCookie from 'universal-cookie'
|
||||
|
||||
import 'normalize.css/normalize.css'
|
||||
import '@fontsource/montserrat/400.css'
|
||||
import '@fontsource/montserrat/500.css'
|
||||
import '@fontsource/montserrat/600.css'
|
||||
import '@fontsource/montserrat/700.css'
|
||||
|
||||
import 'styles/grid.scss'
|
||||
import 'styles/general.scss'
|
||||
import 'styles/nprogress.scss'
|
||||
|
||||
import { Header } from 'components/Header'
|
||||
import { Footer } from 'components/Footer'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const universalCookie = new UniversalCookie()
|
||||
|
||||
/** how long in seconds, until the cookie expires (10 years) */
|
||||
const COOKIE_MAX_AGE = 10 * 365.25 * 24 * 60 * 60
|
||||
|
||||
Router.events.on('routeChangeStart', () => NProgress.start())
|
||||
Router.events.on('routeChangeComplete', () => NProgress.done())
|
||||
Router.events.on('routeChangeError', () => NProgress.done())
|
||||
|
||||
const MyApp = ({ Component, pageProps }: AppProps): JSX.Element => {
|
||||
const { lang } = useTranslation()
|
||||
|
||||
useEffect(() => {
|
||||
universalCookie.set('NEXT_LOCALE', lang, {
|
||||
path: '/',
|
||||
maxAge: COOKIE_MAX_AGE
|
||||
})
|
||||
}, [lang])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<main className='content container'>
|
||||
<Component {...pageProps} />
|
||||
</main>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MyApp
|
69
pages/api/send-email.ts
Normal file
69
pages/api/send-email.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import nodemailer from 'nodemailer'
|
||||
import validator from 'validator'
|
||||
|
||||
const EMAIL_PORT = parseInt(process.env.EMAIL_PORT ?? '465', 10)
|
||||
|
||||
const emailTransporter = nodemailer.createTransport({
|
||||
host: process.env.EMAIL_HOST,
|
||||
port: EMAIL_PORT,
|
||||
secure: EMAIL_PORT === 465,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_PASSWORD
|
||||
},
|
||||
tls: {
|
||||
rejectUnauthorized: false
|
||||
}
|
||||
})
|
||||
|
||||
export default async (
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
): Promise<any> => {
|
||||
if (req.method !== 'POST') {
|
||||
return res.redirect('/404')
|
||||
}
|
||||
|
||||
let { name, email, subject, message } = req.body as {
|
||||
name: string
|
||||
email: string
|
||||
subject: string
|
||||
message: string
|
||||
}
|
||||
|
||||
if (
|
||||
validator.isEmpty(name) ||
|
||||
validator.isEmpty(email) ||
|
||||
validator.isEmpty(subject) ||
|
||||
validator.isEmpty(message)
|
||||
) {
|
||||
return res.status(400).json({ type: 'requiredFields' })
|
||||
}
|
||||
|
||||
if (!validator.isEmail(email)) {
|
||||
return res.status(400).json({ type: 'invalidEmail' })
|
||||
}
|
||||
|
||||
email = validator.normalizeEmail(email) as string
|
||||
message = validator.trim(message)
|
||||
message = validator.escape(message)
|
||||
subject = validator.trim(subject)
|
||||
subject = validator.escape(subject)
|
||||
|
||||
try {
|
||||
await emailTransporter.sendMail({
|
||||
from: '"Divlo" <contact@divlo.fr>',
|
||||
to: email,
|
||||
subject: `Contact - ${subject}`,
|
||||
html: `
|
||||
<b>Name:</b> ${name} <br/>
|
||||
<b>Email:</b> ${email} <br/>
|
||||
<b>Message:</b> ${message}
|
||||
`
|
||||
})
|
||||
return res.status(201).json({ type: 'success' })
|
||||
} catch {
|
||||
return res.status(500).json({ type: 'serverError' })
|
||||
}
|
||||
}
|
61
pages/index.tsx
Normal file
61
pages/index.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { Contact } from 'components/Contact'
|
||||
import { RevealFade } from 'components/design/RevealFade'
|
||||
import { Section } from 'components/design/Section'
|
||||
import Head from 'components/Head'
|
||||
import { Interests } from 'components/Interests'
|
||||
import { Portfolio } from 'components/Portfolio'
|
||||
import { Profile } from 'components/Profile'
|
||||
import { SocialMediaList } from 'components/Profile/SocialMediaList'
|
||||
import { Skills } from 'components/Skills'
|
||||
|
||||
const Home: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head />
|
||||
|
||||
<Section isMain id='about'>
|
||||
<Profile />
|
||||
<SocialMediaList />
|
||||
</Section>
|
||||
|
||||
<RevealFade>
|
||||
<Section id='interests' heading={t('home:interests.title')}>
|
||||
<Interests />
|
||||
</Section>
|
||||
</RevealFade>
|
||||
|
||||
<RevealFade>
|
||||
<Section id='skills' heading={t('home:skills.title')} withoutShadowContainer>
|
||||
<Skills />
|
||||
</Section>
|
||||
</RevealFade>
|
||||
|
||||
<RevealFade>
|
||||
<Section
|
||||
id='portfolio'
|
||||
heading={t('home:portfolio.title')}
|
||||
withoutShadowContainer
|
||||
>
|
||||
<Portfolio />
|
||||
</Section>
|
||||
</RevealFade>
|
||||
|
||||
<RevealFade>
|
||||
<Section id='contact' heading={t('home:contact.title')}>
|
||||
<Contact />
|
||||
</Section>
|
||||
</RevealFade>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
return { props: {} }
|
||||
}
|
||||
|
||||
export default Home
|
31
pages/setup.tsx
Normal file
31
pages/setup.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import { GetStaticProps } from 'next'
|
||||
import useTranslation from 'next-translate/useTranslation'
|
||||
|
||||
import { Section } from 'components/design/Section'
|
||||
import Head from 'components/Head'
|
||||
import { Setup } from 'components/Setup'
|
||||
|
||||
const SetupPage: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head title={t('setup:title')} description={t('setup:description')} />
|
||||
|
||||
<Section
|
||||
id='setup'
|
||||
style={{ marginTop: 60 }}
|
||||
description={t('setup:description')}
|
||||
heading={t('setup:title')}
|
||||
>
|
||||
<Setup />
|
||||
</Section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
return { props: {} }
|
||||
}
|
||||
|
||||
export default SetupPage
|
Reference in New Issue
Block a user