diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/next/public/images/about/2F6ACD0F-D23F-4006-97D8-5D062B0EB330_1_105_c.jpeg b/next/public/images/about/2F6ACD0F-D23F-4006-97D8-5D062B0EB330_1_105_c.jpeg new file mode 100644 index 0000000..8916223 Binary files /dev/null and b/next/public/images/about/2F6ACD0F-D23F-4006-97D8-5D062B0EB330_1_105_c.jpeg differ diff --git a/next/public/images/about/3D037048-F26D-464D-BB7E-13AA8696C6B9_1_105_c.jpeg b/next/public/images/about/3D037048-F26D-464D-BB7E-13AA8696C6B9_1_105_c.jpeg new file mode 100644 index 0000000..25c3974 Binary files /dev/null and b/next/public/images/about/3D037048-F26D-464D-BB7E-13AA8696C6B9_1_105_c.jpeg differ diff --git a/next/public/images/about/EF46B464-0160-404D-B2B4-33224F118617_1_105_c.jpeg b/next/public/images/about/EF46B464-0160-404D-B2B4-33224F118617_1_105_c.jpeg new file mode 100644 index 0000000..7ed13e0 Binary files /dev/null and b/next/public/images/about/EF46B464-0160-404D-B2B4-33224F118617_1_105_c.jpeg differ diff --git a/next/src/app/about/image-section/ImageSection.tsx b/next/src/app/about/image-section/ImageSection.tsx new file mode 100644 index 0000000..89cdc3a --- /dev/null +++ b/next/src/app/about/image-section/ImageSection.tsx @@ -0,0 +1,18 @@ +import React, { FunctionComponent } from 'react'; +import styles from '@/app/about/styles.module.scss'; +import Image from 'next/image'; +import data from './data'; + +const ImageSection: FunctionComponent = () => { + return ( +
+ {data.map((item) => ( + {item.alt} + ))} +
+ ); +}; + +export default ImageSection; \ No newline at end of file diff --git a/next/src/app/about/image-section/data.ts b/next/src/app/about/image-section/data.ts new file mode 100644 index 0000000..5bebe13 --- /dev/null +++ b/next/src/app/about/image-section/data.ts @@ -0,0 +1,21 @@ +interface Item { + src: string; + alt: string; +} + +const data: Item[] = [ + { + src: '/images/about/3D037048-F26D-464D-BB7E-13AA8696C6B9_1_105_c.jpeg', + alt: 'Dušan on the beach.' + }, + { + src: '/images/about/EF46B464-0160-404D-B2B4-33224F118617_1_105_c.jpeg', + alt: 'Lilly on a sofa.' + }, + { + src: '/images/about/2F6ACD0F-D23F-4006-97D8-5D062B0EB330_1_105_c.jpeg', + alt: 'Dušan next to an elephant.' + } +]; + +export default data; \ No newline at end of file diff --git a/next/src/app/about/page.tsx b/next/src/app/about/page.tsx index 969b509..8093714 100644 --- a/next/src/app/about/page.tsx +++ b/next/src/app/about/page.tsx @@ -1,13 +1,72 @@ import { NextPage } from 'next'; -import gStyles from '../../styles/global.module.scss'; +import gStyles from '@/styles/global.module.scss'; +import styles from './styles.module.scss'; import React from 'react'; +import Link from 'next/link'; +import ImageSection from '@/app/about/image-section/ImageSection'; const Page: NextPage = () => { return ( -
-
-

About me

-
+
+
+

About Me

+ +
+

Introduction

+

Hello! I'm Dušan Todorović, a resident of Belgrade, Serbia. You might think I've been coding since + I was in diapers, but nope!

+

I come from a long line of musicians - my dad, granddad, and great-granddad were + all accordion players.

+

So, you can imagine music has always been a big part of my life. I picked up the accordion at the tender + age of 8 and I was pretty good at it!

+

Musical Background

+

I wasn't just limited to the accordion. I also played the keyboard and it became my main source of + income for years. I even taught others how to play accordion and keyboard.

+

Oh, really, I could share so much about this. The main point is, my father was rooting for me to + prolong our musical roots, but I guess I never really found myself going for it commercially, especially + because of the social side in folk music in Serbia.

+

But yes, I had to earn money and I was good at playing, so I kept going for it while exploring other + options.

+

Transition to Programming

+

My transition into programming happened quite unexpectedly. During college, I had to take a web design + class and that's where I fell in love with coding. After that, I started taking front-end courses on + platforms like Udemy and FreeCodeCamp.

+

Eventually, I landed my first job as a junior front-end developer through a friend of a friend. At that + time, knowing HTML, CSS (with a bit of Bootstrap), and some JavaScript was enough to get by.

+

Early Career

+

Despite the lack of advanced tools, I made significant progress after leaving my first job. I spent a lot + of time reading documentation and working on personal projects to build up my full-stack skills. I also + continued to teach music and play on parties to support myself financially.

+

I was pretty disciplined. I worked on my projects every day, as if it was my job. I have to + say, the key to pushing me forward was that I was always working on challenging projects. It started off + from ideas, designing them myself in Figma, and then handling the front-end and/or back-end if it required + it, and then deployment.

+

Current Role

+

After building my portfolio and honing my skills, I landed a job that changed my life. I'm currently a + front-end team lead at a reputable company. I love how I've grown not just in my tech skills, but also in + my management and soft skills.

+

I enjoy both the operational side of work, and also managing people.

+

Interests

+

I have a few quirky habits that might surprise you. For instance, I used to watch streams of League of + Legends, even though I don't play the game anymore. I also enjoy playing PlayStation 5 from time to time, + but lately, I've been more inclined towards working on personal projects than playing video games.

+

I absolutely love dogs and other animals, but dogs are my favorite, that's for sure. They are pure, and + lovable and they are our best friends! I always get fascinated, over and over, by how much positive energy + I get from Lilly. Lilly is my dog, Shi Tzuh!

+

I also like TV shows and movies. Some of my favorites include The Sopranos, Dexter, Mad Men.

+

Contact Information

+

Feel free to reach out to me if you want to chat about coding, or anything else. I'm always open to new + connections and opportunities:

+
    +
  • + Linkedin +
  • +
  • + Mail +
  • +
+
+
); }; diff --git a/next/src/app/about/styles.module.scss b/next/src/app/about/styles.module.scss new file mode 100644 index 0000000..5eff272 --- /dev/null +++ b/next/src/app/about/styles.module.scss @@ -0,0 +1,62 @@ +.main { + h1 { + margin-bottom: 2rem; + } + + .imagesContainer { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 1rem; + + @media (min-width: 480px) { + grid-template-columns: repeat(3, 1fr); + } + + img { + max-width: 100%; + height: auto; + object-fit: cover; + } + } + + .mainContent { + margin-top: 3rem; + + .subheading { + font-weight: 500; + font-size: clamp(1.4rem, 1.1vw, 1.6rem); + margin-bottom: 1rem; + + &:not(:first-of-type) { + margin-top: 2rem; + } + + + img { + + p { + margin-top: 1rem; + } + } + } + + p { + margin-top: 1rem; + + &:first-child { + margin-top: 0; + } + } + } + + a { + text-decoration: underline; + } + + ul, ol { + display: flex; + flex-direction: column; + row-gap: 1rem; + padding-left: 1.5rem; + list-style-type: decimal; + margin-top: 1.5rem; + } +} diff --git a/next/src/app/blog/[slug]/blog-content/styles.scss b/next/src/app/blog/[slug]/blog-content/styles.scss index 7a672a6..782706b 100644 --- a/next/src/app/blog/[slug]/blog-content/styles.scss +++ b/next/src/app/blog/[slug]/blog-content/styles.scss @@ -11,7 +11,7 @@ } p, ul, ol { - margin-bottom: 1.1rem; + margin-bottom: 1.5rem; } img { @@ -21,21 +21,12 @@ .rehype-code-title { background-color: darken($color-background, 2%); color: lighten($color-background, 35%); - padding: .5em; - padding-inline: 1em; + padding: .5em 1em; } pre { - font-size: clamp(1rem, .8vw, 1.2rem); - margin-bottom: 1.1rem; - } - - ul { - list-style-type: disc; - } - - ol { - list-style-type: decimal; + font-size: clamp(1rem, 1vw, 1.2rem); + margin-bottom: 1.5rem; } ul, ol { @@ -43,5 +34,6 @@ flex-direction: column; row-gap: 1rem; padding-left: 1.5rem; + list-style-type: decimal; } } \ No newline at end of file diff --git a/next/src/app/blog/[slug]/blog-footer/styles.module.scss b/next/src/app/blog/[slug]/blog-footer/styles.module.scss index 673b192..5e3c4fe 100644 --- a/next/src/app/blog/[slug]/blog-footer/styles.module.scss +++ b/next/src/app/blog/[slug]/blog-footer/styles.module.scss @@ -3,7 +3,7 @@ .container { display: flex; width: 100%; - margin-top: 3rem; + margin-top: 1.5rem; padding-top: 3rem; border-top: solid lighten($color-background, 3%) 6px; diff --git a/next/src/app/blog/[slug]/blog-header/BlogHeader.tsx b/next/src/app/blog/[slug]/blog-header/BlogHeader.tsx index 5ab4868..fa6f134 100644 --- a/next/src/app/blog/[slug]/blog-header/BlogHeader.tsx +++ b/next/src/app/blog/[slug]/blog-header/BlogHeader.tsx @@ -1,5 +1,6 @@ import React, { FunctionComponent } from 'react'; import gStyles from '@/styles/global.module.scss'; +import styles from './styles.module.scss'; import { Post } from '@/components/blog-post-item/types'; interface Props { @@ -11,7 +12,7 @@ const BlogHeader: FunctionComponent = ({ post }) => { return (
-

{post.attributes.title}

+

{post.attributes.title}

{categories && (
    {categories.map((category) => ( diff --git a/next/src/app/blog/[slug]/blog-header/styles.module.scss b/next/src/app/blog/[slug]/blog-header/styles.module.scss new file mode 100644 index 0000000..3c8aab5 --- /dev/null +++ b/next/src/app/blog/[slug]/blog-header/styles.module.scss @@ -0,0 +1,3 @@ +.heading { + text-transform: initial; +} \ No newline at end of file diff --git a/next/src/app/blog/styles.module.scss b/next/src/app/blog/styles.module.scss index 1d58427..79dd065 100644 --- a/next/src/app/blog/styles.module.scss +++ b/next/src/app/blog/styles.module.scss @@ -6,7 +6,6 @@ } .categories { - margin-top: 2rem; justify-content: flex-start; display: flex; gap: .5rem; diff --git a/next/src/app/skills/jobs/Jobs.tsx b/next/src/app/skills/jobs/Jobs.tsx new file mode 100644 index 0000000..5c693be --- /dev/null +++ b/next/src/app/skills/jobs/Jobs.tsx @@ -0,0 +1,26 @@ +import React, { FunctionComponent } from 'react'; +import styles from './styles.module.scss'; +import jobsData from './data'; +import JobItem from './job-item/JobItem'; + +const Jobs: FunctionComponent = () => { + return ( + <> +

    Work Experience

    +
    + {jobsData.map((item, index) => ( + + ))} +
    + + ); +}; + +export default Jobs; \ No newline at end of file diff --git a/next/src/app/skills/jobs/data.ts b/next/src/app/skills/jobs/data.ts new file mode 100644 index 0000000..ccd2a80 --- /dev/null +++ b/next/src/app/skills/jobs/data.ts @@ -0,0 +1,41 @@ +export interface JobItem { + jobTitle: string; + companyName: string; + startDate: Date; + endDate?: Date; + location: string; + achievements: string[]; +} + +const data: JobItem[] = [ + { + jobTitle: 'Lead Front-End Developer', + companyName: 'Citrus Systems', + location: 'Belgrade, Serbia', + startDate: new Date(2021, 6), + achievements: ['Successfully led the team in migrating to React and Vue, resulting in improved performance and maintainability', + 'Developed and maintained internal front-end framework used across multiple projects' + ] + }, { + jobTitle: 'Front-End Developer', + companyName: 'Citrus Systems', + location: 'Belgrade, Serbia', + startDate: new Date(2020, 7), + endDate: new Date(2021, 5), + achievements: ['Revamped the outdated codebase into a more streamlined, modular structure while preserving our proprietary internal front-end framework utilized across various iGaming projects.', + 'This involved extensive use of technologies such as HTML5, ES6+, SASS, and TWIG.' + ] + }, + { + jobTitle: 'Front-End Web Developer', + companyName: 'Boca Tech', + location: 'Belgrade, Serbia', + startDate: new Date(2018, 4), + endDate: new Date(2019, 2), + achievements: ['As the sole front-end developer in my first role, I successfully executed numerous projects, including a complex appointment scheduling platform for beauty services.', + 'My contributions extended beyond coding, integrating UI/UX designs with functional requirements and managing data entries.' + ] + } +]; + +export default data; \ No newline at end of file diff --git a/next/src/app/skills/jobs/job-item/JobItem.tsx b/next/src/app/skills/jobs/job-item/JobItem.tsx new file mode 100644 index 0000000..20ed064 --- /dev/null +++ b/next/src/app/skills/jobs/job-item/JobItem.tsx @@ -0,0 +1,33 @@ +import React, { FunctionComponent } from 'react'; +import styles from './styles.module.scss'; +import gStyles from '@/styles/global.module.scss'; +import { JobItem } from '../data'; +import { convertToString, formatDuration } from './scripts'; + +const JobItem: FunctionComponent = ({ + jobTitle, + companyName, + location, + achievements, + startDate, + endDate + }) => { + return ( +
    +
    +

    {companyName}

    +

    {convertToString(startDate, endDate)} | {formatDuration(startDate, endDate)}

    +

    {location}

    +
    +

    {jobTitle}

    +
      + {achievements.map((achievement, index) => ( +
    • {achievement}
    • + ))} +
    +
    + ); +}; + +export default JobItem; \ No newline at end of file diff --git a/next/src/app/skills/jobs/job-item/scripts.ts b/next/src/app/skills/jobs/job-item/scripts.ts new file mode 100644 index 0000000..01597d6 --- /dev/null +++ b/next/src/app/skills/jobs/job-item/scripts.ts @@ -0,0 +1,22 @@ +import { differenceInMonths, format } from 'date-fns'; + +export const formatDuration = (startDate: Date, endDate?: Date) => { + const monthsDifference = differenceInMonths(endDate ?? new Date(), startDate); + const years = Math.floor(monthsDifference / 12); + const months = monthsDifference % 12; + + const durationParts = []; + + if (years > 0) { + durationParts.push(`${years} years`); + } + if (months > 0) { + durationParts.push(`${months} months`); + } + + return durationParts.join(', '); +}; + +export const convertToString = (startDate: Date, endDate?: Date) => { + return `${format(startDate, 'MMM yyyy')} - ${endDate ? format(endDate, 'MMM yyyy') : 'present'}`; +}; \ No newline at end of file diff --git a/next/src/app/skills/jobs/job-item/styles.module.scss b/next/src/app/skills/jobs/job-item/styles.module.scss new file mode 100644 index 0000000..5bf7421 --- /dev/null +++ b/next/src/app/skills/jobs/job-item/styles.module.scss @@ -0,0 +1,36 @@ +@import '@/styles/vars'; + +.container { + padding: 1.5rem; + background-color: lighten($color-background, 3%); + border-radius: .35rem; + border: none; + + .title { + margin-top: .5rem; + color: $color-accent; + } + + .companyName, .title { + font-weight: 500; + } + + .location, .duration { + font-size: clamp(.9rem, .8vw, 1.3rem); + font-weight: 300; + } + + ul { + margin-top: .5rem; + display: flex; + flex-direction: column; + row-gap: .5rem; + list-style-type: disc; + margin-left: 1rem; + font-size: clamp(1rem, .9vw, 1.4rem); + } + + .location { + color: darken($color-text, 40%); + } +} diff --git a/next/src/app/skills/jobs/styles.module.scss b/next/src/app/skills/jobs/styles.module.scss new file mode 100644 index 0000000..3278828 --- /dev/null +++ b/next/src/app/skills/jobs/styles.module.scss @@ -0,0 +1,5 @@ +.container { + display: flex; + flex-direction: column; + gap: 1rem; +} \ No newline at end of file diff --git a/next/src/app/skills/page.tsx b/next/src/app/skills/page.tsx index f6194d6..747fbda 100644 --- a/next/src/app/skills/page.tsx +++ b/next/src/app/skills/page.tsx @@ -1,13 +1,33 @@ import { NextPage } from 'next'; -import gStyles from '../../styles/global.module.scss'; +import gStyles from '@/styles/global.module.scss'; +import styles from './styles.module.scss'; import React from 'react'; +import Link from 'next/link'; +import SoftSkills from '@/app/skills/soft-skills/SoftSkills'; +import TechSkills from '@/app/skills/tech-skills/TechSkills'; +import Jobs from '@/app/skills/jobs/Jobs'; const Page: NextPage = () => { return ( -
    -
    -

    My skills

    +
    +

    Skills

    +
    + +
    + +

    Contact Information

    +

    Feel free to reach out to me if you want to chat about coding, or anything else. I'm + always open to new + connections and opportunities:

    +
      +
    • + Linkedin +
    • +
    • + Mail +
    • +
    ); }; diff --git a/next/src/app/skills/soft-skills/SoftSkills.tsx b/next/src/app/skills/soft-skills/SoftSkills.tsx new file mode 100644 index 0000000..ac604be --- /dev/null +++ b/next/src/app/skills/soft-skills/SoftSkills.tsx @@ -0,0 +1,24 @@ +import React, { FunctionComponent } from 'react'; + +const SoftSkills: FunctionComponent = () => { + return ( + <> +

    Soft Skills

    +

    In addition to my technical skills, I place a high value on clear communication and effective + collaboration. I believe that these soft skills are crucial in any team setting, contributing significantly + to the overall success of the project. One of my strengths lies in delegating tasks efficiently, considering + the priorities, capabilities, and work styles of my team members. This approach ensures that everyone feels + valued and motivated, leading to higher productivity and job satisfaction.

    +

    I also champion initiative and proactivity within my team. I believe in giving others the opportunity to + showcase their ideas and abilities, fostering a culture of innovation and growth. At the same time, I'm not + afraid to provide constructive feedback when necessary, helping individuals improve and succeed.

    +

    My leadership journey includes successfully navigating the hiring process, from interviewing candidates to + making the final decision. I take pride in maintaining a stable team with no quitters over three years, a + testament to my ability to build and nurture a positive work environment.

    +

    I thrive on challenges and the thrill of solving complex problems. The feeling of successfully implementing + a major feature is something I truly cherish.

    + + ); +}; + +export default SoftSkills; \ No newline at end of file diff --git a/next/src/app/skills/styles.module.scss b/next/src/app/skills/styles.module.scss new file mode 100644 index 0000000..36b8ec4 --- /dev/null +++ b/next/src/app/skills/styles.module.scss @@ -0,0 +1,33 @@ +@import '@/styles/vars'; + +.main { + h2 { + font-weight: 500; + font-size: clamp(1.4rem, 1.1vw, 1.6rem); + margin-bottom: 1rem; + + &:not(:first-child) { + margin-top: 2rem; + } + } + + p { + + p { + margin-top: 1rem; + } + } + + a { + text-decoration: underline; + } + + ul, ol { + display: flex; + flex-direction: column; + row-gap: 1rem; + padding-left: 1.5rem; + list-style-type: decimal; + margin-top: 1.5rem; + } +} + diff --git a/next/src/app/skills/tech-skills/TechSkills.tsx b/next/src/app/skills/tech-skills/TechSkills.tsx new file mode 100644 index 0000000..df14f24 --- /dev/null +++ b/next/src/app/skills/tech-skills/TechSkills.tsx @@ -0,0 +1,38 @@ +import React, { FunctionComponent } from 'react'; +import Link from 'next/link'; + +const TechSkills: FunctionComponent = () => { + return ( + <> +

    Technical Skills

    +
      +
    • +

      + React - Developed reusable UI components and + state management solutions using hooks and context API. +

      +
    • +
    • +

      + Next.js - Built server-rendered applications + with static site generation and API routes for improved performance and SEO. +

      +
    • +
    • +

      + Redux - Managed global state with actions and + reducers for predictable state updates across complex applications. +

      +
    • +
    • +

      + SASS - Authored modular stylesheets and used + mixins and functions to enhance maintainability and scalability of CSS. +

      +
    • +
    + + ); +}; + +export default TechSkills; \ No newline at end of file diff --git a/next/src/components/home-components/featured-posts/FeaturedPosts.tsx b/next/src/components/home-components/featured-posts/FeaturedPosts.tsx index 5897215..dfb629f 100644 --- a/next/src/components/home-components/featured-posts/FeaturedPosts.tsx +++ b/next/src/components/home-components/featured-posts/FeaturedPosts.tsx @@ -10,7 +10,7 @@ const FeaturedPosts: FunctionComponent = async () => { return (
    -

    Featured posts

    +

    Featured posts

    Ever feel like writing about something intriguing or useful? That's me! I'm all about exploring topics that spark my curiosity or could help fellow developers. Always open to sharing my insights and experiences.

    diff --git a/next/src/components/home-components/featured-posts/styles.module.scss b/next/src/components/home-components/featured-posts/styles.module.scss index 52358cd..270663d 100644 --- a/next/src/components/home-components/featured-posts/styles.module.scss +++ b/next/src/components/home-components/featured-posts/styles.module.scss @@ -3,8 +3,11 @@ .featuredPostsHeading { margin-top: .5rem; margin-bottom: 1.5rem; - font-weight: 500; align-self: flex-start; + font-size: clamp(1.5rem, 2vw, 4rem); + font-weight: 600; + text-transform: lowercase; + letter-spacing: .2rem; } .checkAllPosts { diff --git a/next/src/styles/global.module.scss b/next/src/styles/global.module.scss index cc1bc71..4e85bfe 100644 --- a/next/src/styles/global.module.scss +++ b/next/src/styles/global.module.scss @@ -30,15 +30,10 @@ .pageHeading { font-size: clamp(1.5rem, 3vw, 2.5rem); font-weight: 600; - text-transform: uppercase; + text-transform: lowercase; letter-spacing: .2rem; color: $color-text; -} - -.pageHeadingMini { - @extend .pageHeading; - font-size: clamp(1.5rem, 2vw, 4rem); - margin-bottom: 1.5rem; + margin-bottom: 2rem; } .text {