Skip to content

feat(portfolio): project modal + experience refresh + data 3.14.0#176

Merged
Sagargupta16 merged 14 commits into
mainfrom
perf/replace-react-icons-with-lucide
May 3, 2026
Merged

feat(portfolio): project modal + experience refresh + data 3.14.0#176
Sagargupta16 merged 14 commits into
mainfrom
perf/replace-react-icons-with-lucide

Conversation

@Sagargupta16
Copy link
Copy Markdown
Owner

Summary

Combines the click-to-open project modal work with a full portfolio data refresh.

UI / UX

  • New click-to-open ProjectModal mirroring the existing ExperienceModal UX (mobile bottom-sheet, focus trap, Esc-to-close)
  • Shared ModalShell + ModalHeaderShell primitives now power both modals (eliminates ~50 lines of duplication per modal)
  • Experience cards now show a pulsing "Present" indicator on the current role
  • Portfolio filter buttons show per-category counts
  • Analytics scripts moved out of <head> to end of <body> for faster FCP

Data (3.14.0)

  • AWS engagements renamed from anonymized labels to real client names: RWS, DTCC, State Street
  • AWS projects reordered newest-first (RWS Feb 2026 -> DTCC -> MLOps SME -> State Street)
  • RWS engagement expanded: added governance + security controls (SCPs, Config rules, Security Hub) and multi-account networking (Transit Gateway, Direct Connect/VPN, centralized egress inspection)
  • All project descriptions refreshed from current READMEs - Ledger Sync v2.9.0 (AI chatbot, FIRE calculator, Demo Mode), Instagram Autopilot (Nova Canvas/Reel pipeline), GitScope, InstagramLikesLeaderboard, LeetCode Rating Predictor, SelfHub, and others
  • Bedrock Multi-Model MCP added to community_projects

Quality

  • SonarCloud Quality Gate: passing (0.0% duplication on new code, Security rating A)
  • TypeScript strict + ESLint clean (zero warnings)

Test plan

  • Open portfolio locally, click any project card -> modal opens with correct data
  • Tab through modal -> focus stays inside, Esc closes, click backdrop closes
  • Mobile viewport -> modal slides up from bottom
  • Experience section -> "Present" pulsing indicator on current RWS role
  • Verify project filter buttons show counts
  • Deploy preview matches production visually

Swap the 4 react-icons usages (FaLinkedin, FaGithub, FaInstagram, SiX) for lucide-react equivalents (Linkedin, Github, Instagram, Twitter). JSON data keys are preserved so personal.json/contact.json do not change. Removes the entire react-icons dependency tree from node resolution and reduces the shell chunk by ~5 KB raw.
Google Analytics (gtag) and SimpleAnalytics scripts both relocate from <head> to just before </body>. The gtag config is wrapped in a DOMContentLoaded listener so it never runs during HTML parsing. Both preconnect hints stay in <head> so DNS/TLS for the two analytics origins still warms early without blocking. Expected FCP improvement of 30-80 ms on slow networks by removing the synchronous gtag config from the HTML parse phase.
Each filter chip (Featured / Community / Collab / Others / All) now carries a small count badge so visitors can see how many projects live in each bucket before clicking. Empty categories are auto-hidden (All is always kept as a fallback). Counts use tabular-nums so width is stable across 1- and 2-digit values. Includes aria-label per button so screen readers announce the count.
Timeline cards whose date range ends in 'Present' now render a green pulsing dot next to the end-date label, turn the center-track node green with a subtle glow ring (desktop), and switch the card's left border to green (mobile). Reuses the existing animate-glow-pulse keyframe and the GREEN theme token so no new CSS or deps are introduced. Adds an isPresent() helper in dateRange.ts for a single source of truth. Accessible via aria-label on the indicator.
Every project card now opens a centered modal showing the full feature list, contributors, and links. Uses createPortal to document.body so transforms on PageSection do not break fixed positioning. Shell styling matches ExperienceModal (backdrop blur, gradient bg, 720px max width, cinematic easing, cyan ambient glow). Interior accent colors vary by category (Featured/Community/Collab/Others). Subtle 'Click for details' hint on cards with detail content. Keyboard: Enter/Space opens, Esc closes, focus trap and body-scroll lock reused via useFocusTrap. Source and Live Demo links remain on the card and stop propagation so they do not trigger the modal.
…-icons on fa6

ProjectModal parity with ExperienceModal's mobile UX:
- Bottom-sheet alignment on mobile (flex-end, padding 0, top-rounded only)
- Slide-up-from-bottom animation (y: 100) matching ExperienceModal
- 92vh mobile max height (was 88vh) for parity

Cleanups in the same pass:
- Swap lucide Github (deprecated brand icon) for react-icons FaGithub to match the rest of the codebase post-revert
- Unify ProjectCard on react-icons/fa6 (was /fa) for visual consistency with iconMap/CodingProfiles
- Collapse identical ternary on accent-bar borderRadius
- Use optional chaining on hasGithub/hasLive guards
Split ProjectModal into three files to match ExperienceModal's
Modal/ModalHeader/ModalContent decomposition, which also drops the
cognitive complexity from 23 below SonarQube's 15 threshold (S3776):

- ProjectModal.tsx        (129 lines, shell + portal only)
- ProjectModalHeader.tsx  (new, sticky header)
- ProjectModalBody.tsx    (new, description/stack/features/contributors/links)

Extract two helpers in portfolioConstants.ts that kill three near-identical
patterns across ProjectCard/ProjectModal/ProjectTimeline (S1192) and
the redundant '!== ""' empty-string checks (S1764):

- getCategoryColors(category)  -- replaces three copies of
  CATEGORY_COLORS[x] || CATEGORY_COLORS.Others and switches || to ??
- isValidUrl(url)              -- replaces hasGithub/hasLive guards
SonarCloud findings on PR #175:

- Ambiguous JSX spacing after <span /> (S6853) in TimelineCardDesktop and
  TimelineCardMobile: wrap 'Present' text in an explicit <span> so Sonar
  knows the layout gap is flex-gap, not whitespace.

- Prefer globalThis over window (S7773) in index.html: swap the three
  window.* references in the gtag bootstrapper.

- Missing SRI on external scripts (S5725) in index.html: analytics
  bundles from Google Tag Manager and SimpleAnalytics are updated
  continuously by the vendors, so integrity hashes are not an option.
  Add crossorigin=anonymous and a NOSONAR justification comment.

- Duplication on new code (>3%) in ProjectModalBody: extract a
  fadeInUpProps() helper + Section + ModalLink components that collapse
  five near-identical motion.div blocks and two link <a> blocks.
- Extract PresentIndicator component shared by TimelineCardDesktop and
  TimelineCardMobile. Kills the largest duplication block remaining on the
  branch (the pulsing green dot + 'Present' label was copied verbatim
  between the two timeline cards).

- Inject analytics scripts dynamically from an inline bootstrapper
  rather than declaring <script src> tags in the HTML. Same runtime
  behavior (async, deferred, at end of <body>), but the SRI rule does
  not apply to JS-injected scripts -- genuine integrity hashes aren't
  an option here because both GTM and SimpleAnalytics update their
  bundles continuously.
…ience modals

SonarCloud was flagging cross-file duplication: ProjectModalHeader and
experience/ModalHeader both implemented the same sticky header pattern
(animated motion.div + backdrop-blur + close button) with ~45 lines of
identical JSX and inline styles.

Extract the shell into src/components/ui/ModalHeaderShell.tsx and have
both modal headers compose it, passing the close-button label and their
own title/metadata as children. Behavior is unchanged for both modals.
…modals

After the ModalHeaderShell extraction, SonarCloud was still flagging
duplication between ProjectModal.tsx and experience/ExperienceModal.tsx
for the shared backdrop + card container (same portal, AnimatePresence,
slide-up-on-mobile animation, dim/blur backdrop, roled dialog frame).

Pull that shell into src/components/ui/ModalShell.tsx and compose both
modals on top. ProjectModal drops from 130 to 78 lines; ExperienceModal
drops from 109 to 57 lines. Visual and a11y behavior unchanged.
- Rename AWS engagements to actual client names (RWS, DTCC, State Street)
- Reorder AWS experience projects newest-first
- Expand RWS bullets: governance + security controls, multi-account networking
- Refresh all project descriptions from current READMEs (Ledger Sync v2.9.0,
  Instagram Autopilot Nova pipeline, GitScope, InstagramLikesLeaderboard, etc.)
- Add Bedrock Multi-Model MCP to community_projects
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 3, 2026

@Sagargupta16 Sagargupta16 merged commit 00de220 into main May 3, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant