diff --git a/components/experience-card.tsx b/components/experience-card.tsx deleted file mode 100644 index 9ec057e..0000000 --- a/components/experience-card.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Job } from "@/lib/types"; -import { CircleUserRound, Building, Clock, Microchip } from "lucide-react"; -import Chip from "./chip"; -import formatDate from "@/utils/format-date"; - -const ExperienceCard = ({ job }: { job: Job }) => ( -
-

- {job.position} -

-
- {job.company} -
-
- - {formatDate(job.start_date)} -{" "} - {job.still_working ? "Present" : formatDate(job.end_date!)} -
-

- Technologies -

-
- {job.technologies.map((tech) => ( - - ))} -
-
-); - -export default ExperienceCard; diff --git a/components/experience-timeline.tsx b/components/experience-timeline.tsx new file mode 100644 index 0000000..5bb24ba --- /dev/null +++ b/components/experience-timeline.tsx @@ -0,0 +1,83 @@ +import { Job } from "@/lib/types"; +import Chip from "./chip"; +import formatDate from "@/utils/format-date"; + +interface ExperienceTimelineProps { + jobs: Job[]; + newestFirst?: boolean; +} + +const ExperienceTimeline = ({ jobs, newestFirst = true }: ExperienceTimelineProps) => { + const sorted = [...jobs].sort((a, b) => { + const diff = new Date(b.start_date).getTime() - new Date(a.start_date).getTime(); + return newestFirst ? diff : -diff; + }); + + return ( +
+
+ {sorted.map((job) => ( +
+
+
+
+
+
+

{job.position}

+ + {formatDate(job.start_date)} –{" "} + {job.still_working ? "Present" : job.end_date ? formatDate(job.end_date) : "—"} + +
+

{job.company}

+ + {job.summary && ( +

{job.summary}

+ )} + + {job.sub_phases && job.sub_phases.length > 0 && ( +
+ {job.sub_phases.map((phase) => ( +
+
+ + {phase.label} + + + {formatDate(phase.start_date)} –{" "} + {phase.end_date ? formatDate(phase.end_date) : "Present"} + +
+
    + {phase.bullets.map((b, i) => ( +
  • + {b} +
  • + ))} +
+
+ ))} +
+ )} + +
+

+ Technologies +

+
+ {job.technologies.map((tech) => ( + + ))} +
+
+
+
+ ))} +
+ ); +}; + +export default ExperienceTimeline; diff --git a/components/experience.tsx b/components/experience.tsx index 18f939f..4d3f710 100644 --- a/components/experience.tsx +++ b/components/experience.tsx @@ -1,22 +1,16 @@ import { Job } from "@/lib/types"; -import ExperienceCard from "@/components/experience-card"; +import ExperienceTimeline from "@/components/experience-timeline"; -const Experience = ({ jobs }: { jobs: Job[] }) => { - return ( -
-

- Experience -

-
- {jobs.map((job) => ( - - ))} -
-
- ); -}; +const Experience = ({ jobs }: { jobs: Job[] }) => ( +
+

+ Experience +

+ +
+); export default Experience; diff --git a/docs/superpowers/plans/2026-04-24-experience-timeline.md b/docs/superpowers/plans/2026-04-24-experience-timeline.md new file mode 100644 index 0000000..a56f57e --- /dev/null +++ b/docs/superpowers/plans/2026-04-24-experience-timeline.md @@ -0,0 +1,378 @@ +# Experience Timeline Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace the horizontal experience card carousel with a full-width vertical timeline that surfaces job descriptions and sub-phase breakdowns from static data. + +**Architecture:** Extend the `Job` type with optional `summary` and `sub_phases` fields, update `data.ts` with CV content, then replace the card carousel with a new `ExperienceTimeline` server component. No client-side JS — all content is statically rendered. + +**Tech Stack:** Next.js (App Router), TypeScript, TailwindCSS + +--- + +## File Map + +| Action | File | Responsibility | +|--------|------|---------------| +| Modify | `lib/types.ts` | Add `JobSubPhase`, extend `Job` | +| Modify | `lib/data.ts` | Add `summary` + `sub_phases` to WPP and GIAP entries | +| Create | `components/experience-timeline.tsx` | Renders the full vertical timeline (server component) | +| Modify | `components/experience.tsx` | Swap card loop for `` | +| Delete | `components/experience-card.tsx` | Replaced by timeline component | + +--- + +### Task 1: Extend types + +**Files:** +- Modify: `lib/types.ts` + +- [ ] **Step 1: Add `JobSubPhase` and extend `Job`** + +Replace the entire contents of `lib/types.ts` with: + +```typescript +export interface Skill { + name: string; +} + +export interface JobSubPhase { + label: string; + start_date: string; + end_date: string | null; + bullets: string[]; +} + +export interface Job { + id: number; + position: string; + company: string; + still_working: boolean; + start_date: string; + end_date: string | null; + technologies: string[]; + summary?: string; + sub_phases?: JobSubPhase[]; +} + +export interface Project { + id: number; + name: string; + short_description: string; + description: string; + technologies: string[]; + thumbnails: string[]; + category: 'Web' | 'Mobile' | 'Desktop' | 'Api' | 'Game'; + github_url?: string | null; + visit_url?: string | null; + download_url?: string | null; + commercial?: boolean; +} +``` + +- [ ] **Step 2: Verify TypeScript is happy** + +```bash +npx tsc --noEmit +``` + +Expected: no errors (existing job entries have no `sub_phases` yet, so optional fields are fine). + +- [ ] **Step 3: Commit** + +```bash +git add lib/types.ts +git commit -m "feat: add JobSubPhase type and extend Job with summary and sub_phases" +``` + +--- + +### Task 2: Update job data + +**Files:** +- Modify: `lib/data.ts` + +- [ ] **Step 1: Update the WPP entry (id: 8)** + +Replace the WPP job object in the `jobs` array: + +```typescript +{ + id: 8, + position: "Software Engineer", + company: "WPP Media | Choreograph | Wavemaker", + still_working: true, + start_date: "2023-09-13", + end_date: null, + summary: + "Advanced from frontend UI development to backend systems engineering, leading infrastructure-agnostic design initiatives.", + sub_phases: [ + { + label: "Backend & Infrastructure", + start_date: "2025-03-01", + end_date: null, + bullets: [ + "Engineered and optimized backend applications and internal tools using Python, FastAPI, and Django.", + "Streamlined CI/CD workflows and containerized applications with GitLab Pipelines, Docker, and Kubernetes across GCP and Azure environments.", + ], + }, + { + label: "Frontend Architecture", + start_date: "2023-09-13", + end_date: "2025-03-01", + bullets: [ + "Architected scalable microfrontends utilizing Angular and Module Federation, seamlessly integrating standalone internal tools into a unified enterprise shell application.", + "Ensured seamless integration of UI components with Kubernetes-based deployments and Azure Pipelines.", + ], + }, + ], + technologies: [ + "Angular", + "Azure", + "Azure Pipelines", + "Django", + "Docker", + "FastAPI", + "GCP", + "Gitlab CI", + "Gitlab Pipelines", + "Kubernetes", + "PostgreSQL", + "Python", + "React", + "SCSS", + "TailwindCSS", + "Typescript", + ], +}, +``` + +- [ ] **Step 2: Update the GIAP entry (id: 2)** + +Replace the GIAP job object: + +```typescript +{ + id: 2, + position: "Full Stack Developer", + company: "GIAP", + still_working: false, + start_date: "2021-05-19", + end_date: "2023-02-03", + sub_phases: [ + { + label: "Desktop / Backend", + start_date: "2021-05-19", + end_date: "2022-02-01", + bullets: [ + "Architected and optimized complex PostGIS/PostgreSQL cross-database comparison queries utilizing Common Table Expressions (CTEs), drastically reducing execution time from over 5 minutes to under 15 seconds.", + "Developed a robust GIS data assertion module using Python and Qt to automatically validate spatial data against strict compliance standards.", + ], + }, + { + label: "Frontend", + start_date: "2022-02-01", + end_date: "2023-02-03", + bullets: [ + "Engineered a comprehensive, public-facing web application for the City of Gdańsk (geogdansk.pl) leveraging React, TypeScript, Redux, and the ArcGIS JS API.", + ], + }, + ], + technologies: [ + "Python", + "React", + "Typescript", + "PostgreSQL", + "PostGIS", + "ArcGIS JS API", + "Redux", + "Qt", + "QGIS", + "Git", + ], +}, +``` + +- [ ] **Step 3: Verify TypeScript is happy** + +```bash +npx tsc --noEmit +``` + +Expected: no errors. + +- [ ] **Step 4: Commit** + +```bash +git add lib/data.ts +git commit -m "feat: add sub_phases and summary to WPP and GIAP job entries" +``` + +--- + +### Task 3: Create ExperienceTimeline component + +**Files:** +- Create: `components/experience-timeline.tsx` + +- [ ] **Step 1: Create the component** + +```tsx +import { Job } from "@/lib/types"; +import Chip from "./chip"; +import formatDate from "@/utils/format-date"; + +const ExperienceTimeline = ({ jobs }: { jobs: Job[] }) => ( +
+
+ {jobs.map((job) => ( +
+
+
+
+
+
+

{job.position}

+ + {formatDate(job.start_date)} –{" "} + {job.still_working ? "Present" : formatDate(job.end_date!)} + +
+

{job.company}

+ + {job.summary && ( +

{job.summary}

+ )} + + {job.sub_phases && job.sub_phases.length > 0 && ( +
+ {job.sub_phases.map((phase) => ( +
+
+ + {phase.label} + + + {formatDate(phase.start_date)} –{" "} + {phase.end_date ? formatDate(phase.end_date) : "Present"} + +
+
    + {phase.bullets.map((b) => ( +
  • + {b} +
  • + ))} +
+
+ ))} +
+ )} + +
+

+ Technologies +

+
+ {job.technologies.map((tech) => ( + + ))} +
+
+
+
+ ))} +
+); + +export default ExperienceTimeline; +``` + +- [ ] **Step 2: Verify TypeScript is happy** + +```bash +npx tsc --noEmit +``` + +Expected: no errors. + +- [ ] **Step 3: Commit** + +```bash +git add components/experience-timeline.tsx +git commit -m "feat: add ExperienceTimeline server component" +``` + +--- + +### Task 4: Wire up timeline in Experience section and remove old card + +**Files:** +- Modify: `components/experience.tsx` +- Delete: `components/experience-card.tsx` + +- [ ] **Step 1: Replace `experience.tsx`** + +```tsx +import { Job } from "@/lib/types"; +import ExperienceTimeline from "@/components/experience-timeline"; + +const Experience = ({ jobs }: { jobs: Job[] }) => ( +
+

+ Experience +

+ +
+); + +export default Experience; +``` + +- [ ] **Step 2: Delete the old card component** + +```bash +rm components/experience-card.tsx +``` + +- [ ] **Step 3: Verify no remaining imports of ExperienceCard** + +```bash +grep -r "experience-card\|ExperienceCard" /mnt/drive/dev/gabrielkaszewski-next --include="*.tsx" --include="*.ts" | grep -v node_modules +``` + +Expected: no output. + +- [ ] **Step 4: Verify TypeScript is happy** + +```bash +npx tsc --noEmit +``` + +Expected: no errors. + +- [ ] **Step 5: Start dev server and visually verify the timeline renders correctly** + +```bash +npm run dev +``` + +Open `http://localhost:3000` and scroll to the Experience section. Verify: +- Vertical line and dots are visible +- WPP shows summary + two sub-phase blocks with bullets +- digimonkeys.com shows only dates + chips (no sub-phases) +- GIAP shows two sub-phase blocks with bullets +- Tech chips render under each entry + +- [ ] **Step 6: Commit** + +```bash +git add components/experience.tsx +git commit -m "feat: replace experience card carousel with vertical timeline" +``` diff --git a/docs/superpowers/specs/2026-04-24-experience-timeline-design.md b/docs/superpowers/specs/2026-04-24-experience-timeline-design.md new file mode 100644 index 0000000..d0df0be --- /dev/null +++ b/docs/superpowers/specs/2026-04-24-experience-timeline-design.md @@ -0,0 +1,87 @@ +# Experience Section — Vertical Timeline Redesign + +## Overview + +Replace the horizontal card carousel in the Experience section with a full-width vertical timeline. The new design surfaces job descriptions and sub-phase breakdowns directly from static data — no JavaScript required. + +## Constraints + +- No client-side JavaScript. All content is statically rendered. +- Data lives in `lib/data.ts`; the `Job` type in `lib/types.ts` is extended to support the new fields. + +## Data Model Changes + +### `lib/types.ts` + +Add `JobSubPhase` and extend `Job`: + +```typescript +export interface JobSubPhase { + label: string; + start_date: string; + end_date: string | null; // null = present + bullets: string[]; +} + +export interface Job { + id: number; + position: string; + company: string; + still_working: boolean; + start_date: string; + end_date: string | null; + technologies: string[]; + summary?: string; // optional one-liner shown below company name + sub_phases?: JobSubPhase[]; // optional; if absent, no bullets shown +} +``` + +### `lib/data.ts` + +Update all three job entries: + +**WPP** — add `summary` + two sub-phases: +- Backend & Infrastructure (Mar 2025 – Present) +- Frontend Architecture (Sep 2023 – Mar 2025) + +**digimonkeys.com** — no changes to content (no summary, no sub_phases). + +**GIAP** — add two sub-phases: +- Desktop / Backend (May 2021 – Feb 2022) +- Frontend (Feb 2022 – Feb 2023) + +Bullet text sourced from CV verbatim (slightly trimmed for display). + +## Component Changes + +### Remove + +- `components/experience-card.tsx` — replaced entirely. + +### Add + +- `components/experience-timeline.tsx` — renders the full timeline list as a server component. Accepts `jobs: Job[]`. Renders each entry as a timeline card with dot, vertical line, header, optional sub-phases, and tech chips. Pure JSX, no `useState`/`useEffect`. + +### Update + +- Wherever the Experience section renders `ExperienceCard` in a scroll container — replace with ``. Remove the horizontal scroll wrapper. + +## Visual Design + +- Vertical connecting line: left-aligned, gradient from accent color to transparent. +- Each entry: glassmorphism card (`bg-white/5`, `border-white/10`, `rounded-2xl`), matching the existing site style. +- Sub-phases rendered as nested cards inside the entry card (`bg-white/[0.03]`, `border-white/[0.07]`, `rounded-xl`). +- Sub-phase header: label in small uppercase accent color + date right-aligned. +- Bullets: `
    ` with `text-sm text-white/60`. +- Tech chips: existing `` component reused. +- Divider line between summary/phases and chips: `border-t border-white/10`. + +## Entries (final) + +| Job | Sub-phases | Bullets | +|-----|-----------|---------| +| WPP (Sep 2023–Present) | Backend & Infra, Frontend Arch | Yes | +| digimonkeys.com (May 2021–Present) | None | No | +| GIAP (May 2021–Feb 2023) | Desktop/Backend, Frontend | Yes | + +Freelance period (Feb 2023–Sep 2023) is intentionally excluded. diff --git a/lib/data.ts b/lib/data.ts index 20bc2c3..79d9270 100644 --- a/lib/data.ts +++ b/lib/data.ts @@ -68,6 +68,25 @@ export const jobs: Job[] = [ still_working: false, start_date: "2021-05-19", end_date: "2023-02-03", + sub_phases: [ + { + label: "Frontend", + start_date: "2022-02-01", + end_date: "2023-02-03", + bullets: [ + "Engineered a comprehensive, public-facing web application for the City of Gdańsk (geogdansk.pl) leveraging React, TypeScript, Redux, and the ArcGIS JS API.", + ], + }, + { + label: "Desktop / Backend", + start_date: "2021-05-19", + end_date: "2022-02-01", + bullets: [ + "Architected and optimized complex PostGIS/PostgreSQL cross-database comparison queries utilizing Common Table Expressions (CTEs), drastically reducing execution time from over 5 minutes to under 15 seconds.", + "Developed a robust GIS data assertion module using Python and Qt to automatically validate spatial data against strict compliance standards.", + ], + }, + ], technologies: [ "Python", "React", @@ -105,6 +124,28 @@ export const jobs: Job[] = [ still_working: true, start_date: "2023-09-13", end_date: null, + summary: + "Advanced from frontend UI development to backend systems engineering, leading infrastructure-agnostic design initiatives.", + sub_phases: [ + { + label: "Backend & Infrastructure", + start_date: "2025-03-01", + end_date: null, + bullets: [ + "Engineered and optimized backend applications and internal tools using Python, FastAPI, and Django.", + "Streamlined CI/CD workflows and containerized applications with GitLab Pipelines, Docker, and Kubernetes across GCP and Azure environments.", + ], + }, + { + label: "Frontend Architecture", + start_date: "2023-09-13", + end_date: "2025-03-01", + bullets: [ + "Architected scalable microfrontends utilizing Angular and Module Federation, seamlessly integrating standalone internal tools into a unified enterprise shell application.", + "Ensured seamless integration of UI components with Kubernetes-based deployments and Azure Pipelines.", + ], + }, + ], technologies: [ "Angular", "Azure", diff --git a/lib/types.ts b/lib/types.ts index 2a496aa..514cc0b 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -2,6 +2,13 @@ export interface Skill { name: string; } +export interface JobSubPhase { + label: string; + start_date: string; + end_date: string | null; + bullets: string[]; +} + export interface Job { id: number; position: string; @@ -10,6 +17,8 @@ export interface Job { start_date: string; end_date: string | null; technologies: string[]; + summary?: string; + sub_phases?: JobSubPhase[]; } export interface Project {