diff --git a/content/projects/itour.md b/content/projects/itour.md new file mode 100644 index 0000000..57aa30b --- /dev/null +++ b/content/projects/itour.md @@ -0,0 +1,7 @@ +--- +title: 'iTour' +image: '/iTour.jpg' +technologies: ['JavaScript', 'React', 'Next.js', 'TailwindCSS', 'i18n'] +--- + +iTour project info goes here diff --git a/content/projects/skauna.md b/content/projects/skauna.md new file mode 100644 index 0000000..b20f736 --- /dev/null +++ b/content/projects/skauna.md @@ -0,0 +1,7 @@ +--- +title: 'Skauna' +image: '/Skauna.jpg' +technologies: ['HTML', 'CSS', 'JavaScript'] +--- + +Skauna project info goes here diff --git a/package-lock.json b/package-lock.json index 52e405d..5130b3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "gray-matter": "^4.0.3", + "marked": "^12.0.0", "next": "14.1.0", "next-themes": "^0.2.1", "react": "^18", @@ -3124,7 +3126,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3204,6 +3205,17 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "dependencies": { + "is-extendable": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3642,6 +3654,40 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/gray-matter": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/gray-matter/-/gray-matter-4.0.3.tgz", + "integrity": "sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==", + "dependencies": { + "js-yaml": "^3.13.1", + "kind-of": "^6.0.2", + "section-matter": "^1.0.0", + "strip-bom-string": "^1.0.0" + }, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/gray-matter/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/gray-matter/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -3947,6 +3993,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -4324,6 +4378,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.22", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", @@ -4696,6 +4758,17 @@ "node": ">=10" } }, + "node_modules/marked": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-12.0.0.tgz", + "integrity": "sha512-Vkwtq9rLqXryZnWaQc86+FHLC6tr/fycMfYAhiOIXkrNmeGAyhSxjqu0Rs1i0bBqw5u0S7+lV9fdH2ZSVaoa0w==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -5835,6 +5908,18 @@ "loose-envify": "^1.1.0" } }, + "node_modules/section-matter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz", + "integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==", + "dependencies": { + "extend-shallow": "^2.0.1", + "kind-of": "^6.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -5984,6 +6069,11 @@ "node": ">=0.10.0" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==" + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -6165,6 +6255,14 @@ "node": ">=4" } }, + "node_modules/strip-bom-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-string/-/strip-bom-string-1.0.0.tgz", + "integrity": "sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strip-final-newline": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", diff --git a/package.json b/package.json index 5ea7398..7d08caf 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "dependencies": { "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "gray-matter": "^4.0.3", + "marked": "^12.0.0", "next": "14.1.0", "next-themes": "^0.2.1", "react": "^18", diff --git a/project-words.txt b/project-words.txt index b68e65a..844bf3d 100644 --- a/project-words.txt +++ b/project-words.txt @@ -2,4 +2,5 @@ raleway Bochkovskyi clsx Nextdotjs -Tailwindcss \ No newline at end of file +Tailwindcss +Skauna \ No newline at end of file diff --git a/public/Anton_Bochkovskyi_Front-End_Developer_CV.pdf b/public/Anton_Bochkovskyi_Front-End_Developer_CV.pdf new file mode 100644 index 0000000..7a3fca2 Binary files /dev/null and b/public/Anton_Bochkovskyi_Front-End_Developer_CV.pdf differ diff --git a/public/Skauna.jpg b/public/Skauna.jpg new file mode 100644 index 0000000..694f207 Binary files /dev/null and b/public/Skauna.jpg differ diff --git a/public/iTour.jpg b/public/iTour.jpg new file mode 100644 index 0000000..9cee09d Binary files /dev/null and b/public/iTour.jpg differ diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f8422..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/page.tsx b/src/app/page.tsx index 474da00..ac6e9e0 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,13 @@ import About from '@/components/sections/about' import Hero from '@/components/sections/hero/hero' +import Projects from '@/components/sections/projects/projects' export default function Home() { return ( <> - + + ) } diff --git a/src/components/header/header-navbar.tsx b/src/components/header/header-navbar.tsx index 5a78459..20039cc 100644 --- a/src/components/header/header-navbar.tsx +++ b/src/components/header/header-navbar.tsx @@ -29,7 +29,7 @@ const HeaderNavbar: FC = ({ className }) => { orientation={isMobile ? 'vertical' : 'horizontal'} > About Me - My Work + My Projects Contact Me diff --git a/src/components/header/header.tsx b/src/components/header/header.tsx index b8ad8a2..b583702 100644 --- a/src/components/header/header.tsx +++ b/src/components/header/header.tsx @@ -19,7 +19,7 @@ const Header: FC = ({ className }) => { >
- +
diff --git a/src/components/sections/about.tsx b/src/components/sections/about.tsx index abe7ed0..6cc72ec 100644 --- a/src/components/sections/about.tsx +++ b/src/components/sections/about.tsx @@ -1,15 +1,16 @@ +import { buttonVariants } from '@/components/ui/button' import Content from '@/components/ui/content' import Heading from '@/components/ui/heading' import Technologies from '@/components/ui/technologies/technologies' import { cn } from '@/utils' -import { FC } from 'react' +import Link from 'next/link' +import { FC, HTMLAttributes } from 'react' -const About: FC = () => { +interface AboutProps extends HTMLAttributes {} + +const About: FC = ({ className, ...props }) => { return ( -
+
@@ -37,6 +38,15 @@ const About: FC = () => { lasting impression!

+ + Download CV +
diff --git a/src/components/sections/hero/hero.module.css b/src/components/sections/hero/hero.module.css index bbdb78f..8f4c265 100644 --- a/src/components/sections/hero/hero.module.css +++ b/src/components/sections/hero/hero.module.css @@ -1,4 +1,4 @@ .hero-container { - margin-top: var(--header-height); - height: calc(100dvh - var(--header-height)); + padding-top: var(--header-height); + height: 100dvh; } diff --git a/src/components/sections/hero/hero.tsx b/src/components/sections/hero/hero.tsx index dc592f7..7ede32d 100644 --- a/src/components/sections/hero/hero.tsx +++ b/src/components/sections/hero/hero.tsx @@ -3,18 +3,25 @@ import Content from '@/components/ui/content' import Heading from '@/components/ui/heading' import Socials from '@/components/ui/socials/socials' import { cn } from '@/utils' -import { FC } from 'react' +import { FC, HTMLAttributes } from 'react' import styles from './hero.module.css' -const Hero: FC = () => { +interface HeroProps extends HTMLAttributes {} + +const Hero: FC = ({ className, ...props }) => { return (
- + Hi, I'm Anton Bochkovskyi

diff --git a/src/components/sections/projects/project-card.tsx b/src/components/sections/projects/project-card.tsx new file mode 100644 index 0000000..27c1563 --- /dev/null +++ b/src/components/sections/projects/project-card.tsx @@ -0,0 +1,59 @@ +import Card from '@/components/ui/card' +import Heading from '@/components/ui/heading' +import ImagePlaceholder from '@/components/ui/image-placeholder' +import Project from '@/types/Project' +import { cn } from '@/utils' +import Image from 'next/image' +import Link from 'next/link' +import { FC, HTMLAttributes } from 'react' + +interface ProjectCardProps extends HTMLAttributes { + data: Project + imageSizes?: string +} + +const ProjectCard: FC = ({ + className, + data: { body, image, slug, technologies, title }, + imageSizes, +}) => { + return ( +

+ + + {image ? ( + {title + ) : ( + + )} +
+ {title || slug} +

+ {technologies && ( +

+ {technologies.map((technology, index) => ( + + {technology} + + ))} +
+ )} +
+ +
+
+ ) +} + +export default ProjectCard diff --git a/src/components/sections/projects/projects.tsx b/src/components/sections/projects/projects.tsx new file mode 100644 index 0000000..c931403 --- /dev/null +++ b/src/components/sections/projects/projects.tsx @@ -0,0 +1,41 @@ +import ProjectCard from '@/components/sections/projects/project-card' +import Content from '@/components/ui/content' +import Heading from '@/components/ui/heading' +import Project from '@/types/Project' +import { cn } from '@/utils' +import { getProjects } from '@/utils/projects' +import { FC, HTMLAttributes } from 'react' + +interface ProjectsProps extends HTMLAttributes {} + +const Projects: FC = async ({ className, ...props }) => { + const projects: Project[] = await getProjects() + + return ( +
+ + + My Projects + + {!projects?.length ? ( +

+ No projects found at the moment. Check back later for updates or + explore other sections of the site. +

+ ) : ( +
+ {projects.map((project) => ( + + ))} +
+ )} +
+
+ ) +} + +export default Projects diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 817b61b..75f04f7 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -11,12 +11,12 @@ export const buttonVariants = cva( }, variants: { size: { - default: 'py-2 px-5 rounded-md', - sm: 'p-2', + default: 'block w-fit py-2 px-5 rounded-md', + sm: 'block w-fit p-2', }, variant: { default: - 'border-2 border-accent bg-accent text-light md:hover:bg-opacity-80', + 'border-2 border-accent bg-accent text-light md:hover:bg-accent-light md:hover:border-accent-light', icon: 'bg-transparent md:hover:text-accent', outline: 'border-2 border-accent bg-transparent text-accent md:hover:bg-accent md:hover:text-light', diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..093aa8d --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,17 @@ +import { cn } from '@/utils' +import { FC, HTMLAttributes } from 'react' + +interface CardProps extends HTMLAttributes {} + +const Card: FC = ({ children, className, ...props }) => { + return ( +
+ {children} +
+ ) +} + +export default Card diff --git a/src/components/ui/heading.tsx b/src/components/ui/heading.tsx index 6dd53ec..c324e84 100644 --- a/src/components/ui/heading.tsx +++ b/src/components/ui/heading.tsx @@ -4,16 +4,25 @@ import { FC, HTMLAttributes } from 'react' export const headingVariants = cva('w-fit', { defaultVariants: { + position: 'default', size: 'h1', }, variants: { + position: { + center: 'mx-auto after:left-1/2 after:-translate-x-1/2', + default: 'after:left-0', + }, size: { h1: 'text-4xl font-bold md:text-5xl', h2: 'text-3xl font-semibold md:text-4xl', + h3: 'text-2xl font-semibold md:text-3xl', + h4: 'text-xl font-semibold md:text-2xl', + h5: 'text-lg font-semibold md:text-xl', + h6: 'font-semibold md:text-4xl', }, variant: { underline: - 'pb-1.5 relative after:absolute after:bottom-0 after:left-0 after:h-0.5 after:bg-accent after:w-2/6', + 'pb-1.5 relative after:absolute after:bottom-0 after:h-0.5 after:bg-accent after:w-2/6', }, }, }) @@ -22,11 +31,19 @@ interface HeadingProps extends HTMLAttributes, VariantProps {} -const Heading: FC = ({ children, className, size, variant }) => { +const Heading: FC = ({ + children, + className, + position, + size, + variant, +}) => { const HeadingTag = size ? size : 'h1' return ( - + {children} ) diff --git a/src/components/ui/image-placeholder.tsx b/src/components/ui/image-placeholder.tsx new file mode 100644 index 0000000..38fcfa0 --- /dev/null +++ b/src/components/ui/image-placeholder.tsx @@ -0,0 +1,25 @@ +import { cn } from '@/utils' +import { FC, HTMLAttributes } from 'react' +import { CiImageOff } from 'react-icons/ci' + +interface ImagePlaceholderProps extends HTMLAttributes {} + +const ImagePlaceholder: FC = ({ + className, + ...props +}) => { + return ( +
+ + Image Not Found +
+ ) +} + +export default ImagePlaceholder diff --git a/src/components/ui/technologies/technologies.tsx b/src/components/ui/technologies/technologies.tsx index f156702..9fb4dc5 100644 --- a/src/components/ui/technologies/technologies.tsx +++ b/src/components/ui/technologies/technologies.tsx @@ -1,10 +1,13 @@ import TechnologiesItem from '@/components/ui/technologies/technologies-item' import { cn } from '@/utils' import { FC, HTMLAttributes } from 'react' -import { FaReact, FaSass } from 'react-icons/fa' +import { FaSass } from 'react-icons/fa' import { + SiCss3, + SiHtml5, SiJavascript, SiNextdotjs, + SiReact, SiTailwindcss, SiTypescript, } from 'react-icons/si' @@ -20,13 +23,25 @@ const Technologies: FC = ({ className, ...props }) => { )} {...props} > + + + + @@ -35,17 +50,11 @@ const Technologies: FC = ({ className, ...props }) => { className="text-black dark:text-white" title="Next.js" /> - - ) } diff --git a/src/components/ui/theme-toggler.tsx b/src/components/ui/theme-toggler.tsx index 46870c4..735529f 100644 --- a/src/components/ui/theme-toggler.tsx +++ b/src/components/ui/theme-toggler.tsx @@ -17,26 +17,18 @@ const ThemeToggler: FC = ({ className }) => { useEffect(() => setMounted(true), []) - if (!mounted) { - return ( - - ) - } - return ( <> {!mounted ? ( ) : (