| sessionId |
|---|
| session-261002-153750-168j |
HomeView.vue currently acts as a monolithic view holding layout orchestration, proposal reading and paragraph rendering, amendment carousel presentation, comments placeholder, voting action buttons, export-to-markdown generation and file downloading, and empty/loading states.
The goal is to componentize HomeView.vue into modular, single-responsibility Vue 3 components and shared utility functions. This reduces template verbosity, enhances maintainability and testability, eliminates duplicated markdown parsing across the project, and establishes clean component boundaries.
ProposalDetail.vue: Primary article layout combining proposal header, formatted paragraphs, amendments section, comments section, and the action rail.ProposalVotingCard.vue: Voting options card (Approve, Reject, Abstain).ProposalExportCard.vue: Markdown export card with download trigger.ProposalEmptyState.vue: Informational empty state when no proposal is selected.src/utils/markdown.ts: Centralized markdown parsing and DOM sanitization.src/utils/proposalExport.ts: Proposal Markdown file generator and file download trigger.src/views/HomeView.vue to orchestrate layout, data fetching, and loading states cleanly.ProposalContainer.vue to use the shared markdown utility.Sidebar.vue or modal dialogs beyond their existing integration.HomeView.vue to be concise and focused on high-level layout so that I can easily navigate and maintain page-level logic.ProposalEmptyState with the title “Select a proposal” and helper text.ProposalDetail renders:
AmendmentCarousel with the current proposal ID.ProposalVotingCard and ProposalExportCard.ProposalExportCard triggers the Markdown export and downloads a sanitized .md file containing proposal title, author, and paragraphs.ProposalVotingCard maintain their existing UI styling and emit selection events.HomeView.vue (245 lines) contains:
DOMPurify / marked formatting, AmendmentCarousel, and comments container.ProposalContainer.vue also duplicates markdown parsing logic (marked + DOMPurify).Component Hierarchy:
HomeView.vue remains the top-level route coordinator: handles sidebar toggle state, fetching proposals list, and active proposal selection.ProposalDetail.vue acts as the container for proposal inspection: receives proposal: ProposalWithParagraphs as a prop and embeds AmendmentCarousel, ProposalVotingCard, and ProposalExportCard.ProposalVotingCard.vue and ProposalExportCard.vue are extracted as dedicated components to keep the voting workflow and export logic decoupled from reading layout.ProposalEmptyState.vue handles the “no proposal selected” display.Utility Extraction (src/utils/markdown.ts & src/utils/proposalExport.ts):
applyFormatting / renderMarkdown into src/utils/markdown.ts to prevent code duplication between HomeView.vue and ProposalContainer.vue.downloadProposalMarkdown and sanitizeFilename into src/utils/proposalExport.ts so file generation logic is pure, reusable, and easily testable.src/views/HomeView.vue (Modified):
Sidebar, mobile overlay, loading spinner, selection state, and window resize listeners.<ProposalDetail :proposal="selected" /> and <ProposalEmptyState />.src/components/ProposalDetail.vue (New):
proposal: ProposalWithParagraphs.AmendmentCarousel, comments block, and sidebar rail embedding ProposalVotingCard and ProposalExportCard.src/components/ProposalVotingCard.vue (New):
proposalId: number.vote: [type: 'Positive' | 'Negative' | 'Abstain'].src/components/ProposalExportCard.vue (New):
proposalId: number, proposalName: string..md badge and download action button.src/components/ProposalEmptyState.vue (New):
src/components/ProposalContainer.vue (Modified):
renderMarkdown from src/utils/markdown.ts.src/
├── components/
│ ├── AmendmentCarousel.vue (existing)
│ ├── LoadingSpinner.vue (existing)
│ ├── ProposalContainer.vue (modified - use markdown util)
│ ├── ProposalDetail.vue (new)
│ ├── ProposalEmptyState.vue (new)
│ ├── ProposalExportCard.vue (new)
│ ├── ProposalVotingCard.vue (new)
│ └── Sidebar.vue (existing)
├── utils/
│ ├── markdown.ts (new)
│ ├── proposalExport.ts (new)
│ └── wallet.ts (existing)
└── views/
└── HomeView.vue (modified - simplified coordinator)
graph TD
HomeView["HomeView.vue (Coordinator)"]
Sidebar["Sidebar.vue"]
ProposalEmptyState["ProposalEmptyState.vue"]
ProposalDetail["ProposalDetail.vue"]
AmendmentCarousel["AmendmentCarousel.vue"]
ProposalVotingCard["ProposalVotingCard.vue"]
ProposalExportCard["ProposalExportCard.vue"]
MarkdownUtil["utils/markdown.ts"]
ExportUtil["utils/proposalExport.ts"]
HomeView --> Sidebar
HomeView --> ProposalEmptyState
HomeView --> ProposalDetail
ProposalDetail --> AmendmentCarousel
ProposalDetail --> ProposalVotingCard
ProposalDetail --> ProposalExportCard
ProposalDetail --> MarkdownUtil
ProposalExportCard --> ExportUtil
// src/utils/markdown.ts
export function renderMarkdown(markdown?: string | null): string;
// src/utils/proposalExport.ts
export function sanitizeFilename(name: string): string;
export async function downloadProposalMarkdown(proposalId: number): Promise<void>;
grid xl:grid-cols-[minmax(0,1fr)_15rem], xl:sticky xl:top-6) on the container and child components.Verify the refactored components through type checking, build verification, and functional validation against key user workflows.
/, proposals list loads in sidebar.ProposalEmptyState (“Select a proposal”).ProposalDetail.AmendmentCarousel receives the selected proposal ID and displays amendments.ProposalExportCard generates and downloads the formatted .md file with sanitized filename.ProposalVotingCard render properly with hover and focus states.ProposalDetail handles empty paragraph list gracefully without throwing runtime errors.DOMPurify via markdown.ts sanitizes all rendered HTML in both ProposalDetail and ProposalContainer.sanitizeFilename replaces forbidden filesystem characters with dashes before triggering file download.npm run build (vue-tsc -b && vite build) to ensure 0 TypeScript or template compilation errors.Create shared utilities to eliminate duplicate markdown handling and file export logic across components.
src/utils/markdown.ts exporting renderMarkdown / applyFormatting using marked and DOMPurify.src/utils/proposalExport.ts to encapsulate the Markdown file generator, string sanitization (sanitizeFilename), and browser download trigger.src/components/ProposalContainer.vue to use the shared markdown helper instead of local inline parsing.Create focused components for proposal actions and state placeholders.
src/components/ProposalVotingCard.vue containing the “Cast your vote” card with Approve, Reject, and Abstain action buttons and emit events.src/components/ProposalExportCard.vue containing the Markdown download action and integration with proposalExport.ts.src/components/ProposalEmptyState.vue displaying the empty selection message and guidance when no proposal is active.Encapsulate proposal content presentation into a dedicated detail component.
src/components/ProposalDetail.vue accepting a proposal: ProposalWithParagraphs prop.applyFormatting, AmendmentCarousel integration, and comments section.ProposalVotingCard and ProposalExportCard into the sticky action sidebar rail.Streamline HomeView to act purely as the top-level page orchestrator.
src/views/HomeView.vue with ProposalDetail and ProposalEmptyState.applyFormatting, downloadProposalMarkdown, sanitizeFilename), and redundant styles from HomeView.vue.vue-tsc -b and vite build.