From 39c4e0bd83ef16400e8ee66b553ec3e2eeae770b Mon Sep 17 00:00:00 2001 From: Laercio Rios Date: Wed, 25 Sep 2024 08:50:25 -0300 Subject: [PATCH] feat(home): add experience in home --- src/app/assets/icons/arrow-up.svg | 3 + src/app/components/ExperienceCard/index.tsx | 64 ++++++++++++++ .../ExperienceCard/styles.module.css | 37 ++++++++ src/app/globals.css | 2 + src/app/i18n/locales/en.json | 15 ++-- src/app/i18n/locales/pt.json | 19 ++-- src/app/page.module.css | 48 +++++++++++ src/app/page.tsx | 86 +++++++++++++++++-- 8 files changed, 255 insertions(+), 19 deletions(-) create mode 100644 src/app/assets/icons/arrow-up.svg diff --git a/src/app/assets/icons/arrow-up.svg b/src/app/assets/icons/arrow-up.svg new file mode 100644 index 0000000..ea1cf39 --- /dev/null +++ b/src/app/assets/icons/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/components/ExperienceCard/index.tsx b/src/app/components/ExperienceCard/index.tsx index e69de29..64e6e81 100644 --- a/src/app/components/ExperienceCard/index.tsx +++ b/src/app/components/ExperienceCard/index.tsx @@ -0,0 +1,64 @@ +import { useTranslation } from "react-i18next"; + +import ArrowDownSvg from "../../assets/icons/arrow-down.svg"; +import ArrowUpSvg from "../../assets/icons/arrow-up.svg"; +import Typography from "../Typography"; +import styles from "./styles.module.css"; + +interface ExperienceCardProps { + title: string; + company: string; + startDate: Date; + endDate?: Date; + active?: boolean; + hasOpenIcon?: boolean; + onClick?: () => void; +} + +export default function ExperienceCard({ + title, + company, + startDate, + endDate, + active, + hasOpenIcon, + onClick, +}: ExperienceCardProps) { + const { t, i18n } = useTranslation(); + + const startDateFormatted = new Intl.DateTimeFormat(i18n.language, { + month: "2-digit", + year: "numeric", + }).format(startDate); + const endDateFormatted = endDate + ? new Intl.DateTimeFormat(i18n.language, { + month: "2-digit", + year: "numeric", + }).format(endDate) + : t(`general.current.label`); + + return ( +
  • +
    + {`${startDateFormatted} - ${endDateFormatted}`} +
    + {company} + + {t(`data.experiences.${title}.location.label`)} + +
    + + {t(`data.experiences.${title}.position.label`)} + +
    + {hasOpenIcon && ( +
    + {active ? : } +
    + )} +
  • + ); +} diff --git a/src/app/components/ExperienceCard/styles.module.css b/src/app/components/ExperienceCard/styles.module.css index e69de29..4bd465e 100644 --- a/src/app/components/ExperienceCard/styles.module.css +++ b/src/app/components/ExperienceCard/styles.module.css @@ -0,0 +1,37 @@ +.experience { + width: 100%; + padding: 0.5rem; + border-radius: 0.5rem; + display: flex; + align-items: center; + cursor: pointer; +} + +.experience--active { + background-color: var(--primary-transparent); +} + +.experience-content { + width: 100%; + display: flex; + flex-direction: column; + align-items: flex-start; +} + +.experience-company { + display: flex; + align-items: center; + gap: 0.5rem; +} + +.experience-open_icon { + width: 2.5rem; + height: 2.5rem; + display: flex; + align-items: center; + justify-content: center; +} + +.experience-open_icon svg path { + fill: var(--primary); +} diff --git a/src/app/globals.css b/src/app/globals.css index 14a419c..c1e7ca6 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -4,6 +4,7 @@ --tertiary: #e88d8d; --background: #fff4f4; --home: #ebd5d2; + --primary-transparent: #6600001a; --shadow: 4px 8px 16px 0px rgba(0, 0, 0, 0.25); @@ -16,6 +17,7 @@ --tertiary: #804646; --background: #120000; --home: #400000; + --primary-transparent: #fefefe1a; } * { diff --git a/src/app/i18n/locales/en.json b/src/app/i18n/locales/en.json index a5aba95..34acc95 100644 --- a/src/app/i18n/locales/en.json +++ b/src/app/i18n/locales/en.json @@ -8,7 +8,12 @@ } }, "profile": { - "role": "Software Engineer" + "position": "Software Engineer" + }, + "general": { + "current": { + "label": "Current" + } }, "components": { "buyMeACoffee": { @@ -84,7 +89,7 @@ }, "experiences": { "geodatin": { - "role": { + "position": { "label": "Fullstack Developer" }, "location": { @@ -95,11 +100,11 @@ } }, "golfarma": { - "role": { + "position": { "label": "Backend Developer (Intern)" }, "location": { - "label": "Remote/On-site (Feira de Santana, Brazil)" + "label": "Hybrid (Feira de Santana, Brazil)" }, "description": { "label": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet egestas diam. Donec eget convallis ex. Cras nec leo risus. Nunc finibus lorem risus, id fermentum urna tempus eu.\n\nDonec id iaculis dui. Nulla sit amet euismod velit. Donec sed quam sit amet elit eleifend elementum. Morbi pellentesque ligula vel faucibus commodo. Aenean rhoncus varius ligula ut cursus. Duis congue pellentesque rutrum." @@ -178,4 +183,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/app/i18n/locales/pt.json b/src/app/i18n/locales/pt.json index cf83019..9a371f5 100644 --- a/src/app/i18n/locales/pt.json +++ b/src/app/i18n/locales/pt.json @@ -8,7 +8,12 @@ } }, "profile": { - "role": "Desenvolvedor de Software" + "position": "Desenvolvedor de Software" + }, + "general": { + "current": { + "label": "Atual" + } }, "components": { "buyMeACoffee": { @@ -84,25 +89,25 @@ }, "experiences": { "geodatin": { - "role": { + "position": { "label": "Desenvolvedor Fullstack" }, "location": { "label": "Remoto (Feira de Santana - BA)" }, "description": { - "label": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet egestas diam. Donec eget convallis ex. Cras nec leo risus. Nunc finibus lorem risus, id fermentum urna tempus eu.\n\nDonec id iaculis dui. Nulla sit amet euismod velit. Donec sed quam sit amet elit eleifend elementum. Morbi pellentesque ligula vel faucibus commodo. Aenean rhoncus varius ligula ut cursus. Duis congue pellentesque rutrum." + "label": "Geodatin - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet egestas diam. Donec eget convallis ex. Cras nec leo risus. Nunc finibus lorem risus, id fermentum urna tempus eu.\n\nDonec id iaculis dui. Nulla sit amet euismod velit. Donec sed quam sit amet elit eleifend elementum. Morbi pellentesque ligula vel faucibus commodo. Aenean rhoncus varius ligula ut cursus. Duis congue pellentesque rutrum." } }, "golfarma": { - "role": { + "position": { "label": "Desenvolvedor Backend (Estágio)" }, "location": { - "label": "Remoto/Presencial (Feira de Santana - BA)" + "label": "Híbrido (Feira de Santana - BA)" }, "description": { - "label": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet egestas diam. Donec eget convallis ex. Cras nec leo risus. Nunc finibus lorem risus, id fermentum urna tempus eu.\n\nDonec id iaculis dui. Nulla sit amet euismod velit. Donec sed quam sit amet elit eleifend elementum. Morbi pellentesque ligula vel faucibus commodo. Aenean rhoncus varius ligula ut cursus. Duis congue pellentesque rutrum." + "label": "Golfarma - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc sit amet egestas diam. Donec eget convallis ex. Cras nec leo risus. Nunc finibus lorem risus, id fermentum urna tempus eu.\n\nDonec id iaculis dui. Nulla sit amet euismod velit. Donec sed quam sit amet elit eleifend elementum. Morbi pellentesque ligula vel faucibus commodo. Aenean rhoncus varius ligula ut cursus. Duis congue pellentesque rutrum." } } }, @@ -178,4 +183,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/app/page.module.css b/src/app/page.module.css index fba694f..78be6dc 100644 --- a/src/app/page.module.css +++ b/src/app/page.module.css @@ -224,6 +224,54 @@ } } +.experience-content { + display: none; + + @media (min-width: 1024px) { + display: flex; + gap: 2rem; + } +} + +.experience-summary { + width: 100%; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.experience-summary h4, +.experience-summary p { + text-align: left; +} + +.experience-list { + min-width: 22rem; + list-style-type: none; + display: flex; + flex-direction: column; + gap: 1rem; +} + +.experience-nested_list { + list-style-type: none; + display: flex; + flex-direction: column; + gap: 1rem; + + @media (min-width: 1024px) { + display: none; + } +} +.experience-nested_list li { + border-radius: 0.5rem; + background-color: var(--primary-transparent); +} + +.experience-nested_list li > p { + padding: 0.5rem; +} + .formation-list { list-style-type: none; width: 100%; diff --git a/src/app/page.tsx b/src/app/page.tsx index d50b3a7..06cae3a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,22 +1,25 @@ "use client"; import "./i18n"; import Image from "next/image"; -import Link from "next/link"; +// import Link from "next/link"; +import { useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import ArrowDownIcon from "./assets/icons/arrow-down.svg"; -import ArrowRight from "./assets/icons/arrow-right-alt.svg"; +// import ArrowRight from "./assets/icons/arrow-right-alt.svg"; import EmailIcon from "./assets/icons/email-logo.svg"; import GithubIcon from "./assets/icons/github-logo.svg"; import LinkedinIcon from "./assets/icons/linkedin-logo.svg"; import Logo from "./assets/logo.svg"; import profileImg from "./assets/profile.png"; +import ExperienceCard from "./components/ExperienceCard"; import Footer from "./components/Footer"; import FormationCard from "./components/FormationCard"; import Header from "./components/Header"; import PresentationCard from "./components/PresentationCard"; import ProjectCard from "./components/ProjectCard"; import Typography from "./components/Typography"; +import { experiences } from "./data/experiences"; import { formations } from "./data/formations"; import { presentations } from "./data/presentations"; import { projects } from "./data/projects"; @@ -25,8 +28,34 @@ import { GITHUB_LINK, EMAIL_LINK, LINKEDIN_LINK } from "./utils/contrants"; export default function Home() { const { t } = useTranslation(); + const { innerWidth: windowWidth } = window; const projectsHighlighted = projects.filter((project) => project.highlight); + const [experiencesWithActive, setExperiencesWithActive] = useState( + experiences.map((experience, index) => ({ + ...experience, + active: index === 0 && windowWidth > 1024, + })), + ); + + const activeExperienceSummary = useMemo(() => { + const activeExperience = experiencesWithActive.find( + (experience) => experience.active, + ); + return activeExperience + ? t(`data.experiences.${activeExperience.title}.description.label`) + : ""; + }, [experiencesWithActive, t]); + + function handleActiveExperienceChange(title: string) { + const updatedExperiences = experiencesWithActive.map((experience) => ({ + ...experience, + active: + experience.title === title && + (!experience.active || windowWidth > 1024), + })); + setExperiencesWithActive(updatedExperiences); + } return (
    @@ -36,7 +65,7 @@ export default function Home() {
    Laercio Rios - {t("profile.role")} + {t("profile.position")}
    • @@ -80,12 +109,12 @@ export default function Home() { {t("pages.home.projects.title.label")} - + {/* {t("pages.home.projects.seeMore.label")} - + */}
        {projectsHighlighted.map((project) => ( @@ -106,12 +135,55 @@ export default function Home() { {t("pages.home.experience.title.label")} - + {/* {t("pages.home.experience.seeMore.label")} - + */} + +
          + {experiencesWithActive.map((experience) => ( +
        • + handleActiveExperienceChange(experience.title)} + /> + {experience.active && ( + + {t( + `data.experiences.${experience.title}.description.label`, + )} + + )} +
        • + ))} +
        +
        +
        + + {t("pages.home.experience.summary.label")} + + {activeExperienceSummary} +
        +
          + {experiencesWithActive.map((experience) => ( + handleActiveExperienceChange(experience.title)} + /> + ))} +