The frontend component of PuffPastry, meant to interface with the backend, which in turn interfaces with the smart contract.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

componentize-home-view.md 12 KiB

​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
​
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. ---
  2. sessionId: session-261002-153750-168j
  3. ---
  4. # Requirements
  5. ### Overview & Goals
  6. `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.
  7. 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.
  8. ### Scope
  9. #### In Scope
  10. - Extracting standalone proposal components:
  11. - `ProposalDetail.vue`: Primary article layout combining proposal header, formatted paragraphs, amendments section, comments section, and the action rail.
  12. - `ProposalVotingCard.vue`: Voting options card (Approve, Reject, Abstain).
  13. - `ProposalExportCard.vue`: Markdown export card with download trigger.
  14. - `ProposalEmptyState.vue`: Informational empty state when no proposal is selected.
  15. - Extracting reusable utilities:
  16. - `src/utils/markdown.ts`: Centralized markdown parsing and DOM sanitization.
  17. - `src/utils/proposalExport.ts`: Proposal Markdown file generator and file download trigger.
  18. - Refactoring `src/views/HomeView.vue` to orchestrate layout, data fetching, and loading states cleanly.
  19. - Updating `ProposalContainer.vue` to use the shared markdown utility.
  20. #### Out of Scope
  21. - Backend API schema changes or new endpoints.
  22. - Redesigning the visual styles or changing Tailwind classes and responsive behavior.
  23. - Modifying `Sidebar.vue` or modal dialogs beyond their existing integration.
  24. ### User Stories
  25. - **As a developer**, I want `HomeView.vue` to be concise and focused on high-level layout so that I can easily navigate and maintain page-level logic.
  26. - **As a developer**, I want proposal voting, export, and presentation logic split into dedicated components so that they can be tested and modified independently without risking regressions across the entire page.
  27. - **As a user**, I want proposal display, voting, and file downloading to function seamlessly with identical UI and responsive interactions.
  28. ### Functional Requirements
  29. - **FR-1**: When no proposal is selected, the application displays `ProposalEmptyState` with the title "Select a proposal" and helper text.
  30. - **FR-2**: When a proposal is selected, `ProposalDetail` renders:
  31. - Proposal header with tags, creator wallet address, and proposal name.
  32. - Proposal paragraphs formatted using sanitized Markdown.
  33. - `AmendmentCarousel` with the current proposal ID.
  34. - Comments section placeholder.
  35. - Right-hand action rail containing `ProposalVotingCard` and `ProposalExportCard`.
  36. - **FR-3**: Clicking the download button in `ProposalExportCard` triggers the Markdown export and downloads a sanitized `.md` file containing proposal title, author, and paragraphs.
  37. - **FR-4**: Voting actions in `ProposalVotingCard` maintain their existing UI styling and emit selection events.
  38. - **FR-5**: Sidebar visibility toggles and loading spinners remain fully operational across desktop and mobile viewports.
  39. ### Non-Functional Requirements
  40. - **Maintainability**: Clear separation of presentation, business logic, and file utilities.
  41. - **Type Safety**: Full TypeScript typing for props, emits, and utility functions conforming to generated API models.
  42. - **Performance**: Zero overhead from component decomposition; no extra re-renders or layout shifts.
  43. # Technical Design
  44. ### Current Implementation
  45. `HomeView.vue` (245 lines) contains:
  46. - Primary container with responsive sidebar integration and mobile backdrop.
  47. - Proposal article layout with header, paragraph loops using inline `DOMPurify` / `marked` formatting, `AmendmentCarousel`, and comments container.
  48. - Aside rail containing voting buttons and the markdown download card.
  49. - Empty selection state.
  50. - Script containing proposal data fetching, markdown sanitization, file download generation, loading states, and sidebar toggling.
  51. - `ProposalContainer.vue` also duplicates markdown parsing logic (`marked` + `DOMPurify`).
  52. ### Key Decisions
  53. 1. **Component Hierarchy**:
  54. - `HomeView.vue` remains the top-level route coordinator: handles sidebar toggle state, fetching proposals list, and active proposal selection.
  55. - `ProposalDetail.vue` acts as the container for proposal inspection: receives `proposal: ProposalWithParagraphs` as a prop and embeds `AmendmentCarousel`, `ProposalVotingCard`, and `ProposalExportCard`.
  56. - `ProposalVotingCard.vue` and `ProposalExportCard.vue` are extracted as dedicated components to keep the voting workflow and export logic decoupled from reading layout.
  57. - `ProposalEmptyState.vue` handles the "no proposal selected" display.
  58. 2. **Utility Extraction (`src/utils/markdown.ts` & `src/utils/proposalExport.ts`)**:
  59. - Extract `applyFormatting` / `renderMarkdown` into `src/utils/markdown.ts` to prevent code duplication between `HomeView.vue` and `ProposalContainer.vue`.
  60. - Extract `downloadProposalMarkdown` and `sanitizeFilename` into `src/utils/proposalExport.ts` so file generation logic is pure, reusable, and easily testable.
  61. ### Components
  62. - **`src/views/HomeView.vue` (Modified)**:
  63. - Retains: `Sidebar`, mobile overlay, loading spinner, selection state, and window resize listeners.
  64. - Uses: `<ProposalDetail :proposal="selected" />` and `<ProposalEmptyState />`.
  65. - **`src/components/ProposalDetail.vue` (New)**:
  66. - Props: `proposal: ProposalWithParagraphs`.
  67. - Template: Proposal header, sanitized paragraph list, `AmendmentCarousel`, comments block, and sidebar rail embedding `ProposalVotingCard` and `ProposalExportCard`.
  68. - **`src/components/ProposalVotingCard.vue` (New)**:
  69. - Props: `proposalId: number`.
  70. - Emits: `vote: [type: 'Positive' | 'Negative' | 'Abstain']`.
  71. - Template: "Cast your vote" card with Approve, Reject, Abstain buttons.
  72. - **`src/components/ProposalExportCard.vue` (New)**:
  73. - Props: `proposalId: number`, `proposalName: string`.
  74. - Template: Proposal record card with `.md` badge and download action button.
  75. - **`src/components/ProposalEmptyState.vue` (New)**:
  76. - Template: Centered container with "Select a proposal" header and instructions.
  77. - **`src/components/ProposalContainer.vue` (Modified)**:
  78. - Refactored to import `renderMarkdown` from `src/utils/markdown.ts`.
  79. ### File Structure
  80. ```
  81. src/
  82. ├── components/
  83. │ ├── AmendmentCarousel.vue (existing)
  84. │ ├── LoadingSpinner.vue (existing)
  85. │ ├── ProposalContainer.vue (modified - use markdown util)
  86. │ ├── ProposalDetail.vue (new)
  87. │ ├── ProposalEmptyState.vue (new)
  88. │ ├── ProposalExportCard.vue (new)
  89. │ ├── ProposalVotingCard.vue (new)
  90. │ └── Sidebar.vue (existing)
  91. ├── utils/
  92. │ ├── markdown.ts (new)
  93. │ ├── proposalExport.ts (new)
  94. │ └── wallet.ts (existing)
  95. └── views/
  96. └── HomeView.vue (modified - simplified coordinator)
  97. ```
  98. ### Architecture Diagram
  99. ```mermaid
  100. graph TD
  101. HomeView["HomeView.vue (Coordinator)"]
  102. Sidebar["Sidebar.vue"]
  103. ProposalEmptyState["ProposalEmptyState.vue"]
  104. ProposalDetail["ProposalDetail.vue"]
  105. AmendmentCarousel["AmendmentCarousel.vue"]
  106. ProposalVotingCard["ProposalVotingCard.vue"]
  107. ProposalExportCard["ProposalExportCard.vue"]
  108. MarkdownUtil["utils/markdown.ts"]
  109. ExportUtil["utils/proposalExport.ts"]
  110. HomeView --> Sidebar
  111. HomeView --> ProposalEmptyState
  112. HomeView --> ProposalDetail
  113. ProposalDetail --> AmendmentCarousel
  114. ProposalDetail --> ProposalVotingCard
  115. ProposalDetail --> ProposalExportCard
  116. ProposalDetail --> MarkdownUtil
  117. ProposalExportCard --> ExportUtil
  118. ```
  119. ### Data Models / Contracts
  120. ```typescript
  121. // src/utils/markdown.ts
  122. export function renderMarkdown(markdown?: string | null): string;
  123. // src/utils/proposalExport.ts
  124. export function sanitizeFilename(name: string): string;
  125. export async function downloadProposalMarkdown(proposalId: number): Promise<void>;
  126. ```
  127. ### Risks
  128. - **Styling or Layout Breakage**: Moving aside rail or article container into subcomponents could disrupt CSS grid or sticky positioning.
  129. - *Mitigation*: Preserve exact Tailwind classes (`grid xl:grid-cols-[minmax(0,1fr)_15rem]`, `xl:sticky xl:top-6`) on the container and child components.
  130. - **Event / Props Wiring**: Ensure proposal ID and markdown download methods properly receive required props.
  131. - *Mitigation*: Strict TypeScript interfaces for all component props.
  132. # Testing
  133. ### Validation Approach
  134. Verify the refactored components through type checking, build verification, and functional validation against key user workflows.
  135. ### Key Scenarios
  136. 1. **Initial Page Load & Empty State**:
  137. - On navigating to `/`, proposals list loads in sidebar.
  138. - Main content area displays `ProposalEmptyState` ("Select a proposal").
  139. 2. **Proposal Selection**:
  140. - Clicking a proposal in the sidebar loads details and renders `ProposalDetail`.
  141. - Title, author badge, creator wallet, and paragraphs are displayed with correct markdown formatting.
  142. - `AmendmentCarousel` receives the selected proposal ID and displays amendments.
  143. 3. **Proposal Export / Download**:
  144. - Clicking the download button in `ProposalExportCard` generates and downloads the formatted `.md` file with sanitized filename.
  145. 4. **Voting Actions**:
  146. - Approve, Reject, and Abstain buttons in `ProposalVotingCard` render properly with hover and focus states.
  147. 5. **Mobile Responsiveness**:
  148. - Sidebar toggle and overlay function on small screens; sidebar closes upon selecting a proposal.
  149. ### Edge Cases
  150. - **Missing or Empty Proposal Paragraphs**: `ProposalDetail` handles empty paragraph list gracefully without throwing runtime errors.
  151. - **Unsafe HTML in Markdown**: `DOMPurify` via `markdown.ts` sanitizes all rendered HTML in both `ProposalDetail` and `ProposalContainer`.
  152. - **Special Characters in Proposal Title**: `sanitizeFilename` replaces forbidden filesystem characters with dashes before triggering file download.
  153. ### Test Changes
  154. - Execute `npm run build` (`vue-tsc -b && vite build`) to ensure 0 TypeScript or template compilation errors.
  155. # Delivery Steps
  156. ### ✓ Step 1: Extract markdown formatting and export utilities
  157. Create shared utilities to eliminate duplicate markdown handling and file export logic across components.
  158. - Create `src/utils/markdown.ts` exporting `renderMarkdown` / `applyFormatting` using `marked` and `DOMPurify`.
  159. - Create `src/utils/proposalExport.ts` to encapsulate the Markdown file generator, string sanitization (`sanitizeFilename`), and browser download trigger.
  160. - Refactor `src/components/ProposalContainer.vue` to use the shared markdown helper instead of local inline parsing.
  161. ### ✓ Step 2: Implement standalone proposal action and state components
  162. Create focused components for proposal actions and state placeholders.
  163. - Create `src/components/ProposalVotingCard.vue` containing the "Cast your vote" card with Approve, Reject, and Abstain action buttons and emit events.
  164. - Create `src/components/ProposalExportCard.vue` containing the Markdown download action and integration with `proposalExport.ts`.
  165. - Create `src/components/ProposalEmptyState.vue` displaying the empty selection message and guidance when no proposal is active.
  166. ### ✓ Step 3: Implement ProposalDetail component
  167. Encapsulate proposal content presentation into a dedicated detail component.
  168. - Create `src/components/ProposalDetail.vue` accepting a `proposal: ProposalWithParagraphs` prop.
  169. - Include the proposal header (tags, creator attribution, title), content body rendering paragraphs via `applyFormatting`, `AmendmentCarousel` integration, and comments section.
  170. - Compose `ProposalVotingCard` and `ProposalExportCard` into the sticky action sidebar rail.
  171. ### ✓ Step 4: Refactor HomeView and verify integration
  172. Streamline HomeView to act purely as the top-level page orchestrator.
  173. - Replace inline proposal markup and action rail in `src/views/HomeView.vue` with `ProposalDetail` and `ProposalEmptyState`.
  174. - Clean up unused imports, methods (`applyFormatting`, `downloadProposalMarkdown`, `sanitizeFilename`), and redundant styles from `HomeView.vue`.
  175. - Verify TypeScript compilation and runtime behavior with `vue-tsc -b` and `vite build`.