Skip to content

Add Markdown download for work experience#259

Open
jayvicsanantonio wants to merge 1 commit into
mainfrom
feat/work-experience-download-12196215783298108633
Open

Add Markdown download for work experience#259
jayvicsanantonio wants to merge 1 commit into
mainfrom
feat/work-experience-download-12196215783298108633

Conversation

@jayvicsanantonio

Copy link
Copy Markdown
Owner

Implemented a new feature that allows users to download the full work experience in Markdown format from the /work page.

Key changes:

  1. Data Centralization: Moved hardcoded work experience data from WorkTimeline.tsx to app/work/_data/experiences.ts to ensure consistency across the UI, SEO metadata, and the download functionality.
  2. Iconography: Added a new download icon to the standard Icon primitive component.
  3. Download Logic: Created a client-side DownloadExperience component that dynamically generates a Markdown file based on the centralized data and triggers a browser download.
  4. UI Integration: Placed a subtle, elegant download button next to the "Professional Experience" heading. The button features a minimalist design with glassmorphism and subtle hover animations consistent with the site's aesthetic.
  5. Verification: Verified the feature with automated Playwright tests, confirming the button is functional, the generated content is correct, and the UI is responsive.

PR created automatically by Jules for task 12196215783298108633 started by @jayvicsanantonio

- Refactor work experience data into a shared data file.
- Implement a DownloadExperience component to export work history.
- Add a download icon to the Icon primitive.
- Integrate the download button into the /work page UI.
- Ensure consistent styling and mobile responsiveness.

Co-authored-by: jayvicsanantonio <20731321+jayvicsanantonio@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented May 12, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
jayvicsanantonio-dev Ready Ready Preview May 12, 2026 1:27am

@coderabbitai

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jayvicsanantonio has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 41 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9a6d0662-e35d-40fa-85ca-1114e6033025

📥 Commits

Reviewing files that changed from the base of the PR and between 7740ecf and 1ced58f.

📒 Files selected for processing (6)
  • app/work/_components/DownloadExperience.tsx
  • app/work/_components/WorkPageContent.tsx
  • app/work/_components/WorkTimeline.tsx
  • app/work/_data/experiences.ts
  • app/work/page.tsx
  • components/primitives/Icon.tsx
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/work-experience-download-12196215783298108633

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a feature to download work experience as a Markdown file. Key changes include the creation of a DownloadExperience component, the centralization of experience data into a shared _data file to eliminate redundancy, and the addition of a new download icon. Review feedback suggests optimizing the Markdown generation by pre-computing static content and enhancing accessibility by marking decorative UI elements as hidden from screen readers.

Comment on lines +8 to +31
const handleDownload = () => {
const markdown = EXPERIENCES.map((exp) => {
return `## ${exp.title} | ${exp.company}
${exp.period}

${exp.bullets.map((bullet) => `- ${bullet}`).join("\n")}

**Tags:** ${exp.tags.join(", ")}
`;
}).join("\n---\n\n");

const header = `# Jayvic San Antonio - Professional Experience\n\n`;
const fullMarkdown = header + markdown;

const blob = new Blob([fullMarkdown], { type: "text/markdown" });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "jayvic-san-antonio-experience.md";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The handleDownload function contains logic that can be optimized for both performance and maintainability. Since the EXPERIENCES data is static and imported at the module level, the Markdown string generation can be extracted and pre-computed outside the component. This avoids re-calculating the string on every click. Additionally, resource cleanup for the created object URL should be wrapped in a try...finally block to ensure URL.revokeObjectURL is called even if an error occurs during the download trigger.

const MARKDOWN_CONTENT = `# Jayvic San Antonio - Professional Experience\n\n` +
  EXPERIENCES.map((exp) => {
    return `## ${exp.title} | ${exp.company}\n${exp.period}\n\n${exp.bullets.map((bullet) => `- ${bullet}`).join("\n")}\n\n**Tags:** ${exp.tags.join(", ")}`;
  }).join("\n\n---\n\n");

export default function DownloadExperience() {
  const handleDownload = () => {
    const blob = new Blob([MARKDOWN_CONTENT], { type: "text/markdown" });
    const url = URL.createObjectURL(blob);
    try {
      const link = document.createElement("a");
      link.href = url;
      link.download = "jayvic-san-antonio-experience.md";
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    } finally {
      URL.revokeObjectURL(url);
    }
  };

className="transition-transform group-hover:-translate-y-0.5"
/>
<span>Download .md</span>
<span className="absolute -bottom-px left-1/2 h-px w-0 -translate-x-1/2 bg-linear-to-r from-transparent via-cyan-300/50 to-transparent transition-all group-hover:w-2/3" />

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The decorative span used for the hover underline should be hidden from assistive technologies to avoid cluttering the accessibility tree.

Suggested change
<span className="absolute -bottom-px left-1/2 h-px w-0 -translate-x-1/2 bg-linear-to-r from-transparent via-cyan-300/50 to-transparent transition-all group-hover:w-2/3" />
<span aria-hidden="true" className="absolute -bottom-px left-1/2 h-px w-0 -translate-x-1/2 bg-linear-to-r from-transparent via-cyan-300/50 to-transparent transition-all group-hover:w-2/3" />

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