mirror of
https://github.com/hazemKrimi/personal-website.git
synced 2026-05-01 18:00:26 +00:00
Add prettier configuration
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
[*.{ts,js,json,tsx,md,mdx,html}]
|
[*.{ts,js,json,tsx,md,mdx,html}]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = tab
|
indent_style = space
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
quote_type= single
|
quote_type= single
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# next.js
|
||||||
|
/.next/
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
# vercel
|
||||||
|
.vercel
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"singleQuote": true,
|
||||||
|
"jsxSingleQuote": true,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"quoteProps": "as-needed"
|
||||||
|
}
|
||||||
@@ -7,9 +7,15 @@ const Button = ({
|
|||||||
target,
|
target,
|
||||||
onClick,
|
onClick,
|
||||||
children,
|
children,
|
||||||
className
|
className,
|
||||||
}: Props) => (
|
}: Props) => (
|
||||||
<StyledButton href={href} target={target} className={className} onClick={onClick} variant={variant}>
|
<StyledButton
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
className={className}
|
||||||
|
onClick={onClick}
|
||||||
|
variant={variant}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ export const StyledButton = styled(Link)<Props>`
|
|||||||
border: ${({ variant }) =>
|
border: ${({ variant }) =>
|
||||||
variant === 'outline' ? '2px solid var(--text)' : '2px solid transparent'};
|
variant === 'outline' ? '2px solid var(--text)' : '2px solid transparent'};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
text-transform: ${({ variant }) => (variant === 'outline' ? 'uppercase' : 'inherit')};
|
text-transform: ${({ variant }) =>
|
||||||
|
variant === 'outline' ? 'uppercase' : 'inherit'};
|
||||||
padding: ${({ variant }) => (variant === 'outline' ? '.5rem 1rem' : '0rem')};
|
padding: ${({ variant }) => (variant === 'outline' ? '.5rem 1rem' : '0rem')};
|
||||||
text-align: left;
|
text-align: left;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@@ -19,7 +20,8 @@ export const StyledButton = styled(Link)<Props>`
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
padding: ${({ variant }) => (variant === 'outline' ? '.5rem .75rem' : '0rem')};
|
padding: ${({ variant }) =>
|
||||||
|
variant === 'outline' ? '.5rem .75rem' : '0rem'};
|
||||||
}
|
}
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
@@ -30,14 +32,16 @@ export const StyledButton = styled(Link)<Props>`
|
|||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: -1;
|
z-index: -1;
|
||||||
background-color: ${({ variant }) => (variant === 'outline' ? 'var(--text)' : 'inherit')};
|
background-color: ${({ variant }) =>
|
||||||
|
variant === 'outline' ? 'var(--text)' : 'inherit'};
|
||||||
transition: transform 250ms ease-in-out;
|
transition: transform 250ms ease-in-out;
|
||||||
transform: scaleX(0);
|
transform: scaleX(0);
|
||||||
transform-origin: left;
|
transform-origin: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
color: ${({ variant }) => (variant === 'outline' ? 'var(--background)' : 'inherit')};
|
color: ${({ variant }) =>
|
||||||
|
variant === 'outline' ? 'var(--background)' : 'inherit'};
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,22 @@ import Image from 'next/image';
|
|||||||
import { StyledCard } from './styles';
|
import { StyledCard } from './styles';
|
||||||
import { Props } from './types';
|
import { Props } from './types';
|
||||||
|
|
||||||
const Card = ({ title, description, image, tags, href, target, onClick }: Props) => {
|
const Card = ({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
image,
|
||||||
|
tags,
|
||||||
|
href,
|
||||||
|
target,
|
||||||
|
onClick,
|
||||||
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<StyledCard href={href} onClick={onClick} image={image ? Boolean(image) : undefined} target={target}>
|
<StyledCard
|
||||||
|
href={href}
|
||||||
|
onClick={onClick}
|
||||||
|
image={image ? Boolean(image) : undefined}
|
||||||
|
target={target}
|
||||||
|
>
|
||||||
<div className='card-content'>
|
<div className='card-content'>
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
<p>{description}</p>
|
<p>{description}</p>
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ export const StyledCard = styled(Link)<{ image?: boolean }>`
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1440px) {
|
@media (min-width: 1440px) {
|
||||||
grid-template-columns: ${({ image }) => (image ? 'auto 15.625rem' : 'auto')};
|
grid-template-columns: ${({ image }) =>
|
||||||
|
image ? 'auto 15.625rem' : 'auto'};
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ const Footer = () => {
|
|||||||
<div className='contact'>
|
<div className='contact'>
|
||||||
<IconButton
|
<IconButton
|
||||||
alt='GitHub'
|
alt='GitHub'
|
||||||
icon={mode === 'dark' ? '/icons/light-github.svg' : '/icons/dark-github.svg'}
|
icon={
|
||||||
|
mode === 'dark'
|
||||||
|
? '/icons/light-github.svg'
|
||||||
|
: '/icons/dark-github.svg'
|
||||||
|
}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://github.com/hazemKrimi'
|
href='https://github.com/hazemKrimi'
|
||||||
@@ -19,7 +23,11 @@ const Footer = () => {
|
|||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
alt='Twitter'
|
alt='Twitter'
|
||||||
icon={mode === 'dark' ? '/icons/light-twitter.svg' : '/icons/dark-twitter.svg'}
|
icon={
|
||||||
|
mode === 'dark'
|
||||||
|
? '/icons/light-twitter.svg'
|
||||||
|
: '/icons/dark-twitter.svg'
|
||||||
|
}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://twitter.com/HazemKrimi'
|
href='https://twitter.com/HazemKrimi'
|
||||||
@@ -27,7 +35,11 @@ const Footer = () => {
|
|||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
alt='LinkedIn'
|
alt='LinkedIn'
|
||||||
icon={mode === 'dark' ? '/icons/light-linkedin.svg' : '/icons/dark-linkedin.svg'}
|
icon={
|
||||||
|
mode === 'dark'
|
||||||
|
? '/icons/light-linkedin.svg'
|
||||||
|
: '/icons/dark-linkedin.svg'
|
||||||
|
}
|
||||||
width={16}
|
width={16}
|
||||||
height={16}
|
height={16}
|
||||||
href='https://linkedin.com/in/hazemkrimi'
|
href='https://linkedin.com/in/hazemkrimi'
|
||||||
|
|||||||
@@ -10,9 +10,15 @@ const IconButton = ({
|
|||||||
onClick,
|
onClick,
|
||||||
className,
|
className,
|
||||||
width = 24,
|
width = 24,
|
||||||
height = 24
|
height = 24,
|
||||||
}: Props) => href ? (
|
}: Props) =>
|
||||||
<StyledLink href={href} target={target} onClick={onClick} className={className}>
|
href ? (
|
||||||
|
<StyledLink
|
||||||
|
href={href}
|
||||||
|
target={target}
|
||||||
|
onClick={onClick}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
<Image alt={alt} src={icon} width={width} height={height} />
|
<Image alt={alt} src={icon} width={width} height={height} />
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { BigField, SmallField } from "./styles";
|
import { BigField, SmallField } from './styles';
|
||||||
import { Props } from "./types";
|
import { Props } from './types';
|
||||||
|
|
||||||
const Input = ({
|
const Input = ({
|
||||||
type = 'text',
|
type = 'text',
|
||||||
@@ -9,7 +9,7 @@ const Input = ({
|
|||||||
required,
|
required,
|
||||||
placeholder,
|
placeholder,
|
||||||
className,
|
className,
|
||||||
onChange
|
onChange,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return variant === 'small' ? (
|
return variant === 'small' ? (
|
||||||
<SmallField
|
<SmallField
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import styled from "styled-components";
|
import styled from 'styled-components';
|
||||||
|
|
||||||
export const SmallField = styled.input`
|
export const SmallField = styled.input`
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ export interface Props {
|
|||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
onChange?: (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
|
onChange?: (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
|
) => void;
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const MDXButton = ({
|
|||||||
target,
|
target,
|
||||||
children,
|
children,
|
||||||
disabled,
|
disabled,
|
||||||
className
|
className,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { mode } = useContext(ThemeContext);
|
const { mode } = useContext(ThemeContext);
|
||||||
|
|
||||||
@@ -27,7 +27,13 @@ const MDXButton = ({
|
|||||||
{children}
|
{children}
|
||||||
</StyledLink>
|
</StyledLink>
|
||||||
) : (
|
) : (
|
||||||
<StyledButton variant={variant} type={type} mode={mode} disabled={disabled} className={className}>
|
<StyledButton
|
||||||
|
variant={variant}
|
||||||
|
type={type}
|
||||||
|
mode={mode}
|
||||||
|
disabled={disabled}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -6,17 +6,22 @@ export const sharedStyles = css<Props>`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: ${({ variant }) =>
|
display: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
|
['action', 'outline'].includes(variant as string) ? 'block' : 'inline'};
|
||||||
width: ${({ variant }) => (['action', 'outline'].includes(variant as string) ? '100%' : 'auto')};
|
width: ${({ variant }) =>
|
||||||
|
['action', 'outline'].includes(variant as string) ? '100%' : 'auto'};
|
||||||
background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')};
|
background: ${({ variant }) => (variant === 'action' ? '#1573CA' : 'none')};
|
||||||
color: ${({ variant, mode }) =>
|
color: ${({ variant, mode }) =>
|
||||||
variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'};
|
variant === 'action' ? 'white' : mode === 'dark' ? 'white' : 'black'};
|
||||||
border: ${({ variant, mode }) =>
|
border: ${({ variant, mode }) =>
|
||||||
variant === 'outline' ? `2px solid ${mode === 'dark' ? 'white' : 'black'}` : 'none'};
|
variant === 'outline'
|
||||||
|
? `2px solid ${mode === 'dark' ? 'white' : 'black'}`
|
||||||
|
: 'none'};
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: ${({ variant }) =>
|
font-size: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
|
['action', 'outline'].includes(variant as string) ? '1.05rem' : 'inherit'};
|
||||||
text-transform: ${({ variant }) =>
|
text-transform: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? 'uppercase' : 'inherit'};
|
['action', 'outline'].includes(variant as string)
|
||||||
|
? 'uppercase'
|
||||||
|
: 'inherit'};
|
||||||
padding: ${({ variant }) =>
|
padding: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? '.5rem 1rem' : '0rem'};
|
['action', 'outline'].includes(variant as string) ? '.5rem 1rem' : '0rem'};
|
||||||
text-align: ${({ variant }) =>
|
text-align: ${({ variant }) =>
|
||||||
@@ -24,14 +29,18 @@ export const sharedStyles = css<Props>`
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
transition: color 250ms ease-in-out;
|
transition: color 250ms ease-in-out;
|
||||||
|
|
||||||
${({ disabled }) => disabled && `
|
${({ disabled }) =>
|
||||||
|
disabled &&
|
||||||
|
`
|
||||||
background: gray;
|
background: gray;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
`}
|
`}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
padding: ${({ variant }) =>
|
padding: ${({ variant }) =>
|
||||||
['action', 'outline'].includes(variant as string) ? '.5rem .75rem' : '0rem'};
|
['action', 'outline'].includes(variant as string)
|
||||||
|
? '.5rem .75rem'
|
||||||
|
: '0rem'};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ const MobileNav = ({ open, close }: Props) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.addEventListener('mousedown', (event: MouseEvent) => {
|
document.addEventListener('mousedown', (event: MouseEvent) => {
|
||||||
if (ref.current && ref.current.contains(event.target as Node)) {
|
if (ref.current && ref.current.contains(event.target as Node)) {
|
||||||
document.addEventListener('mouseup', event => {
|
document.addEventListener('mouseup', (event) => {
|
||||||
if (ref.current && !ref.current.contains(event.target as Node)) return;
|
if (ref.current && !ref.current.contains(event.target as Node))
|
||||||
|
return;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
document.addEventListener('mouseup', event => {
|
document.addEventListener('mouseup', (event) => {
|
||||||
if (ref.current && !ref.current.contains(event.target as Node)) close();
|
if (ref.current && !ref.current.contains(event.target as Node))
|
||||||
|
close();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -33,7 +35,9 @@ const MobileNav = ({ open, close }: Props) => {
|
|||||||
<div className='close'>
|
<div className='close'>
|
||||||
<IconButton
|
<IconButton
|
||||||
alt='Theme toggler'
|
alt='Theme toggler'
|
||||||
icon={mode === 'dark' ? '/icons/dark-close.svg' : '/icons/light-close.svg'}
|
icon={
|
||||||
|
mode === 'dark' ? '/icons/dark-close.svg' : '/icons/light-close.svg'
|
||||||
|
}
|
||||||
onClick={close}
|
onClick={close}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ const Nav = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
<IconButton
|
<IconButton
|
||||||
alt='Hamburger menu'
|
alt='Hamburger menu'
|
||||||
icon={mode === 'dark' ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'}
|
icon={
|
||||||
|
mode === 'dark' ? '/icons/light-menu.svg' : '/icons/dark-menu.svg'
|
||||||
|
}
|
||||||
onClick={() => setMobileNavOpen(true)}
|
onClick={() => setMobileNavOpen(true)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+4
-4
@@ -1,13 +1,13 @@
|
|||||||
const withMDX = require('@next/mdx')({
|
const withMDX = require('@next/mdx')({
|
||||||
extension: /\.mdx?$/,
|
extension: /\.mdx?$/,
|
||||||
options: {
|
options: {
|
||||||
providerImportSource: '@mdx-js/react'
|
providerImportSource: '@mdx-js/react',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = withMDX({
|
module.exports = withMDX({
|
||||||
pageExtensions: ['ts', 'tsx', 'md', 'mdx'],
|
pageExtensions: ['ts', 'tsx', 'md', 'mdx'],
|
||||||
images: {
|
images: {
|
||||||
domains: ['res.cloudinary.com']
|
domains: ['res.cloudinary.com'],
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
+3
-1
@@ -5,7 +5,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start"
|
"start": "next start",
|
||||||
|
"format": "prettier --write ./**/*.{js,jsx,ts,tsx}"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formspree/react": "^2.4.1",
|
"@formspree/react": "^2.4.1",
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
"@types/styled-components": "^5.1.26",
|
"@types/styled-components": "^5.1.26",
|
||||||
"babel-plugin-styled-components": "^2.0.7",
|
"babel-plugin-styled-components": "^2.0.7",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
|
"prettier": "^2.8.8",
|
||||||
"typescript": "^5.0.2"
|
"typescript": "^5.0.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
+3
-3
@@ -27,7 +27,7 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
NProgress.start();
|
NProgress.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.events.on('routeChangeComplete', url => {
|
router.events.on('routeChangeComplete', (url) => {
|
||||||
NProgress.done();
|
NProgress.done();
|
||||||
pageview(url);
|
pageview(url);
|
||||||
});
|
});
|
||||||
@@ -37,7 +37,7 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
router.events.off('routeChangeComplete', url => {
|
router.events.off('routeChangeComplete', (url) => {
|
||||||
pageview(url);
|
pageview(url);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -49,7 +49,7 @@ const App = ({ Component, pageProps }: AppProps) => {
|
|||||||
id='styles-init'
|
id='styles-init'
|
||||||
strategy='afterInteractive'
|
strategy='afterInteractive'
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: initStyles()
|
__html: initStyles(),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Theme>
|
<Theme>
|
||||||
|
|||||||
+25
-8
@@ -1,4 +1,10 @@
|
|||||||
import Document, { DocumentContext, Html, Head, Main, NextScript } from 'next/document';
|
import Document, {
|
||||||
|
DocumentContext,
|
||||||
|
Html,
|
||||||
|
Head,
|
||||||
|
Main,
|
||||||
|
NextScript,
|
||||||
|
} from 'next/document';
|
||||||
|
|
||||||
import { ServerStyleSheet } from 'styled-components';
|
import { ServerStyleSheet } from 'styled-components';
|
||||||
|
|
||||||
@@ -13,7 +19,8 @@ class Doc extends Document {
|
|||||||
try {
|
try {
|
||||||
ctx.renderPage = () =>
|
ctx.renderPage = () =>
|
||||||
originalRenderPage({
|
originalRenderPage({
|
||||||
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
|
enhanceApp: (App) => (props) =>
|
||||||
|
sheet.collectStyles(<App {...props} />),
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialProps = await Document.getInitialProps(ctx);
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
@@ -24,7 +31,7 @@ class Doc extends Document {
|
|||||||
{initialProps.styles}
|
{initialProps.styles}
|
||||||
{sheet.getStyleElement()}
|
{sheet.getStyleElement()}
|
||||||
</>
|
</>
|
||||||
)
|
),
|
||||||
};
|
};
|
||||||
} finally {
|
} finally {
|
||||||
sheet.seal();
|
sheet.seal();
|
||||||
@@ -35,24 +42,34 @@ class Doc extends Document {
|
|||||||
return (
|
return (
|
||||||
<Html>
|
<Html>
|
||||||
<Head>
|
<Head>
|
||||||
<link rel='shortcut icon' href='/light-favicon.png' id='light-favicon'></link>
|
<link
|
||||||
<link rel='shortcut icon' href='/dark-favicon.png' id='dark-favicon'></link>
|
rel='shortcut icon'
|
||||||
|
href='/light-favicon.png'
|
||||||
|
id='light-favicon'
|
||||||
|
></link>
|
||||||
|
<link
|
||||||
|
rel='shortcut icon'
|
||||||
|
href='/dark-favicon.png'
|
||||||
|
id='dark-favicon'
|
||||||
|
></link>
|
||||||
<link rel='preconnect' href='https://fonts.gstatic.com' />
|
<link rel='preconnect' href='https://fonts.gstatic.com' />
|
||||||
<link
|
<link
|
||||||
href='https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&display=swap'
|
href='https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&display=swap'
|
||||||
rel='stylesheet'
|
rel='stylesheet'
|
||||||
/>
|
/>
|
||||||
<script src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_KEY}`} />
|
<script
|
||||||
|
src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_KEY}`}
|
||||||
|
/>
|
||||||
<script
|
<script
|
||||||
id='analytics-init'
|
id='analytics-init'
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: initAnalytics()
|
__html: initAnalytics(),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<script
|
<script
|
||||||
id='styles-init'
|
id='styles-init'
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: initStyles()
|
__html: initStyles(),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Head>
|
</Head>
|
||||||
|
|||||||
+12
-8
@@ -58,7 +58,10 @@ const BlogPost = ({ source, frontMatter, text }: Props) => {
|
|||||||
with the latest trends in software engineering.`
|
with the latest trends in software engineering.`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<meta property='og:title' content={`${frontMatter?.title} | Hazem Krimi`} />
|
<meta
|
||||||
|
property='og:title'
|
||||||
|
content={`${frontMatter?.title} | Hazem Krimi`}
|
||||||
|
/>
|
||||||
<meta
|
<meta
|
||||||
name='keywords'
|
name='keywords'
|
||||||
content={
|
content={
|
||||||
@@ -78,7 +81,8 @@ const BlogPost = ({ source, frontMatter, text }: Props) => {
|
|||||||
<h1>{frontMatter?.title}</h1>
|
<h1>{frontMatter?.title}</h1>
|
||||||
<p>{frontMatter?.description}</p>
|
<p>{frontMatter?.description}</p>
|
||||||
<p>
|
<p>
|
||||||
By <b>{frontMatter?.author}</b> on <b>{frontMatter?.date}</b> ({stats.text})
|
By <b>{frontMatter?.author}</b> on <b>{frontMatter?.date}</b> (
|
||||||
|
{stats.text})
|
||||||
</p>
|
</p>
|
||||||
{frontMatter?.tags ? (
|
{frontMatter?.tags ? (
|
||||||
<div className='tags-wrapper'>
|
<div className='tags-wrapper'>
|
||||||
@@ -110,7 +114,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||||||
const paths = getBlogPostsSlugs();
|
const paths = getBlogPostsSlugs();
|
||||||
return {
|
return {
|
||||||
paths,
|
paths,
|
||||||
fallback: false
|
fallback: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
||||||
@@ -120,20 +124,20 @@ export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
source: undefined,
|
source: undefined,
|
||||||
frontMatter: undefined
|
frontMatter: undefined,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data, content } = matter(blogPostContent);
|
const { data, content } = matter(blogPostContent);
|
||||||
const mdxSource = await serialize(content, {
|
const mdxSource = await serialize(content, {
|
||||||
scope: data
|
scope: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
source: mdxSource,
|
source: mdxSource,
|
||||||
frontMatter: data,
|
frontMatter: data,
|
||||||
text: content
|
text: content,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ export const getStaticProps = async () => {
|
|||||||
const blogPosts = getBlogPosts();
|
const blogPosts = getBlogPosts();
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
blogPosts
|
blogPosts,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+24
-6
@@ -6,15 +6,21 @@ import Input from '../components/Input';
|
|||||||
import MDXButton from '../components/MDXButton';
|
import MDXButton from '../components/MDXButton';
|
||||||
|
|
||||||
const About = () => {
|
const About = () => {
|
||||||
const [form, setForm] = useState<{ name: string; email: string; message: string }>({
|
const [form, setForm] = useState<{
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
message: string;
|
||||||
|
}>({
|
||||||
name: '',
|
name: '',
|
||||||
email: '',
|
email: '',
|
||||||
message: ''
|
message: '',
|
||||||
});
|
});
|
||||||
const [state, Submit] = useForm(`${process.env.NEXT_PUBLIC_FORMSPREE_KEY}`);
|
const [state, Submit] = useForm(`${process.env.NEXT_PUBLIC_FORMSPREE_KEY}`);
|
||||||
const [submitted, setSubmitted] = useState<boolean>(false);
|
const [submitted, setSubmitted] = useState<boolean>(false);
|
||||||
|
|
||||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
const handleChange = (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||||
|
) => {
|
||||||
setForm({ ...form, [event.target.name]: event.target.value });
|
setForm({ ...form, [event.target.name]: event.target.value });
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +65,10 @@ const About = () => {
|
|||||||
<Wrapper>
|
<Wrapper>
|
||||||
<div className='content'>
|
<div className='content'>
|
||||||
<div>
|
<div>
|
||||||
<h1>Contact Me {submitted && <span className='success'>Message sent ✔️</span>}</h1>
|
<h1>
|
||||||
|
Contact Me{' '}
|
||||||
|
{submitted && <span className='success'>Message sent ✔️</span>}
|
||||||
|
</h1>
|
||||||
<form className='contact' onSubmit={handleSubmit}>
|
<form className='contact' onSubmit={handleSubmit}>
|
||||||
<Input
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
@@ -70,7 +79,12 @@ const About = () => {
|
|||||||
value={form.name}
|
value={form.name}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
<ValidationError className='error' prefix='Name' field='name' errors={state.errors} />
|
<ValidationError
|
||||||
|
className='error'
|
||||||
|
prefix='Name'
|
||||||
|
field='name'
|
||||||
|
errors={state.errors}
|
||||||
|
/>
|
||||||
<Input
|
<Input
|
||||||
type='text'
|
type='text'
|
||||||
placeholder='Email'
|
placeholder='Email'
|
||||||
@@ -101,7 +115,11 @@ const About = () => {
|
|||||||
field='message'
|
field='message'
|
||||||
errors={state.errors}
|
errors={state.errors}
|
||||||
/>
|
/>
|
||||||
<MDXButton type='submit' variant='action' disabled={state.submitting || submitted}>
|
<MDXButton
|
||||||
|
type='submit'
|
||||||
|
variant='action'
|
||||||
|
disabled={state.submitting || submitted}
|
||||||
|
>
|
||||||
Submit
|
Submit
|
||||||
</MDXButton>
|
</MDXButton>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
+6
-3
@@ -60,7 +60,10 @@ const Index = ({ blogPosts, projects }: Props) => {
|
|||||||
<h1>About</h1>
|
<h1>About</h1>
|
||||||
<div className='about'>
|
<div className='about'>
|
||||||
<p>
|
<p>
|
||||||
Experienced Full Stack developer with a focus on building user-friendly web and cross-platform mobile applications using cutting-edge technologies. Passionate about ongoing learning and staying up-to-date with the latest trends in software engineering.
|
Experienced Full Stack developer with a focus on building
|
||||||
|
user-friendly web and cross-platform mobile applications using
|
||||||
|
cutting-edge technologies. Passionate about ongoing learning and
|
||||||
|
staying up-to-date with the latest trends in software engineering.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{projects.length !== 0 && (
|
{projects.length !== 0 && (
|
||||||
@@ -107,7 +110,7 @@ export const getStaticProps: GetStaticProps = async () => {
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
blogPosts,
|
blogPosts,
|
||||||
projects
|
projects,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -56,7 +56,10 @@ const Project = ({ source, frontMatter }: Props) => {
|
|||||||
with the latest trends in software engineering.`
|
with the latest trends in software engineering.`
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<meta property='og:title' content={`${frontMatter?.title} | Hazem Krimi`} />
|
<meta
|
||||||
|
property='og:title'
|
||||||
|
content={`${frontMatter?.title} | Hazem Krimi`}
|
||||||
|
/>
|
||||||
<meta
|
<meta
|
||||||
name='keywords'
|
name='keywords'
|
||||||
content={
|
content={
|
||||||
@@ -94,10 +97,18 @@ const Project = ({ source, frontMatter }: Props) => {
|
|||||||
<MDXRemote {...source} />
|
<MDXRemote {...source} />
|
||||||
<h1>Showcase</h1>
|
<h1>Showcase</h1>
|
||||||
<div className='showcase-buttons'>
|
<div className='showcase-buttons'>
|
||||||
<MDXButton variant='action' link={frontMatter?.demo} target='_blank'>
|
<MDXButton
|
||||||
|
variant='action'
|
||||||
|
link={frontMatter?.demo}
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
Demo
|
Demo
|
||||||
</MDXButton>
|
</MDXButton>
|
||||||
<MDXButton variant='outline' link={frontMatter?.code} target='_blank'>
|
<MDXButton
|
||||||
|
variant='outline'
|
||||||
|
link={frontMatter?.code}
|
||||||
|
target='_blank'
|
||||||
|
>
|
||||||
Source Code
|
Source Code
|
||||||
</MDXButton>
|
</MDXButton>
|
||||||
</div>
|
</div>
|
||||||
@@ -114,7 +125,7 @@ export const getStaticPaths: GetStaticPaths = async () => {
|
|||||||
const paths = getPorjectsSlugs();
|
const paths = getPorjectsSlugs();
|
||||||
return {
|
return {
|
||||||
paths,
|
paths,
|
||||||
fallback: false
|
fallback: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
||||||
@@ -124,19 +135,19 @@ export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
|||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
source: undefined,
|
source: undefined,
|
||||||
frontMatter: undefined
|
frontMatter: undefined,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const { data, content } = matter(projectContent);
|
const { data, content } = matter(projectContent);
|
||||||
const mdxSource = await serialize(content, {
|
const mdxSource = await serialize(content, {
|
||||||
scope: data
|
scope: data,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
source: mdxSource,
|
source: mdxSource,
|
||||||
frontMatter: data
|
frontMatter: data,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export const getStaticProps = async () => {
|
|||||||
const projects = getProjects();
|
const projects = getProjects();
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
projects
|
projects,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
+4
-4
@@ -5,14 +5,14 @@ const Shared = ({ children }: { children: React.ReactNode }) => {
|
|||||||
colors: {
|
colors: {
|
||||||
dark: {
|
dark: {
|
||||||
background: '#262626',
|
background: '#262626',
|
||||||
text: 'white'
|
text: 'white',
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
background: '#F9F9F9',
|
background: '#F9F9F9',
|
||||||
text: 'black'
|
text: 'black',
|
||||||
|
},
|
||||||
|
blue: '#1573CA',
|
||||||
},
|
},
|
||||||
blue: '#1573CA'
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
|
||||||
|
|||||||
+10
-4
@@ -1,9 +1,11 @@
|
|||||||
import { useReducer, useEffect, createContext } from 'react';
|
import { useReducer, useEffect, createContext } from 'react';
|
||||||
|
|
||||||
export const ThemeContext = createContext<{ mode: string; toggle: () => void }>({
|
export const ThemeContext = createContext<{ mode: string; toggle: () => void }>(
|
||||||
|
{
|
||||||
mode: 'light',
|
mode: 'light',
|
||||||
toggle: () => {}
|
toggle: () => {},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
const reducer = (state: string, action: { type: string }) => {
|
const reducer = (state: string, action: { type: string }) => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@@ -51,7 +53,11 @@ const Theme = ({ children }: { children: React.ReactNode }) => {
|
|||||||
dispatch({ type: initialThemeMode === 'dark' ? 'DARK' : 'LIGHT' });
|
dispatch({ type: initialThemeMode === 'dark' ? 'DARK' : 'LIGHT' });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <ThemeContext.Provider value={{ mode, toggle }}>{children}</ThemeContext.Provider>;
|
return (
|
||||||
|
<ThemeContext.Provider value={{ mode, toggle }}>
|
||||||
|
{children}
|
||||||
|
</ThemeContext.Provider>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Theme;
|
export default Theme;
|
||||||
|
|||||||
+10
-7
@@ -10,7 +10,7 @@ export const getBlogPosts = () => {
|
|||||||
|
|
||||||
if (!fileNames) return [];
|
if (!fileNames) return [];
|
||||||
|
|
||||||
const allBlogPostsData = fileNames.map(filename => {
|
const allBlogPostsData = fileNames.map((filename) => {
|
||||||
const slug = filename.replace('.mdx', '');
|
const slug = filename.replace('.mdx', '');
|
||||||
|
|
||||||
const fullPath = path.join(blogPostsDirectory, filename);
|
const fullPath = path.join(blogPostsDirectory, filename);
|
||||||
@@ -19,15 +19,18 @@ export const getBlogPosts = () => {
|
|||||||
|
|
||||||
const options = { month: 'long', day: 'numeric', year: 'numeric' };
|
const options = { month: 'long', day: 'numeric', year: 'numeric' };
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const formattedDate = new Date(data.date).toLocaleDateString('en-IN', options);
|
const formattedDate = new Date(data.date).toLocaleDateString(
|
||||||
|
'en-IN',
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
const frontmatter = {
|
const frontmatter = {
|
||||||
...data,
|
...data,
|
||||||
date: formattedDate
|
date: formattedDate,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
slug,
|
slug,
|
||||||
...frontmatter
|
...frontmatter,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,11 +52,11 @@ export const getBlogPostsSlugs = () => {
|
|||||||
|
|
||||||
if (!fileNames) return [];
|
if (!fileNames) return [];
|
||||||
|
|
||||||
return fileNames.map(filename => {
|
return fileNames.map((filename) => {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
slug: filename.replace('.mdx', '')
|
slug: filename.replace('.mdx', ''),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
+4
-3
@@ -1,9 +1,10 @@
|
|||||||
export const GOOGLE_ANALYTICS_KEY = process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_KEY;
|
export const GOOGLE_ANALYTICS_KEY =
|
||||||
|
process.env.NEXT_PUBLIC_GOOGLE_ANALYTICS_KEY;
|
||||||
|
|
||||||
export const pageview = (url: any) => {
|
export const pageview = (url: any) => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
window.gtag('config', GOOGLE_ANALYTICS_KEY, {
|
window.gtag('config', GOOGLE_ANALYTICS_KEY, {
|
||||||
page_path: url
|
page_path: url,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ export const event = ({ action, category, label, value }: any) => {
|
|||||||
window.gtag('event', action, {
|
window.gtag('event', action, {
|
||||||
event_category: category,
|
event_category: category,
|
||||||
event_label: label,
|
event_label: label,
|
||||||
value: value
|
value: value,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+10
-7
@@ -10,7 +10,7 @@ export const getProjects = () => {
|
|||||||
|
|
||||||
if (!fileNames) return [];
|
if (!fileNames) return [];
|
||||||
|
|
||||||
const allProjectsData = fileNames.map(filename => {
|
const allProjectsData = fileNames.map((filename) => {
|
||||||
const slug = filename.replace('.mdx', '');
|
const slug = filename.replace('.mdx', '');
|
||||||
|
|
||||||
const fullPath = path.join(projects, filename);
|
const fullPath = path.join(projects, filename);
|
||||||
@@ -19,15 +19,18 @@ export const getProjects = () => {
|
|||||||
|
|
||||||
const options = { month: 'long', day: 'numeric', year: 'numeric' };
|
const options = { month: 'long', day: 'numeric', year: 'numeric' };
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const formattedDate = new Date(data.date).toLocaleDateString('en-IN', options);
|
const formattedDate = new Date(data.date).toLocaleDateString(
|
||||||
|
'en-IN',
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
const frontmatter = {
|
const frontmatter = {
|
||||||
...data,
|
...data,
|
||||||
date: formattedDate
|
date: formattedDate,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
slug,
|
slug,
|
||||||
...frontmatter
|
...frontmatter,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,11 +52,11 @@ export const getPorjectsSlugs = () => {
|
|||||||
|
|
||||||
if (!fileNames) return [];
|
if (!fileNames) return [];
|
||||||
|
|
||||||
return fileNames.map(filename => {
|
return fileNames.map((filename) => {
|
||||||
return {
|
return {
|
||||||
params: {
|
params: {
|
||||||
slug: filename.replace('.mdx', '')
|
slug: filename.replace('.mdx', ''),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -1338,6 +1338,11 @@ postcss@8.4.14:
|
|||||||
picocolors "^1.0.0"
|
picocolors "^1.0.0"
|
||||||
source-map-js "^1.0.2"
|
source-map-js "^1.0.2"
|
||||||
|
|
||||||
|
prettier@^2.8.8:
|
||||||
|
version "2.8.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
|
||||||
|
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
|
||||||
|
|
||||||
prism-react-renderer@^1.3.5:
|
prism-react-renderer@^1.3.5:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085"
|
resolved "https://registry.yarnpkg.com/prism-react-renderer/-/prism-react-renderer-1.3.5.tgz#786bb69aa6f73c32ba1ee813fbe17a0115435085"
|
||||||
|
|||||||
Reference in New Issue
Block a user