mirror of
https://github.com/hazemKrimi/personal-website.git
synced 2026-05-02 02:10:27 +00:00
Add prettier configuration
This commit is contained in:
+32
-32
@@ -4,45 +4,45 @@ import IconButton from '../components/IconButton';
|
||||
import { Wrapper } from '../styles/404';
|
||||
|
||||
const NotFound = () => {
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>404 Not Found | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<h1>404: This page could not be found</h1>
|
||||
<div className='back' onClick={() => router.push('/')}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Go Home</span>
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>404 Not Found | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<h1>404: This page could not be found</h1>
|
||||
<div className='back' onClick={() => router.push('/')}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Go Home</span>
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
|
||||
+39
-39
@@ -20,50 +20,50 @@ import { initStyles } from '../utils/styles';
|
||||
NProgress.configure({ showSpinner: false });
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
router.events.on('routeChangeStart', () => {
|
||||
NProgress.start();
|
||||
});
|
||||
useEffect(() => {
|
||||
router.events.on('routeChangeStart', () => {
|
||||
NProgress.start();
|
||||
});
|
||||
|
||||
router.events.on('routeChangeComplete', url => {
|
||||
NProgress.done();
|
||||
pageview(url);
|
||||
});
|
||||
router.events.on('routeChangeComplete', (url) => {
|
||||
NProgress.done();
|
||||
pageview(url);
|
||||
});
|
||||
|
||||
router.events.on('routeChangeError', () => {
|
||||
NProgress.done();
|
||||
});
|
||||
router.events.on('routeChangeError', () => {
|
||||
NProgress.done();
|
||||
});
|
||||
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', url => {
|
||||
pageview(url);
|
||||
});
|
||||
};
|
||||
}, [router.events]);
|
||||
return () => {
|
||||
router.events.off('routeChangeComplete', (url) => {
|
||||
pageview(url);
|
||||
});
|
||||
};
|
||||
}, [router.events]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
id='styles-init'
|
||||
strategy='afterInteractive'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initStyles()
|
||||
}}
|
||||
/>
|
||||
<Theme>
|
||||
<SharedStyles>
|
||||
<GlobalStyles />
|
||||
<Container>
|
||||
<Nav />
|
||||
<Component {...pageProps} />
|
||||
<Footer />
|
||||
</Container>
|
||||
</SharedStyles>
|
||||
</Theme>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
id='styles-init'
|
||||
strategy='afterInteractive'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initStyles(),
|
||||
}}
|
||||
/>
|
||||
<Theme>
|
||||
<SharedStyles>
|
||||
<GlobalStyles />
|
||||
<Container>
|
||||
<Nav />
|
||||
<Component {...pageProps} />
|
||||
<Footer />
|
||||
</Container>
|
||||
</SharedStyles>
|
||||
</Theme>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
||||
+72
-55
@@ -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';
|
||||
|
||||
@@ -6,63 +12,74 @@ import { GOOGLE_ANALYTICS_KEY, initAnalytics } from '../utils/gtag';
|
||||
import { initStyles } from '../utils/styles';
|
||||
|
||||
class Doc extends Document {
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
static async getInitialProps(ctx: DocumentContext) {
|
||||
const sheet = new ServerStyleSheet();
|
||||
const originalRenderPage = ctx.renderPage;
|
||||
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
|
||||
});
|
||||
try {
|
||||
ctx.renderPage = () =>
|
||||
originalRenderPage({
|
||||
enhanceApp: (App) => (props) =>
|
||||
sheet.collectStyles(<App {...props} />),
|
||||
});
|
||||
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
)
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
const initialProps = await Document.getInitialProps(ctx);
|
||||
return {
|
||||
...initialProps,
|
||||
styles: (
|
||||
<>
|
||||
{initialProps.styles}
|
||||
{sheet.getStyleElement()}
|
||||
</>
|
||||
),
|
||||
};
|
||||
} finally {
|
||||
sheet.seal();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<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
|
||||
href='https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&display=swap'
|
||||
rel='stylesheet'
|
||||
/>
|
||||
<script src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_KEY}`} />
|
||||
<script
|
||||
id='analytics-init'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initAnalytics()
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
id='styles-init'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initStyles()
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<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
|
||||
href='https://fonts.googleapis.com/css2?family=Source+Code+Pro:wght@400;700&display=swap'
|
||||
rel='stylesheet'
|
||||
/>
|
||||
<script
|
||||
src={`https://www.googletagmanager.com/gtag/js?id=${GOOGLE_ANALYTICS_KEY}`}
|
||||
/>
|
||||
<script
|
||||
id='analytics-init'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initAnalytics(),
|
||||
}}
|
||||
/>
|
||||
<script
|
||||
id='styles-init'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: initStyles(),
|
||||
}}
|
||||
/>
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Doc;
|
||||
|
||||
+105
-101
@@ -14,126 +14,130 @@ import readingTime from 'reading-time';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface Props {
|
||||
source: MDXRemoteSerializeResult;
|
||||
frontMatter: any;
|
||||
text: string;
|
||||
source: MDXRemoteSerializeResult;
|
||||
frontMatter: any;
|
||||
text: string;
|
||||
}
|
||||
|
||||
const BlogPost = ({ source, frontMatter, text }: Props) => {
|
||||
const router = useRouter();
|
||||
const stats = readingTime(text);
|
||||
const htmlOverrides = { code: CodeBlock };
|
||||
const mdxComponents = {};
|
||||
const router = useRouter();
|
||||
const stats = readingTime(text);
|
||||
const htmlOverrides = { code: CodeBlock };
|
||||
const mdxComponents = {};
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an 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.`
|
||||
}
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
}
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an 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.`
|
||||
}
|
||||
/>
|
||||
<meta property='og:title' content={`${frontMatter?.title} | Hazem Krimi`} />
|
||||
<meta
|
||||
name='keywords'
|
||||
content={
|
||||
frontMatter?.tags
|
||||
? frontMatter.tags.join(' ')
|
||||
: `Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js`
|
||||
}
|
||||
/>
|
||||
<title>{`${frontMatter?.title} | Hazem Krimi`}</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='meta'>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>{frontMatter?.title}</h1>
|
||||
<p>{frontMatter?.description}</p>
|
||||
<p>
|
||||
By <b>{frontMatter?.author}</b> on <b>{frontMatter?.date}</b> ({stats.text})
|
||||
</p>
|
||||
{frontMatter?.tags ? (
|
||||
<div className='tags-wrapper'>
|
||||
{frontMatter.tags.map((tag: string, index: number) => (
|
||||
<span key={index}>#{tag} </span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{frontMatter?.image ? (
|
||||
<div className='image'>
|
||||
<Image alt={frontMatter?.title} src={frontMatter?.image} fill />
|
||||
</div>
|
||||
) : null}
|
||||
<hr />
|
||||
</div>
|
||||
<MDXProvider components={{ ...htmlOverrides, ...mdxComponents }}>
|
||||
<div className='content'>
|
||||
<MDXRemote {...source} />
|
||||
</div>
|
||||
</MDXProvider>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
/>
|
||||
<meta
|
||||
property='og:title'
|
||||
content={`${frontMatter?.title} | Hazem Krimi`}
|
||||
/>
|
||||
<meta
|
||||
name='keywords'
|
||||
content={
|
||||
frontMatter?.tags
|
||||
? frontMatter.tags.join(' ')
|
||||
: `Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js`
|
||||
}
|
||||
/>
|
||||
<title>{`${frontMatter?.title} | Hazem Krimi`}</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='meta'>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>{frontMatter?.title}</h1>
|
||||
<p>{frontMatter?.description}</p>
|
||||
<p>
|
||||
By <b>{frontMatter?.author}</b> on <b>{frontMatter?.date}</b> (
|
||||
{stats.text})
|
||||
</p>
|
||||
{frontMatter?.tags ? (
|
||||
<div className='tags-wrapper'>
|
||||
{frontMatter.tags.map((tag: string, index: number) => (
|
||||
<span key={index}>#{tag} </span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{frontMatter?.image ? (
|
||||
<div className='image'>
|
||||
<Image alt={frontMatter?.title} src={frontMatter?.image} fill />
|
||||
</div>
|
||||
) : null}
|
||||
<hr />
|
||||
</div>
|
||||
<MDXProvider components={{ ...htmlOverrides, ...mdxComponents }}>
|
||||
<div className='content'>
|
||||
<MDXRemote {...source} />
|
||||
</div>
|
||||
</MDXProvider>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BlogPost;
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const paths = getBlogPostsSlugs();
|
||||
return {
|
||||
paths,
|
||||
fallback: false
|
||||
};
|
||||
const paths = getBlogPostsSlugs();
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
||||
const blogPostContent = await getBlogPostdata(params.slug);
|
||||
const blogPostContent = await getBlogPostdata(params.slug);
|
||||
|
||||
if (!blogPostContent)
|
||||
return {
|
||||
props: {
|
||||
source: undefined,
|
||||
frontMatter: undefined
|
||||
}
|
||||
};
|
||||
if (!blogPostContent)
|
||||
return {
|
||||
props: {
|
||||
source: undefined,
|
||||
frontMatter: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const { data, content } = matter(blogPostContent);
|
||||
const mdxSource = await serialize(content, {
|
||||
scope: data
|
||||
});
|
||||
const { data, content } = matter(blogPostContent);
|
||||
const mdxSource = await serialize(content, {
|
||||
scope: data,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
frontMatter: data,
|
||||
text: content
|
||||
}
|
||||
};
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
frontMatter: data,
|
||||
text: content,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+56
-56
@@ -6,69 +6,69 @@ import IconButton from '../../components/IconButton';
|
||||
import Head from 'next/head';
|
||||
|
||||
interface Props {
|
||||
blogPosts: {
|
||||
title: string;
|
||||
author: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
blogPosts: {
|
||||
title: string;
|
||||
author: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const Index = ({ blogPosts }: Props) => {
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is a Full Stack JavaScript Developer and a Software Engineering Enthusiast'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech/blog' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is a Full Stack JavaScript Developer and a Software Engineering Enthusiast'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News'
|
||||
/>
|
||||
<title>Blog | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>Blog</h1>
|
||||
<div className='articles-wrapper'>
|
||||
{blogPosts.length !== 0 ? (
|
||||
blogPosts.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/blog/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is a Full Stack JavaScript Developer and a Software Engineering Enthusiast'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech/blog' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is a Full Stack JavaScript Developer and a Software Engineering Enthusiast'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News'
|
||||
/>
|
||||
<title>Blog | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>Blog</h1>
|
||||
<div className='articles-wrapper'>
|
||||
{blogPosts.length !== 0 ? (
|
||||
blogPosts.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/blog/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const blogPosts = getBlogPosts();
|
||||
return {
|
||||
props: {
|
||||
blogPosts
|
||||
}
|
||||
};
|
||||
const blogPosts = getBlogPosts();
|
||||
return {
|
||||
props: {
|
||||
blogPosts,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+113
-95
@@ -6,110 +6,128 @@ import Input from '../components/Input';
|
||||
import MDXButton from '../components/MDXButton';
|
||||
|
||||
const About = () => {
|
||||
const [form, setForm] = useState<{ name: string; email: string; message: string }>({
|
||||
name: '',
|
||||
email: '',
|
||||
message: ''
|
||||
});
|
||||
const [state, Submit] = useForm(`${process.env.NEXT_PUBLIC_FORMSPREE_KEY}`);
|
||||
const [submitted, setSubmitted] = useState<boolean>(false);
|
||||
const [form, setForm] = useState<{
|
||||
name: string;
|
||||
email: string;
|
||||
message: string;
|
||||
}>({
|
||||
name: '',
|
||||
email: '',
|
||||
message: '',
|
||||
});
|
||||
const [state, Submit] = useForm(`${process.env.NEXT_PUBLIC_FORMSPREE_KEY}`);
|
||||
const [submitted, setSubmitted] = useState<boolean>(false);
|
||||
|
||||
const handleChange = (event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
setForm({ ...form, [event.target.name]: event.target.value });
|
||||
};
|
||||
const handleChange = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
) => {
|
||||
setForm({ ...form, [event.target.name]: event.target.value });
|
||||
};
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
try {
|
||||
await Submit(event);
|
||||
setSubmitted(true);
|
||||
} finally {
|
||||
setTimeout(() => setSubmitted(false), 1000);
|
||||
setForm({ name: '', email: '', message: '' });
|
||||
}
|
||||
};
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
try {
|
||||
await Submit(event);
|
||||
setSubmitted(true);
|
||||
} finally {
|
||||
setTimeout(() => setSubmitted(false), 1000);
|
||||
setForm({ name: '', email: '', message: '' });
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Contact | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='content'>
|
||||
<div>
|
||||
<h1>Contact Me {submitted && <span className='success'>Message sent ✔️</span>}</h1>
|
||||
<form className='contact' onSubmit={handleSubmit}>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Name'
|
||||
variant='small'
|
||||
name='name'
|
||||
required
|
||||
value={form.name}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError className='error' prefix='Name' field='name' errors={state.errors} />
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Email'
|
||||
variant='small'
|
||||
name='email'
|
||||
required
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError
|
||||
className='error'
|
||||
prefix='Email'
|
||||
field='email'
|
||||
errors={state.errors}
|
||||
/>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Message'
|
||||
variant='big'
|
||||
name='message'
|
||||
required
|
||||
value={form.message}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError
|
||||
className='error'
|
||||
prefix='Message'
|
||||
field='message'
|
||||
errors={state.errors}
|
||||
/>
|
||||
<MDXButton type='submit' variant='action' disabled={state.submitting || submitted}>
|
||||
Submit
|
||||
</MDXButton>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Contact | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='content'>
|
||||
<div>
|
||||
<h1>
|
||||
Contact Me{' '}
|
||||
{submitted && <span className='success'>Message sent ✔️</span>}
|
||||
</h1>
|
||||
<form className='contact' onSubmit={handleSubmit}>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Name'
|
||||
variant='small'
|
||||
name='name'
|
||||
required
|
||||
value={form.name}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError
|
||||
className='error'
|
||||
prefix='Name'
|
||||
field='name'
|
||||
errors={state.errors}
|
||||
/>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Email'
|
||||
variant='small'
|
||||
name='email'
|
||||
required
|
||||
value={form.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError
|
||||
className='error'
|
||||
prefix='Email'
|
||||
field='email'
|
||||
errors={state.errors}
|
||||
/>
|
||||
<Input
|
||||
type='text'
|
||||
placeholder='Message'
|
||||
variant='big'
|
||||
name='message'
|
||||
required
|
||||
value={form.message}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<ValidationError
|
||||
className='error'
|
||||
prefix='Message'
|
||||
field='message'
|
||||
errors={state.errors}
|
||||
/>
|
||||
<MDXButton
|
||||
type='submit'
|
||||
variant='action'
|
||||
disabled={state.submitting || submitted}
|
||||
>
|
||||
Submit
|
||||
</MDXButton>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default About;
|
||||
|
||||
+91
-88
@@ -8,106 +8,109 @@ import Card from '../components/Card';
|
||||
import Head from 'next/head';
|
||||
|
||||
interface Props {
|
||||
blogPosts: {
|
||||
title: string;
|
||||
author: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
projects: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
blogPosts: {
|
||||
title: string;
|
||||
author: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
projects: {
|
||||
title: string;
|
||||
description: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const Index = ({ blogPosts, projects }: Props) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<Hero />
|
||||
<div className='content'>
|
||||
<h1>About</h1>
|
||||
<div className='about'>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
{projects.length !== 0 && (
|
||||
<>
|
||||
<h1>Projects</h1>
|
||||
<Button href='/projects' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='projects'>
|
||||
<div className='projects-wrapper'>
|
||||
{projects.slice(0, 3).map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/projects/${slug}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{blogPosts.length !== 0 && (
|
||||
<>
|
||||
<h1>Blog</h1>
|
||||
<Button href='/blog' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='blog'>
|
||||
<div className='articles-wrapper'>
|
||||
{blogPosts.slice(0, 3).map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/blog/${slug}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<Hero />
|
||||
<div className='content'>
|
||||
<h1>About</h1>
|
||||
<div className='about'>
|
||||
<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.
|
||||
</p>
|
||||
</div>
|
||||
{projects.length !== 0 && (
|
||||
<>
|
||||
<h1>Projects</h1>
|
||||
<Button href='/projects' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='projects'>
|
||||
<div className='projects-wrapper'>
|
||||
{projects.slice(0, 3).map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/projects/${slug}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{blogPosts.length !== 0 && (
|
||||
<>
|
||||
<h1>Blog</h1>
|
||||
<Button href='/blog' className='blue'>
|
||||
See More
|
||||
</Button>
|
||||
<div className='blog'>
|
||||
<div className='articles-wrapper'>
|
||||
{blogPosts.slice(0, 3).map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/blog/${slug}`} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
|
||||
export const getStaticProps: GetStaticProps = async () => {
|
||||
const blogPosts = getBlogPosts();
|
||||
const projects = getProjects();
|
||||
return {
|
||||
props: {
|
||||
blogPosts,
|
||||
projects
|
||||
}
|
||||
};
|
||||
const blogPosts = getBlogPosts();
|
||||
const projects = getProjects();
|
||||
return {
|
||||
props: {
|
||||
blogPosts,
|
||||
projects,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+115
-104
@@ -14,129 +14,140 @@ import MDXButton from '../../components/MDXButton';
|
||||
import Image from 'next/image';
|
||||
|
||||
interface Props {
|
||||
source: MDXRemoteSerializeResult;
|
||||
frontMatter: any;
|
||||
source: MDXRemoteSerializeResult;
|
||||
frontMatter: any;
|
||||
}
|
||||
|
||||
const Project = ({ source, frontMatter }: Props) => {
|
||||
const router = useRouter();
|
||||
const htmlOverrides = { code: CodeBlock };
|
||||
const mdxComponents = { Button: MDXButton };
|
||||
const router = useRouter();
|
||||
const htmlOverrides = { code: CodeBlock };
|
||||
const mdxComponents = { Button: MDXButton };
|
||||
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
window.scrollTo(0, 0);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an 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.`
|
||||
}
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
}
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content={
|
||||
frontMatter?.description
|
||||
? frontMatter.description
|
||||
: `Hazem Krimi is an 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.`
|
||||
}
|
||||
/>
|
||||
<meta property='og:title' content={`${frontMatter?.title} | Hazem Krimi`} />
|
||||
<meta
|
||||
name='keywords'
|
||||
content={
|
||||
frontMatter?.tags
|
||||
? frontMatter.tags.join(' ')
|
||||
: `Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js`
|
||||
}
|
||||
/>
|
||||
<title>{`${frontMatter?.title} | Hazem Krimi`}</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='meta'>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>{frontMatter?.title}</h1>
|
||||
<p>{frontMatter?.description}</p>
|
||||
{frontMatter?.tags ? (
|
||||
<div className='tags-wrapper'>
|
||||
{frontMatter.tags.map((tag: string, index: number) => (
|
||||
<span key={index}>#{tag} </span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{frontMatter?.image && !frontMatter?.hideImage ? (
|
||||
<div className='image'>
|
||||
<Image alt={frontMatter?.title} src={frontMatter?.image} fill />
|
||||
</div>
|
||||
) : null}
|
||||
<hr />
|
||||
</div>
|
||||
<MDXProvider components={{ ...htmlOverrides, ...mdxComponents }}>
|
||||
<div className='content'>
|
||||
<MDXRemote {...source} />
|
||||
<h1>Showcase</h1>
|
||||
<div className='showcase-buttons'>
|
||||
<MDXButton variant='action' link={frontMatter?.demo} target='_blank'>
|
||||
Demo
|
||||
</MDXButton>
|
||||
<MDXButton variant='outline' link={frontMatter?.code} target='_blank'>
|
||||
Source Code
|
||||
</MDXButton>
|
||||
</div>
|
||||
</div>
|
||||
</MDXProvider>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
/>
|
||||
<meta
|
||||
property='og:title'
|
||||
content={`${frontMatter?.title} | Hazem Krimi`}
|
||||
/>
|
||||
<meta
|
||||
name='keywords'
|
||||
content={
|
||||
frontMatter?.tags
|
||||
? frontMatter.tags.join(' ')
|
||||
: `Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js`
|
||||
}
|
||||
/>
|
||||
<title>{`${frontMatter?.title} | Hazem Krimi`}</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='meta'>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>{frontMatter?.title}</h1>
|
||||
<p>{frontMatter?.description}</p>
|
||||
{frontMatter?.tags ? (
|
||||
<div className='tags-wrapper'>
|
||||
{frontMatter.tags.map((tag: string, index: number) => (
|
||||
<span key={index}>#{tag} </span>
|
||||
))}
|
||||
</div>
|
||||
) : null}
|
||||
{frontMatter?.image && !frontMatter?.hideImage ? (
|
||||
<div className='image'>
|
||||
<Image alt={frontMatter?.title} src={frontMatter?.image} fill />
|
||||
</div>
|
||||
) : null}
|
||||
<hr />
|
||||
</div>
|
||||
<MDXProvider components={{ ...htmlOverrides, ...mdxComponents }}>
|
||||
<div className='content'>
|
||||
<MDXRemote {...source} />
|
||||
<h1>Showcase</h1>
|
||||
<div className='showcase-buttons'>
|
||||
<MDXButton
|
||||
variant='action'
|
||||
link={frontMatter?.demo}
|
||||
target='_blank'
|
||||
>
|
||||
Demo
|
||||
</MDXButton>
|
||||
<MDXButton
|
||||
variant='outline'
|
||||
link={frontMatter?.code}
|
||||
target='_blank'
|
||||
>
|
||||
Source Code
|
||||
</MDXButton>
|
||||
</div>
|
||||
</div>
|
||||
</MDXProvider>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Project;
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
const paths = getPorjectsSlugs();
|
||||
return {
|
||||
paths,
|
||||
fallback: false
|
||||
};
|
||||
const paths = getPorjectsSlugs();
|
||||
return {
|
||||
paths,
|
||||
fallback: false,
|
||||
};
|
||||
};
|
||||
export const getStaticProps: GetStaticProps = async ({ params }: any) => {
|
||||
const projectContent = await getProjectdata(params.slug);
|
||||
const projectContent = await getProjectdata(params.slug);
|
||||
|
||||
if (!projectContent)
|
||||
return {
|
||||
props: {
|
||||
source: undefined,
|
||||
frontMatter: undefined
|
||||
}
|
||||
};
|
||||
if (!projectContent)
|
||||
return {
|
||||
props: {
|
||||
source: undefined,
|
||||
frontMatter: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
const { data, content } = matter(projectContent);
|
||||
const mdxSource = await serialize(content, {
|
||||
scope: data
|
||||
});
|
||||
const { data, content } = matter(projectContent);
|
||||
const mdxSource = await serialize(content, {
|
||||
scope: data,
|
||||
});
|
||||
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
frontMatter: data
|
||||
}
|
||||
};
|
||||
return {
|
||||
props: {
|
||||
source: mdxSource,
|
||||
frontMatter: data,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
+55
-55
@@ -6,74 +6,74 @@ import IconButton from '../../components/IconButton';
|
||||
import Head from 'next/head';
|
||||
|
||||
interface Props {
|
||||
projects: {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
projects: {
|
||||
title: string;
|
||||
description: string;
|
||||
image?: string;
|
||||
slug: string;
|
||||
date: string;
|
||||
tags?: string[];
|
||||
}[];
|
||||
}
|
||||
|
||||
const Index = ({ projects }: Props) => {
|
||||
const router = useRouter();
|
||||
const router = useRouter();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
||||
<meta name='author' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an experienced Full Stack developer with a focus on building user-friendly
|
||||
/>
|
||||
<link rel='canonical' href='https://hazemkrimi.tech' />
|
||||
<meta property='og:image' content='/logo.png' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='Hazem Krimi is an 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.'
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Projects | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>Projects</h1>
|
||||
<div className='projects-wrapper'>
|
||||
{projects.length !== 0 ? (
|
||||
projects.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/projects/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
/>
|
||||
<meta property='og:title' content='Hazem Krimi' />
|
||||
<meta
|
||||
name='keywords'
|
||||
content='Hazem, Krimi, Hazem Krimi, Developer, Software, Engineer, Web, Mobile, Frontend, Backend, Fullstack, JavaScript, TypeScript, React.js, React Native, Node.js, Portfolio, Blog, Tutorials, Tech News, Software Developer, Software Engineer, Full Stack TypeScript Developer, Next.js'
|
||||
/>
|
||||
<title>Projects | Hazem Krimi</title>
|
||||
</Head>
|
||||
<Wrapper>
|
||||
<div className='back' onClick={() => router.back()}>
|
||||
<IconButton alt='Back' icon='/icons/arrow-left.svg' />
|
||||
<span>Back</span>
|
||||
</div>
|
||||
<h1>Projects</h1>
|
||||
<div className='projects-wrapper'>
|
||||
{projects.length !== 0 ? (
|
||||
projects.map(({ slug, ...rest }) => (
|
||||
<Card {...rest} key={slug} href={`/projects/${slug}`} />
|
||||
))
|
||||
) : (
|
||||
<h4>Nothing for now</h4>
|
||||
)}
|
||||
</div>
|
||||
</Wrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
||||
|
||||
export const getStaticProps = async () => {
|
||||
const projects = getProjects();
|
||||
return {
|
||||
props: {
|
||||
projects
|
||||
}
|
||||
};
|
||||
const projects = getProjects();
|
||||
return {
|
||||
props: {
|
||||
projects,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user