diff --git a/public/icons/email.png b/public/icons/email.png new file mode 100644 index 0000000..e4e4ad9 Binary files /dev/null and b/public/icons/email.png differ diff --git a/public/icons/github.png b/public/icons/github.png new file mode 100644 index 0000000..90be16f Binary files /dev/null and b/public/icons/github.png differ diff --git a/public/icons/instagram.png b/public/icons/instagram.png new file mode 100644 index 0000000..fcfcc9e Binary files /dev/null and b/public/icons/instagram.png differ diff --git a/public/icons/linkedin.png b/public/icons/linkedin.png new file mode 100644 index 0000000..b22aa05 Binary files /dev/null and b/public/icons/linkedin.png differ diff --git a/public/icons/medium.png b/public/icons/medium.png new file mode 100644 index 0000000..0fcdfd4 Binary files /dev/null and b/public/icons/medium.png differ diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx index 1cd1fdd..673d46f 100644 --- a/src/app/about/page.tsx +++ b/src/app/about/page.tsx @@ -1,8 +1,29 @@ +'use client'; + +import { useState, useEffect } from 'react'; import Image from 'next/image'; import AboutSections from '../components/AboutSections/AboutSections'; import LinkButton from '../components/Button/LinkButton'; +import LoadingSpinner from '../components/Loading/Loading'; export default function About() { + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => { + setIsLoading(false); + }, 1000); // Simulate a loading time, adjust as needed + return () => clearTimeout(timer); + }, []); + + if (isLoading) { + return ( +
+ +
+ ); + } + return (
diff --git a/src/app/components/AboutSections/AboutSections.tsx b/src/app/components/AboutSections/AboutSections.tsx index 0205cf4..6f9f396 100644 --- a/src/app/components/AboutSections/AboutSections.tsx +++ b/src/app/components/AboutSections/AboutSections.tsx @@ -1,6 +1,5 @@ 'use client'; -import React from 'react'; -import { useState } from 'react'; +import React, { useState } from 'react'; const sections = [ { @@ -64,56 +63,62 @@ export default function AboutSections() {

{sections.map((section, index) => ( -
toggleSection(index)} > -
- +

{section.title} - +

toggleSection(index)} - role="button" // Semântica de acessibilidade - aria-expanded={openSection === index} > ▼ -
+ {openSection === index && (
{typeof section.content === 'string' ? ( - - {section.content} - +

{section.content}

) : ( -
+
{React.Children.map( section.content.props.children, (child, childIndex) => ( -

{child} -

+
) )}
)}
)} -
+ ))} ); diff --git a/src/app/components/Button/SkillButton.tsx b/src/app/components/Button/SkillButton.tsx index 87130be..ce14f64 100644 --- a/src/app/components/Button/SkillButton.tsx +++ b/src/app/components/Button/SkillButton.tsx @@ -5,6 +5,7 @@ interface SkillButtonProps { description?: string; isActive: boolean; onClick: () => void; + testId?: string; // Novo parâmetro para test ID } export default function SkillButton({ @@ -12,6 +13,7 @@ export default function SkillButton({ description = 'This is a placeholder description for the skill.', isActive, onClick, + testId, // Recebendo test ID }: SkillButtonProps) { const colorClasses = skillColors[text] || defaultSkillColor; @@ -26,6 +28,7 @@ export default function SkillButton({ alignItems: 'center', justifyContent: 'center', }} + data-test-id={testId} // Adicionando test ID ao container principal > @@ -97,52 +119,101 @@ export default function MediumFeed() { - {/* Posts Grid */} -
+
{filteredPosts.map((post, index) => (
{post.thumbnail && ( -
+
{post.title}
)} -
-

+
+

{post.title}

-

+

{extractFirstParagraph(post.content)}

-

+

{new Date(post.pubDate).toLocaleDateString()} by {post.author}

-
+
{post.categories.map((category, idx) => ( {category} ))}
-
- +
+
))}
-

+
); } diff --git a/src/app/projects/page.tsx b/src/app/projects/page.tsx index c329021..24b5516 100644 --- a/src/app/projects/page.tsx +++ b/src/app/projects/page.tsx @@ -1,65 +1,116 @@ 'use client'; import Image from 'next/image'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import { PROJECTS } from '../content/projects'; import { projectStyles } from '../styles/projectStyles'; import LinkButton from '../components/Button/LinkButton'; +import LoadingSpinner from '../components/Loading/Loading'; export default function ProjectsPage() { + const [isClient, setIsClient] = useState(false); const [openSection, setOpenSection] = useState(null); + useEffect(() => { + setIsClient(true); + }, []); + const toggleSection = (title: string, sectionIndex: number) => { const key = `${title}-${sectionIndex}`; setOpenSection(openSection === key ? null : key); }; + if (!isClient) { + return ( +
+ +
+ ); + } + return ( -
-
- {PROJECTS.map((project) => ( +
+

+ Projects +

+ +
+ {PROJECTS.map((project, projectIndex) => (
{/* Logo Section */} -
+
{`${project.title}
{/* Content Section */} -
-

+
+

{project.title}

-

+

{project.description}

- {project.sections.map((section, index) => ( + {project.sections.map((section, sectionIndex) => (
toggleSection(project.title, index)} + onClick={() => toggleSection(project.title, sectionIndex)} + aria-expanded={ + openSection === `${project.title}-${sectionIndex}` + } + aria-controls={`section-content-${projectIndex}-${sectionIndex}`} + data-test-id={`section-toggle-${projectIndex}-${sectionIndex}`} > {section.title}
- {openSection === `${project.title}-${index}` && ( -
+ {openSection === `${project.title}-${sectionIndex}` && ( +
{section.content}
)} @@ -80,9 +135,18 @@ export default function ProjectsPage() {
{/* Go to Posts Button */} -
- +
+
-
+

); } diff --git a/src/app/resume/page.tsx b/src/app/resume/page.tsx index e5c87b5..38c1302 100644 --- a/src/app/resume/page.tsx +++ b/src/app/resume/page.tsx @@ -1,52 +1,119 @@ 'use client'; + +import { useState, useEffect } from 'react'; import Link from 'next/link'; import { EXPERIENCES } from '../content/experiences'; import LinkButton from '../components/Button/LinkButton'; +import LoadingSpinner from '../components/Loading/Loading'; export default function Resume() { + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => setIsLoading(false), 500); + return () => clearTimeout(timeout); + }, []); + + if (isLoading) { + return ( +
+ +
+ ); + } + return ( -
- {/* Linha do Tempo */} -
+
+

+ Resume Page +

+ + {/* Timeline */} +
{EXPERIENCES.map((item, index) => ( -
- {/* Ano */} -
+
+ {/* Year */} +
{item.year}
-
+ - {/* Detalhes da Vaga */} -
-

+ {/* Job Details */} +
+

{item.company}

-

{item.period}

-

+

+ {item.period} +

+

{item.role}

-

+

{item.shortDescription}

- {/* Centralizar texto e botão */} -
+ {/* Centralize button */} +
Know more -

))}

- {/* Botão Centralizado Abaixo */} -
- + + {/* Button to go to Skills */} +
+
-
+
); } diff --git a/src/app/skills/page.tsx b/src/app/skills/page.tsx index 4a4c9af..5fd6fa5 100644 --- a/src/app/skills/page.tsx +++ b/src/app/skills/page.tsx @@ -1,13 +1,20 @@ 'use client'; -import { useState } from 'react'; +import { useState, useEffect } from 'react'; import SkillButton from '../components/Button/SkillButton'; import { SKILLS } from '../content/skills'; import LinkButton from '../components/Button/LinkButton'; +import LoadingSpinner from '../components/Loading/Loading'; export default function SkillsPage() { - const [selectedTab, setSelectedTab] = useState('All'); // Aba "All" selecionada por padrão + const [selectedTab, setSelectedTab] = useState('All'); // Default tab const [activeSkill, setActiveSkill] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const timeout = setTimeout(() => setIsLoading(false), 500); // Simulating data loading + return () => clearTimeout(timeout); + }, []); const categories = selectedTab === 'All' @@ -21,10 +28,34 @@ export default function SkillsPage() { setActiveSkill((prev) => (prev === skill ? null : skill)); }; + if (isLoading) { + return ( +
+ +
+ ); + } + return ( -
+
+

+ Skills Page +

+ {/* Tabs */} -
+
{tabs.map((tab, index) => ( @@ -43,13 +78,25 @@ export default function SkillsPage() {
{/* Skills */} -
+
{categories.map((subcategory, subIndex) => ( -
-

+
+

{subcategory.name}

-
+
{subcategory.items.map(({ text, description }, itemIndex) => ( handleSkillClick(text)} + testId={`skill-button-${text.toLowerCase()}`} + aria-pressed={activeSkill === text} /> ))}
-
+
))}

- {/* Divisória - Final das Skills */} -
+ {/* Divider */} +
); } diff --git a/src/app/styles/skillStyles.ts b/src/app/styles/skillStyles.ts index 14bbf1e..6ab8dd5 100644 --- a/src/app/styles/skillStyles.ts +++ b/src/app/styles/skillStyles.ts @@ -62,7 +62,7 @@ export const skillColors: Record = { // Interpersonal Skills 'Public Speaking': 'text-purple-500 border-purple-500', 'Didactic Explanations': 'text-purple-500 border-purple-500', - 'Talks on Accessibility and Security': 'text-purple-500 border-purple-500', + Talks: 'text-purple-500 border-purple-500', 'Commitment to Goals': 'text-blue-400 border-blue-400', 'Critical Thinking': 'text-blue-400 border-blue-400', diff --git a/tests/about.spec.ts b/tests/about.spec.ts index 10c4502..6902585 100644 --- a/tests/about.spec.ts +++ b/tests/about.spec.ts @@ -31,7 +31,7 @@ test.describe('About Page', () => { const { title, content } = ABOUT_DATA.sections[i]; await test.step(`Section ${i}: ${title}`, async () => { - await aboutPage.clickToggleIcon(i); + await aboutPage.clickSession(i); await aboutPage.validateSectionTitleVisible(i, title); await aboutPage.validateSectionContentVisible(i, content); }); diff --git a/tests/pages/AboutPage.ts b/tests/pages/AboutPage.ts index a3b3e94..1954e53 100644 --- a/tests/pages/AboutPage.ts +++ b/tests/pages/AboutPage.ts @@ -43,18 +43,16 @@ export class AboutPage { await expect(containerLocator).toBeVisible(); } - async clickToggleIcon(index: number) { - const toggleIcon = this.page.locator( - `[data-test-id="toggle-icon-${index}"]` - ); - await toggleIcon.click(); + async clickSession(index: number) { + const session = this.page.locator(`[data-test-id="section-${index}"]`); + await session.click(); const contentLocator = this.page.locator( `[data-test-id="section-content-${index}"]` ); await this.page.waitForTimeout(300); if (!(await contentLocator.isVisible())) { - await toggleIcon.click(); + await session.click(); await this.page.waitForTimeout(300); } } @@ -76,7 +74,7 @@ export class AboutPage { ); if (!(await contentLocator.isVisible())) { - await this.clickToggleIcon(index); + await this.clickSession(index); } await expect(contentLocator).toBeVisible();