-
Notifications
You must be signed in to change notification settings - Fork 5
feat: copy markdown generated reports from report page #148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds a Markdown export workflow to the report client page: introduces Markdown generation utilities, status/emoji mappings, clipboard copy logic with UI feedback, updates types for status/repo mapping, and integrates a “Copy as markdown” button in the UI. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant UI as ReportClientPage UI
participant MD as Markdown Generator
participant CB as Clipboard API
User->>UI: Click "Copy as markdown"
UI->>MD: generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap)
MD-->>UI: Markdown string
UI->>CB: navigator.clipboard.writeText(markdown)
CB-->>UI: Promise resolved
UI->>UI: Set copied=true (brief visual cue)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. 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. Comment |
There was a problem hiding this comment.
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 (1)
src/components/features/report/report-client-page.tsx (1)
80-127: Markdown generation logic is comprehensive and well-structured.The function properly generates a structured markdown report with:
- Clear header with date range
- Legend section for review decision emojis
- Team-based organization with repository grouping
- Proper sorting (gnolang repos first, then alphabetical)
- Status-based PR listings with proper formatting
Minor improvement suggestion for better readability:
- md += ` - **${pr.title}** `; - md += `([#${pr.number}](${pr.url})) by @${pr.authorLogin}`; - if (pr.reviewDecision) { - md += ` ${reviewDecisionToEmoji[(pr.reviewDecision as keyof typeof reviewDecisionToEmoji)] || ''}`; - } - md += `\n \n`; + const reviewEmoji = pr.reviewDecision ? + reviewDecisionToEmoji[(pr.reviewDecision as keyof typeof reviewDecisionToEmoji)] || '' : + ''; + md += ` - **${pr.title}** ([#${pr.number}](${pr.url})) by @${pr.authorLogin}${reviewEmoji ? ` ${reviewEmoji}` : ''}\n\n`;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/features/report/report-client-page.tsx(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/features/report/report-client-page.tsx (1)
src/utils/schemas.ts (1)
TPullRequest(131-131)
🔇 Additional comments (6)
src/components/features/report/report-client-page.tsx (6)
10-11: LGTM! Imported icons support the markdown functionality.The CheckIcon and CopyIcon imports are properly added to support the visual feedback in the markdown copy button.
43-59: Type definitions look well-structured.The type definitions provide good structure for organizing the data:
RepoStatusMapproperly constrains status keys to the defined unionTeamRepoStatusMapincludes both the data map and afoundAnyflagRepoStatusArrayprovides a clean tuple type for the sorted repo processing
65-78: Status and review decision emoji mappings are well-chosen.The emoji mappings provide good visual representations:
- Status emojis are intuitive (🕒 for waiting, 🚧 for in progress, etc.)
- Review decision emojis follow a clear color pattern (🟢 approved, 🟠 changes requested, 🔵 review required)
- Proper handling of empty review decisions
165-165: State management for copy feedback is appropriate.The
copiedstate provides good user feedback for the clipboard operation.
215-236: Explicit typing improves code clarity.The explicit
TeamRepoStatusMaptyping in theuseMemomakes the code more maintainable and provides better type safety.
281-284: UI implementation for markdown button is clean.The button provides clear visual feedback with appropriate icons and text. The conditional rendering of CheckIcon vs CopyIcon effectively communicates the operation status.
| const handleCopyMarkdown = async () => { | ||
| const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap); | ||
| try { | ||
| await navigator.clipboard.writeText(md); | ||
| setCopied(true); | ||
| setTimeout(() => setCopied(false), 2000); | ||
| } catch (e) { | ||
| setCopied(false); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clipboard implementation needs browser compatibility improvements.
The current implementation only uses the modern navigator.clipboard.writeText() API, which requires HTTPS/secure contexts and may not work in all browsers or deployment scenarios.
Consider implementing a fallback mechanism for better browser compatibility:
const handleCopyMarkdown = async () => {
const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap);
try {
- await navigator.clipboard.writeText(md);
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
+ // Try modern clipboard API first
+ if (navigator.clipboard && window.isSecureContext) {
+ await navigator.clipboard.writeText(md);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ } else {
+ // Fallback for insecure contexts or unsupported browsers
+ const textArea = document.createElement('textarea');
+ textArea.value = md;
+ textArea.style.position = 'absolute';
+ textArea.style.left = '-999999px';
+ document.body.appendChild(textArea);
+ textArea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textArea);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ }
} catch (e) {
+ console.warn('Clipboard operation failed:', e);
setCopied(false);
}
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const handleCopyMarkdown = async () => { | |
| const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap); | |
| try { | |
| await navigator.clipboard.writeText(md); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| } catch (e) { | |
| setCopied(false); | |
| } | |
| }; | |
| const handleCopyMarkdown = async () => { | |
| const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap); | |
| try { | |
| // Try modern clipboard API first | |
| if (navigator.clipboard && window.isSecureContext) { | |
| await navigator.clipboard.writeText(md); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| } else { | |
| // Fallback for insecure contexts or unsupported browsers | |
| const textArea = document.createElement('textarea'); | |
| textArea.value = md; | |
| textArea.style.position = 'absolute'; | |
| textArea.style.left = '-999999px'; | |
| document.body.appendChild(textArea); | |
| textArea.select(); | |
| document.execCommand('copy'); | |
| document.body.removeChild(textArea); | |
| setCopied(true); | |
| setTimeout(() => setCopied(false), 2000); | |
| } | |
| } catch (e) { | |
| console.warn('Clipboard operation failed:', e); | |
| setCopied(false); | |
| } | |
| }; |
🤖 Prompt for AI Agents
In src/components/features/report/report-client-page.tsx around lines 238 to
247, the clipboard write currently only uses navigator.clipboard.writeText which
fails in non-secure or older browsers; add feature detection and a fallback: try
navigator.clipboard.writeText(md) inside a try/catch, and if it throws or
navigator.clipboard is unavailable fall back to creating a hidden textarea, set
its value to md, append it to document.body, select its contents (using
range/selectAll if needed), call document.execCommand('copy'), then remove the
textarea; ensure setCopied(true)/setCopied(false) are set based on the success
of either method and keep the existing timeout for clearing the copied state,
and catch/log errors for debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (7)
src/components/features/report/report-client-page.tsx (7)
43-47: De-duplicate status key unions; reuse the Status type.The inline union is repeated in two places. Prefer referencing Status to avoid drift.
Apply these diffs:
- [repo: string]: { - [status in 'in_progress' | 'waiting_for_review' | 'reviewed' | 'merged' | 'blocked']?: TPullRequest[]; - }; + [repo: string]: { + [status in Status]?: TPullRequest[]; + };type RepoStatusArray = [ repo: string, statusMap: { - [status in 'in_progress' | 'waiting_for_review' | 'reviewed' | 'merged' | 'blocked']?: TPullRequest[]; + [status in Status]?: TPullRequest[]; } ];Optionally, introduce a reusable alias and use it in both places (outside these ranges):
type StatusPRMap = Partial<Record<Status, TPullRequest[]>>;Also applies to: 54-59
65-71: Type the emoji map for stronger guarantees.Annotate to ensure all Status keys are covered.
-const statusToEmoji = { +const statusToEmoji: Record<Status, string> = { waiting_for_review: '🕒', in_progress: '🚧', reviewed: '✅', merged: '🔀', blocked: '⛔', -}; +};
73-79: Freeze reviewDecisionToEmoji to prevent accidental mutation.-const reviewDecisionToEmoji = { +const reviewDecisionToEmoji = { APPROVED: '🟢', CHANGES_REQUESTED: '🟠', REVIEW_REQUIRED: '🔵', '': '', -}; +} as const;
88-94: Usereturn;inside forEach instead of returningmd.Returning a value from a forEach callback is unnecessary; use a bare return.
- md += `No pull requests found for team **${teamName}**.\n\n`; - return md; + md += `No pull requests found for team **${teamName}**.\n\n`; + return;
113-113: Avoid headings inside list items for cleaner Markdown.Headings within list bullets render inconsistently. Use plain text or bold.
- md += `\n - #### ${statusToEmoji[status] || ''} ${status.replace(/_/g, ' ').toUpperCase()}\n`; + md += `\n - ${statusToEmoji[status] ?? ''} ${status.replace(/_/g, ' ').toUpperCase()}\n`;
120-120: Remove stray space between newlines.- md += '\n \n'; + md += '\n\n';
281-284: Add accessible label/title to the Markdown button.Improves a11y and UX when the icon changes.
- <Button onClick={handleCopyMarkdown} variant="soft" mb="2"> + <Button + onClick={handleCopyMarkdown} + variant="soft" + mb="2" + aria-label={copied ? 'Copied' : 'Copy markdown report'} + title={copied ? 'Copied!' : 'Copy markdown report'} + > {copied ? <CheckIcon /> : <CopyIcon />} Markdown </Button>
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/features/report/report-client-page.tsx(6 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/features/report/report-client-page.tsx (1)
src/utils/schemas.ts (1)
TPullRequest(131-131)
🔇 Additional comments (5)
src/components/features/report/report-client-page.tsx (5)
10-11: Icon imports for copy feedback look good.
49-52: TeamRepoStatusMap shape reads well.
165-165: Copied state addition looks good.
215-236: Memoized aggregation with explicit typing looks correct.
238-247: Add clipboard fallback for non-secure/older browsers.Current code only uses navigator.clipboard; add a textarea fallback.
const handleCopyMarkdown = async () => { const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap); try { - await navigator.clipboard.writeText(md); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + if (navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(md); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + } else { + const textArea = document.createElement('textarea'); + textArea.value = md; + textArea.style.position = 'fixed'; + textArea.style.left = '-9999px'; + textArea.style.top = '0'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + const ok = document.execCommand('copy'); + document.body.removeChild(textArea); + setCopied(ok); + if (ok) setTimeout(() => setCopied(false), 2000); + } } catch (e) { + console.warn('Clipboard operation failed:', e); setCopied(false); } };
naim-ea
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/components/features/report/report-client-page.tsx (3)
43-47: Unify status typing and simplify maps.Avoid duplicating the status union. Derive from
Statusand usePartial<Record<...>>for brevity and consistency.-type RepoStatusMap = { - [repo: string]: { - [status in 'in_progress' | 'waiting_for_review' | 'reviewed' | 'merged' | 'blocked']?: TPullRequest[]; - }; -}; +type RepoStatusMap = Record<string, Partial<Record<Status, TPullRequest[]>>>; -type RepoStatusArray = [ - repo: string, - statusMap: { - [status in Status]?: TPullRequest[]; - } -]; +type RepoStatusArray = [ + repo: string, + statusMap: Partial<Record<Status, TPullRequest[]>> +];Also applies to: 54-59
87-121: Minor: fix misleading return and trim extra whitespace in Markdown.Returning a value inside
forEachis ignored; usereturn;to skip the rest of the current iteration. Also remove the stray space in the blank line.if (!repoStatusMap || Object.keys(repoStatusMap).length === 0) { md += `No pull requests found for team **${teamName}**.\n\n`; - return md; + return; } @@ - md += '\n \n'; + md += '\n\n';
62-62: Nit: remove redundant semicolon.Cosmetic only; keeps formatting clean.
-export const STATUS_ORDER = ['waiting_for_review', 'in_progress', 'reviewed', 'merged', 'blocked'] as const satisfies readonly Status[];; +export const STATUS_ORDER = ['waiting_for_review', 'in_progress', 'reviewed', 'merged', 'blocked'] as const satisfies readonly Status[];
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/features/report/report-client-page.tsx(7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/features/report/report-client-page.tsx (1)
src/utils/schemas.ts (2)
TPullRequest(131-131)TPullRequestReport(141-141)
🔇 Additional comments (2)
src/components/features/report/report-client-page.tsx (2)
237-246: Clipboard: add fallback for non-secure/older browsers (duplicate of earlier feedback).Implement a fallback to a hidden textarea when
navigator.clipboardis unavailable or the context isn’t secure. Also log failures for diagnostics.const handleCopyMarkdown = async () => { const md = generateMarkdownReport(startDate, endDate, selectedTeams, teamRepoStatusMap); try { - await navigator.clipboard.writeText(md); - setCopied(true); - setTimeout(() => setCopied(false), 2000); + // Prefer modern API when available in secure contexts + if (typeof navigator !== 'undefined' && navigator.clipboard && window.isSecureContext) { + await navigator.clipboard.writeText(md); + } else { + // Fallback for non-secure contexts or older browsers + const textArea = document.createElement('textarea'); + textArea.value = md; + textArea.style.position = 'fixed'; + textArea.style.left = '-9999px'; + textArea.style.top = '0'; + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + try { + const ok = document.execCommand && document.execCommand('copy'); + if (ok === false) throw new Error('execCommand returned false'); + } finally { + document.body.removeChild(textArea); + } + } + setCopied(true); + setTimeout(() => setCopied(false), 2000); } catch (e) { + console.warn('Clipboard operation failed:', e); setCopied(false); } };
214-235: Status keys match TPullRequestReport and STATUS_ORDER.PullRequestReportSchema (src/utils/schemas.ts) defines merged, in_progress, reviewed, waiting_for_review, blocked; report-client-page.tsx declares Status = keyof TPullRequestReport and STATUS_ORDER is typed to satisfy readonly Status[], so the cast is consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (2)
src/components/features/report/report-client-page.tsx (2)
43-47: Improve type safety with readonly status mapping.The
RepoStatusMaptype uses a mapped type with optional status keys, which is good. However, for better maintainability, consider making the status keys readonly to prevent accidental modification.Apply this diff to make the status keys readonly:
-type RepoStatusMap = { - [repo: string]: { - [status in 'in_progress' | 'waiting_for_review' | 'reviewed' | 'merged' | 'blocked']?: TPullRequest[]; - }; -}; +type RepoStatusMap = { + readonly [repo: string]: { + readonly [status in Status]?: TPullRequest[]; + }; +};
79-126: LGTM: Comprehensive markdown generation with minor formatting improvement.The
generateMarkdownReportfunction is well-structured and handles all the necessary cases for generating a comprehensive markdown report. The logic for sorting repositories (gnolang repos first, then alphabetically) is appropriate.One minor improvement for markdown formatting:
Apply this diff to improve markdown formatting:
- md += '\n \n'; + md += '\n';The extra space in
'\n \n'creates unnecessary whitespace in the markdown output.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/components/features/report/report-client-page.tsx(7 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/features/report/report-client-page.tsx (1)
src/utils/schemas.ts (2)
TPullRequest(131-131)TPullRequestReport(141-141)
🔇 Additional comments (8)
src/components/features/report/report-client-page.tsx (8)
10-12: LGTM: Icon imports added correctly.The
CheckIconandCopyIconimports are appropriately added for the copy markdown functionality.
35-35: LGTM: Import added for type derivation.The
TPullRequestReportimport is correctly added to derive theStatustype from the schema.
49-62: LGTM: Well-structured type definitions.The new types
TeamRepoStatusMap,RepoStatusArray, andStatusare well-defined. The use ofkeyof TPullRequestReportfor theStatustype ensures consistency with the schema, and theSTATUS_ORDERconstant provides a proper ordering with type safety.
64-77: LGTM: Well-organized emoji mappings.The emoji mappings for statuses and review decisions are well-organized and provide clear visual indicators for the markdown output.
164-164: LGTM: State management for copy feedback.The
copiedstate is appropriately added to provide user feedback for the copy operation.
214-235: LGTM: Proper memoization of team repository status mapping.The
teamRepoStatusMapis correctly memoized and structured to support the markdown generation functionality.
237-246: Critical clipboard compatibility issue requires immediate attention.The current clipboard implementation only uses the modern
navigator.clipboard.writeText()API, which requires HTTPS/secure contexts and may fail in older browsers or insecure contexts. This is a critical usability issue that needs to be addressed.The existing review comment addresses the same issue with a comprehensive fallback solution. Please implement the suggested clipboard compatibility improvements to ensure the feature works across all deployment scenarios and browser environments.
267-282: LGTM: Clean UI integration of copy functionality.The copy markdown button is well-integrated into the existing UI layout with proper visual feedback using the
CheckIcon/CopyIcontoggle. The button placement within the responsive flex layout is appropriate.
Summary by CodeRabbit