Skip to content

feat: add Markdown export for assessment reports - #26

Merged
Yuvraj-Sarathe merged 4 commits into
Yuvraj-Sarathe:mainfrom
ishhwarrii:feature/export-assessment-markdown
Jul 30, 2026
Merged

feat: add Markdown export for assessment reports#26
Yuvraj-Sarathe merged 4 commits into
Yuvraj-Sarathe:mainfrom
ishhwarrii:feature/export-assessment-markdown

Conversation

@ishhwarrii

@ishhwarrii ishhwarrii commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

🔗 Related Issue

Closes #23


📝 Description of Changes

What's changed

  • Added a reusable assessmentToMarkdown() helper to serialize assessment data into a clean Markdown format.
  • Implemented client-side Markdown export for assessment reports without introducing any external dependencies.
  • Added an Export as Markdown button near the existing Compare Candidates button on the assessment page.
  • Included relevant assessment sections such as summary, SWOT analysis, metrics, timeline, repository assessments, and the mentorship plan (Developer Mode only).
  • Generated downloadable Markdown files using the browser Blob API.

🏷️ Proposed Labels

  • UI/UX
  • Documentation
  • CI/CD
  • Backend Logic
  • Anything else

📂 Core Files Changed

  • lib/exportMarkdown.ts
  • Assessment page/component
  • Related UI components for the export button

📸 Verification & Screenshots

  • UI/UX (User Interface / User Experience — how it looks and feels):

    • Verified that the Export as Markdown button appears correctly alongside the existing action buttons.
    • Confirmed that clicking the button downloads a properly formatted .md file.
    • Validated the exported content for both Employer Mode and Developer Mode.
    • Added screenshots of the updated interface and an example exported Markdown file.
  • CI/CD (Continuous Integration / Continuous Deployment — the automated build/test pipeline):

    • No CI/CD changes.

🤖 AI Assistance Declaration

Did you use an AI tool to write or assist with this code OR Pull Request?

  • Yes
  • No

⚠️ IF YOU CHECKED "YES", YOU MUST ANSWER THE FOLLOWING:

  • Which AI Model did you use?

    • Claude Sonnet 5
  • Which Platform/Tool?

    • Claude
  • What exactly did the AI do?

    • Assisted with implementation ideas, Markdown formatting suggestions, and code structure for the export functionality.
  • What exactly did YOU do?

    • Implemented the feature, integrated it into the existing assessment page, tested the Markdown output, verified downloads, and ensured compatibility with both Employer and Developer modes.
  • What is the advantage of using this AI approach here?

    • It accelerated development by providing implementation guidance while all coding, integration, testing, and validation were completed manually.

⚠️ Reviewer Notes

  • The feature is implemented entirely on the client side and does not require any server changes or additional dependencies.
  • Please verify the formatting of the exported Markdown file across different assessment types and confirm that all available sections are exported correctly.

✅ The "I Swear I Didn't Break Anything" Pledge

  • I have thoroughly tested these changes in my own local branch.
  • I verified multiple times that this code compiles into a standalone build and does not break existing production features.

Summary by CodeRabbit

  • New Features

    • Added the ability to export assessments as downloadable Markdown reports.
    • Reports include profile details, assessment results, insights, metrics, and relevant recommendations.
    • Added clear export progress feedback and error notifications.
    • Generated filenames are safe and tailored to the assessment mode.
  • Tests

    • Added automated coverage for Markdown content, filenames, conditional sections, and browser downloads.
    • Added Vitest testing support and scripts.

Adds a pure assessmentToMarkdown() helper (lib/exportMarkdown.ts) that
serializes an AssessmentResult + the GitHub profile it was generated
from into a clean, shareable Markdown report: profile header, executive
summary, hirability verdict, career timeline, SWOT, metrics, advanced AI
insights, per-repo assessments (including keyHighlights/redFlags, which
existed on the type but weren't surfaced in the UI before), the full
detailed report, and (developer mode only) the mentorship plan.

Wires up a small 'Export as Markdown' button next to the existing
'Compare Candidates' button on the assessment page. It's visible in
both Employer and Developer mode (unlike Compare, which is
employer-only), since the issue calls out both audiences. Clicking it
builds a Blob client-side and triggers a download -- no server
involved, consistent with the app's 100%-client-side architecture.

Filename pattern: gitdeep-<username>-<mode>-report.md.
@vercel

vercel Bot commented Jul 27, 2026

Copy link
Copy Markdown

@ishhwarrii is attempting to deploy a commit to the Yuvraj Sarathe's projects Team on Vercel.

A member of the Team first needs to authorize it.

@sourcery-ai

sourcery-ai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

🧙 Sourcery has finished reviewing your pull request!


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3c5cb6a5-d662-49b0-819f-b0e154565c48

📥 Commits

Reviewing files that changed from the base of the PR and between a6ab1d1 and b06576b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • lib/exportMarkdown.test.ts
  • package.json
  • vitest.config.ts

📝 Walkthrough

Walkthrough

The assessment page now exports current GitHub and AI assessment data as a mode-specific Markdown file, with formatted report sections, filename sanitization, browser download handling, completion feedback, error alerts, and Vitest coverage.

Changes

Markdown assessment export

Layer / File(s) Summary
Assessment report generation
lib/exportMarkdown.ts
Defines the export data contract, assembles optional and mode-specific assessment sections, formats Markdown-safe content, creates filenames, and handles browser downloads.
Assessment-page export flow
app/assessment/page.tsx
Adds the sidebar export action, invokes Markdown generation and download helpers, tracks temporary completion state, and alerts on failures.
Export testing and configuration
lib/exportMarkdown.test.ts, vitest.config.ts, package.json
Adds Vitest setup, scripts, dependencies, and tests for report output, filename sanitization, and browser download behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant AssessmentPage
  participant assessmentToMarkdown
  participant downloadMarkdown
  participant Browser
  AssessmentPage->>assessmentToMarkdown: generate Markdown from current assessment
  assessmentToMarkdown-->>AssessmentPage: return report content
  AssessmentPage->>downloadMarkdown: pass filename and content
  downloadMarkdown->>Browser: trigger file download
  Browser-->>AssessmentPage: complete export and show feedback
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding Markdown export for assessment reports.
Linked Issues check ✅ Passed The PR implements the requested client-side Markdown export button, helper, filename pattern, and developer-mode content for issue #23.
Out of Scope Changes check ✅ Passed The changes stay focused on Markdown export and its test setup, with no unrelated feature work evident.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Fix failing CI checks
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

lib/exportMarkdown.test.ts

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)

package.json

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)

vitest.config.ts

Oops! Something went wrong! :(

ESLint: 9.39.1

TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Object'
| property 'configs' -> object with constructor 'Object'
| property 'flat' -> object with constructor 'Object'
| ...
| property 'plugins' -> object with constructor 'Object'
--- property 'react' closes the circle
Referenced from: /.eslintrc.json
at JSON.stringify ()
at /node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2255:45
at Array.map ()
at ConfigValidator.formatErrors (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2246:23)
at ConfigValidator.validateConfigSchema (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:2277:84)
at ConfigArrayFactory._normalizeConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3123:19)
at ConfigArrayFactory._loadConfigData (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3088:21)
at ConfigArrayFactory._loadExtendedShareableConfig (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3393:21)
at ConfigArrayFactory._loadExtends (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3261:25)
at ConfigArrayFactory._normalizeObjectConfigDataBody (/node_modules/@eslint/eslintrc/dist/eslintrc.cjs:3199:25)
(node:2) ESLintRCWarning: You are using an eslintrc configuration file, which is deprecated and support will be removed in v10.0.0. Please migrate to an eslint.config.js file. See https://eslint.org/docs/latest/use/configure/migration-guide for details. An eslintrc configuration file is used because you have the ESLINT_USE_FLAT_CONFIG environment variable set to false. If you want to use an eslint.config.js file, remove the environment variable. If you want to find the location of the eslintrc configuration file, use the --debug flag.
(Use node --trace-warnings ... to show where the warning was created)


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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've left some high level feedback:

  • The assessmentToMarkdown helper currently embeds new Date().toLocaleString() directly, which makes output non-deterministic and harder to test; consider passing a generated timestamp into the function or allowing an optional generatedAt override for stable fixture-based tests.
  • Using alert in handleExportMarkdown is a bit jarring relative to the rest of the UI; consider surfacing export errors via an inline toast or status message in the existing layout instead of a blocking browser alert.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `assessmentToMarkdown` helper currently embeds `new Date().toLocaleString()` directly, which makes output non-deterministic and harder to test; consider passing a generated timestamp into the function or allowing an optional `generatedAt` override for stable fixture-based tests.
- Using `alert` in `handleExportMarkdown` is a bit jarring relative to the rest of the UI; consider surfacing export errors via an inline toast or status message in the existing layout instead of a blocking browser alert.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
app/assessment/page.tsx (1)

432-448: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Announce the transient "Exported!" state to assistive tech.

The label swap is purely visual — aria-label stays constant, so screen reader users get no confirmation that the download fired. A small live region (or a dynamic aria-label) closes the gap.

♻️ Proposed change
             <button
               onClick={handleExportMarkdown}
-              aria-label="Export this assessment report as a Markdown file"
+              aria-label={exported ? 'Report exported as Markdown' : 'Export this assessment report as a Markdown file'}
               className="w-full flex items-center justify-center gap-2 bg-[`#21262D`] hover:bg-[`#30363D`] border border-[`#30363D`] text-[`#C9D1D9`] text-xs font-bold py-3 px-4 rounded-lg transition-colors uppercase tracking-widest"
             >
@@
             </button>
+            <span role="status" aria-live="polite" className="sr-only">
+              {exported ? 'Report exported as Markdown' : ''}
+            </span>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/assessment/page.tsx` around lines 432 - 448, Update the export control
around handleExportMarkdown so the transient exported state is announced to
assistive technologies. Add a dynamically updated aria-label or a small polite
live region tied to exported, while preserving the existing visual label swap
and button behavior.
lib/exportMarkdown.ts (1)

270-280: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Schedule revokeObjectURL for the next tick.

Revoking the blob URL immediately after link.click() can cause Safari/WebKit downloads to fail because the browser may still need the object URL in the current tick. Move cleanup into setTimeout(..., 0) so the download can start before revocation.

♻️ Proposed change
   document.body.appendChild(link);
   link.click();
   document.body.removeChild(link);
-  URL.revokeObjectURL(url);
+  setTimeout(() => URL.revokeObjectURL(url), 0);
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/exportMarkdown.ts` around lines 270 - 280, Update downloadMarkdown so
URL.revokeObjectURL runs inside a zero-delay setTimeout after the link is
clicked and removed, allowing the download to start before cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/exportMarkdown.ts`:
- Around line 45-48: Update formatAccountAge to detect an invalid parsed
createdAt date before calculating the year difference, and fall back to 0 years
for unparsable values while preserving the existing singular/plural formatting.

---

Nitpick comments:
In `@app/assessment/page.tsx`:
- Around line 432-448: Update the export control around handleExportMarkdown so
the transient exported state is announced to assistive technologies. Add a
dynamically updated aria-label or a small polite live region tied to exported,
while preserving the existing visual label swap and button behavior.

In `@lib/exportMarkdown.ts`:
- Around line 270-280: Update downloadMarkdown so URL.revokeObjectURL runs
inside a zero-delay setTimeout after the link is clicked and removed, allowing
the download to start before cleanup.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: ac27deeb-0ab7-4988-be94-41e5ec0afcc6

📥 Commits

Reviewing files that changed from the base of the PR and between 0452800 and 495d9eb.

📒 Files selected for processing (2)
  • app/assessment/page.tsx
  • lib/exportMarkdown.ts

Comment thread lib/exportMarkdown.ts
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Yuvraj-Sarathe

Copy link
Copy Markdown
Owner

@ishhwarrii please follow the given PR template and also link the issue this PR is for.

@ishhwarrii

Copy link
Copy Markdown
Contributor Author

@Yuvraj-Sarathe Done! I've updated the PR to follow the required template and linked the related issue. Thanks for the reminder!

@Yuvraj-Sarathe Yuvraj-Sarathe added enhancement New feature or request backend Deals with backend problems frontend Deals with UI/UX components ECSoC26 Elite Coders Summer of Camp merged ECSoC26-L3 level: advanced good-backend labels Jul 27, 2026

@Yuvraj-Sarathe Yuvraj-Sarathe left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Make sure linting tests are passed. You can check the exact logs from workflow in conversation tab or check the issue on exact code block in Files changed tab.

@Yuvraj-Sarathe
Yuvraj-Sarathe merged commit 0f4a6f4 into Yuvraj-Sarathe:main Jul 30, 2026
2 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend Deals with backend problems ECSoC26-L3 ECSoC26 Elite Coders Summer of Camp ecsoc-hard enhancement New feature or request frontend Deals with UI/UX components good-backend good-pr good-ui level: advanced merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] Export assessment report as Markdown

2 participants