--- sessionId: session-261002-164508-dfif --- # Requirements ### Overview & Goals The objective of this task is to convert the placeholder `Add` element in `AmendmentCarousel.vue` into a fully styled, accessible "dummy" amendment card. Clicking this card launches a new amendment creation modal, allowing users to propose an amendment to the currently viewed proposal using their connected Stellar Freighter wallet. ### Scope - **In Scope**: - Transforming the existing placeholder `
` in `src/components/AmendmentCarousel.vue` into an attractive, interactive dummy amendment card. - Creating `src/components/modals/NewAmendmentModal.vue` to capture amendment details (name/title and markdown content). - Integrating `AmendmentService.addAmendment` with wallet authorization via `@stellar/freighter-api`. - Refreshing the amendment list in `AmendmentCarousel.vue` upon successful submission. - Supporting full keyboard navigation and accessible roles for the dummy amendment trigger. - **Out of Scope**: - Modifying backend API endpoints or database schemas (already provided via `AmendmentService`). - Changing voting or confirm/reject approval flows for amendments. ### User Stories - **As a contributor**, I want to click an "Add Amendment" card in the carousel so that I can easily propose a revision to the current proposal. - **As a keyboard user**, I want to focus and activate the "Add Amendment" card with Enter or Space so that the interface is fully accessible. - **As a proposer**, I want a clean modal form to enter the amendment title and body and sign with my wallet so that my submission is properly recorded. ### Functional Requirements - **Card UI & Styling**: The dummy card must match the carousel dimensions (`w-36 md:w-72 lg:w-80 shrink-0`) with styling indicating an actionable create card (e.g. dashed border, centered plus icon, clear typography, and subtle hover animations). - **Modal Triggering**: Clicking or pressing Enter/Space on the dummy card must open the `NewAmendmentModal` via `useModal().show()`. - **Form Submission**: - The modal must contain inputs for Amendment Name (`name`) and Amendment Description/Content (`content`). - Submit button must be disabled until required fields are filled. - On submit, fetch wallet address from `@stellar/freighter-api` and invoke `AmendmentService.addAmendment({ proposalId, creator, name, content })`. - **Carousel Refresh**: Once submitted successfully, the modal closes and the amendment carousel automatically reloads its list to display the newly proposed amendment. ### Non-Functional Requirements - **Consistency**: Visual style must align with the existing Tailwind palette (slate/amber/neutral tones) used in `ProposalDetail.vue` and `ProposalModal.vue`. - **Accessibility**: Provide proper `role="button"`, `tabindex="0"`, `aria-label`, and keyboard event handlers. # Technical Design ### Current Implementation - `src/components/AmendmentCarousel.vue`: Renders existing amendments using horizontal scroll. Currently ends with a raw placeholder `
Add
`. - `src/components/modals/Modal.vue`: Base dialog component with backdrop, keyboard listeners (Esc), headers, and action buttons. - `src/components/modals/ProposalModal.vue`: Example creation modal utilizing `Modal.vue`, `PostBuilder.vue`, `freighter-api`, and `useModal`. - `src/composables/useModal.ts`: Controls active modal visibility and dynamic content via `show(component)` and `hide()`. - `src/generated/typescript/services/AmendmentService.ts`: Provides `addAmendment(requestBody: AddAmendmentRequest)` and `listAmendments(proposalId)`. ### Key Decisions 1. **Dedicated `NewAmendmentModal.vue` Component**: - *Approach*: Create a new modal component specifically for adding amendments rather than overloading the read-only `AmendmentModal.vue`. - *Rationale*: Keeps concerns separated—`AmendmentModal` focuses on inspecting existing amendments while `NewAmendmentModal` handles form inputs, validation, and wallet signing. 2. **Context & Refresh Communication**: - *Approach*: Pass proposal context to the modal and utilize an event emitter or bus notification (`eventBus` or direct callback) upon submission to trigger `getAmendments()` in `AmendmentCarousel.vue`. - *Rationale*: Avoids full page reload and keeps the carousel responsive. 3. **Card Presentation**: - *Approach*: Render the dummy card at the end of the carousel item list with dashed border (`border-2 border-dashed border-slate-300 hover:border-amber-400`), a plus icon from `src/components/icons/Plus.vue`, and descriptive text. - *Rationale*: Makes it instantly recognizable as an action card while preserving the flow and scroll snapping of the carousel. ### Proposed Changes - **`src/components/modals/NewAmendmentModal.vue`** (New File): - Form layout with `name` input and `PostBuilder` (or markdown content area). - Validation ensuring `name.trim().length > 0` and content presence. - Submission handler calling `AmendmentService.addAmendment(...)` with Freighter address. - Emits `created` event or publishes to `eventBus`. - **`src/components/AmendmentCarousel.vue`** (Modified): - Replace raw `Add` article with styled card component. - Add `openNewAmendmentModal` handler invoking `show(NewAmendmentModal)`. - Listen for amendment creation events to trigger `getAmendments()`. ### Architecture Diagram ```mermaid graph LR User[User] -->|Click / Keyboard| DummyCard[Dummy Card in Carousel] DummyCard -->|useModal.show| NewModal[NewAmendmentModal] NewModal -->|Sign & Submit| Freighter[Stellar Freighter Wallet] NewModal -->|addAmendment| Backend[AmendmentService API] Backend -->|Success| Refresh[Refresh Carousel] ``` ### File Structure & Changes - `src/components/modals/NewAmendmentModal.vue`: New creation modal component. - `src/components/AmendmentCarousel.vue`: Updated dummy card markup, styling, and modal invocation. - `src/composables/useModal.ts` (if needed): Context helpers for active proposal ID. # Testing ### Validation Approach Verify the feature through interactive testing and automated checks: - Verify that the dummy card renders correctly within the carousel at different viewport sizes (mobile, tablet, desktop). - Verify keyboard interactions (Tab navigation, Enter, Space keys) trigger the modal. - Verify modal form validation (disabling submission on empty fields). - Verify successful API submission payload structure and carousel refresh. ### Key Scenarios 1. **Card Rendering & Styling**: - Dummy card appears at the end of the amendments carousel. - Styles match design requirements (dashed border, centered icon, hover transitions). 2. **Opening the Modal**: - Clicking the card opens `NewAmendmentModal`. - Pressing Enter or Space when focused on the card opens `NewAmendmentModal`. 3. **Submitting New Amendment**: - Fill in amendment title and body. - Click "Submit Amendment" -> triggers Freighter authorization and calls `AmendmentService.addAmendment`. - Modal closes automatically and the new amendment appears in the carousel list. ### Edge Cases - **No Wallet Connected / Rejected Signing**: Display appropriate feedback if Freighter rejects connection or signing fails. - **Empty or Whitespace-only Inputs**: Ensure submit button remains disabled until valid name and content are provided. - **Carousel Scroll Recalculation**: Ensure `updateEdges()` executes after adding an amendment to update left/right navigation arrows. # Delivery Steps ### ✓ Step 1: Create NewAmendmentModal component A dedicated `NewAmendmentModal.vue` component is created and integrated with the modal system. - Create `src/components/modals/NewAmendmentModal.vue` utilizing `` for the dialog container. - Implement title input with validation (`name`), content editor (using `PostBuilder` or markdown input), and submit action. - Integrate with `@stellar/freighter-api` to retrieve the signer address and `AmendmentService.addAmendment` to dispatch the proposal amendment. - Add error handling, loading state during submission, and automated modal dismissal on success. ### ✓ Step 2: Implement and style the dummy amendment card in AmendmentCarousel The carousel placeholder is replaced with a styled, accessible "Add Amendment" dummy card that opens the modal. - Update the dummy `
` in `src/components/AmendmentCarousel.vue` to match carousel card dimensions (`w-36 md:w-72 lg:w-80`) with dashed border and hover effects (`border-2 border-dashed border-slate-300 hover:border-amber-400 bg-slate-50/50 hover:bg-amber-50/60`). - Embed the `Plus` icon (`src/components/icons/Plus.vue`), title ("Add Amendment"), and descriptive helper text. - Add keyboard accessibility (`role="button"`, `tabindex="0"`, Enter/Space handlers) and click handler calling `useModal().show(NewAmendmentModal)`. - Pass current `proposalId` context to the modal so new amendments are attached to the active proposal. ### ✓ Step 3: Wire submission refresh and carousel reactivity Amendment creation triggers carousel list reload and seamlessly updates the UI. - Emit an event or trigger `getAmendments()` in `AmendmentCarousel.vue` upon successful amendment submission. - Ensure the newly added amendment appears in the carousel without requiring a full page refresh. - Verify scrolling edge calculations (`updateEdges()`) update after new amendment addition.