From d7f8b738788a6afe31b8d7b3222b4acc46388336 Mon Sep 17 00:00:00 2001 From: Jared Bell Date: Tue, 6 Oct 2026 17:46:36 -0400 Subject: [PATCH] Remove unused amendment types and voting controls --- .junie/plans/add-amendment-carousel-card.md | 134 ++++++++ .junie/plans/add-auth-header-to-gen-api.md | 143 ++++++++ .../plans/add-comment-area-proposal-detail.md | 142 ++++++++ .junie/plans/add-vote-amendment-parameters.md | 163 +++++++++ .junie/plans/componentize-home-view.md | 211 ++++++++++++ ...light-user-proposal-and-amendment-votes.md | 181 ++++++++++ .../plans/linkable-proposals-router-view.md | 154 +++++++++ .junie/plans/modernize-icon-system.md | 278 ++++++++++++++++ .../plans/persist-wallet-data-use-session.md | 177 ++++++++++ .../plans/style-amendment-carousel-cards.md | 110 +++++++ index.html | 2 +- src/App.vue | 12 +- src/components/AmendmentCarousel.vue | 310 +++++++++++++----- src/components/BrandBar.vue | 38 +-- src/components/CommentThread.vue | 189 +++++++++++ src/components/ProposalDetail.vue | 227 ++++++++++++- src/components/ProposalVotingCard.vue | 42 ++- src/components/Sidebar.vue | 7 +- src/components/VotingControls.vue | 65 ---- src/components/icons/Abstain.vue | 25 +- src/components/icons/AppIcon.vue | 55 ++++ src/components/icons/Arrow.vue | 15 +- src/components/icons/Cancel.vue | 25 +- src/components/icons/Confirm.vue | 25 +- src/components/icons/DownArrow.vue | 25 +- src/components/icons/Download.vue | 28 +- src/components/icons/Filter.vue | 29 +- src/components/icons/IconBase.vue | 40 +++ src/components/icons/Identification.vue | 43 +-- src/components/icons/Menu.vue | 27 +- src/components/icons/Plus.vue | 16 + src/components/icons/SpeechBubbles.vue | 31 +- src/components/icons/Stack.vue | 32 +- src/components/icons/ThumbsDown.vue | 25 +- src/components/icons/ThumbsUp.vue | 25 +- src/components/icons/index.ts | 45 +++ src/components/icons/types.ts | 25 ++ src/components/modals/AmendmentModal.vue | 6 +- src/components/modals/FilterModal.vue | 4 +- src/components/modals/Modal.vue | 3 +- src/components/modals/NewAmendmentModal.vue | 118 +++++++ src/components/modals/ProposalModal.vue | 2 +- src/components/modals/RegistrationModal.vue | 29 +- src/composables/useSession.ts | 146 ++++++++- src/generated/typescript/index.ts | 24 ++ .../typescript/models/AddAmendmentRequest.ts | 2 +- .../typescript/models/AddCommentRequest.ts | 14 + src/generated/typescript/models/Amendment.ts | 15 + .../typescript/models/AmendmentParagraph.ts | 11 + .../typescript/models/AmendmentStatus.ts | 10 + .../typescript/models/AmendmentVote.ts | 12 + .../models/AmendmentWithParagraphs.ts | 18 + .../typescript/models/AmendmentWithSummary.ts | 17 + src/generated/typescript/models/Claims.ts | 14 + .../typescript/models/CommentWithUser.ts | 17 + .../models/FreighterLoginResponse.ts | 8 + .../typescript/models/Organization.ts | 11 + src/generated/typescript/models/Proposal.ts | 1 + .../typescript/models/ProposalVote.ts | 13 + .../models/RegisterOrganizationRequest.ts | 8 + .../typescript/models/RegisterRequest.ts | 6 +- .../typescript/models/RegisteredUser.ts | 12 + .../typescript/models/SelectableAmendment.ts | 16 + .../typescript/models/SelectableComment.ts | 15 + .../typescript/models/SelectableProposal.ts | 1 + .../typescript/models/UserAmendmentVote.ts | 13 + .../typescript/models/UserProposalVotes.ts | 19 ++ .../typescript/models/VoteAmendmentRequest.ts | 9 + .../models/VoteAmendmentResponse.ts | 13 + src/generated/typescript/models/VoteChoice.ts | 9 + .../typescript/models/VoteProposalRequest.ts | 10 + .../typescript/services/AmendmentService.ts | 54 ++- .../typescript/services/AuthService.ts | 10 +- .../typescript/services/CommentService.ts | 74 +++++ .../services/OrganizationService.ts | 52 +++ .../typescript/services/ProposalService.ts | 54 ++- src/main.ts | 4 +- src/types/amendment.ts | 30 -- src/utils/auth.ts | 38 +++ src/utils/comments.ts | 35 ++ src/utils/eventBus.ts | 9 +- src/views/HomeView.vue | 8 +- src/views/ProposalView.vue | 2 +- tsconfig.app.tsbuildinfo | 2 +- 84 files changed, 3599 insertions(+), 520 deletions(-) create mode 100644 .junie/plans/add-amendment-carousel-card.md create mode 100644 .junie/plans/add-auth-header-to-gen-api.md create mode 100644 .junie/plans/add-comment-area-proposal-detail.md create mode 100644 .junie/plans/add-vote-amendment-parameters.md create mode 100644 .junie/plans/componentize-home-view.md create mode 100644 .junie/plans/highlight-user-proposal-and-amendment-votes.md create mode 100644 .junie/plans/linkable-proposals-router-view.md create mode 100644 .junie/plans/modernize-icon-system.md create mode 100644 .junie/plans/persist-wallet-data-use-session.md create mode 100644 .junie/plans/style-amendment-carousel-cards.md create mode 100644 src/components/CommentThread.vue delete mode 100644 src/components/VotingControls.vue create mode 100644 src/components/icons/AppIcon.vue create mode 100644 src/components/icons/IconBase.vue create mode 100644 src/components/icons/Plus.vue create mode 100644 src/components/icons/index.ts create mode 100644 src/components/icons/types.ts create mode 100644 src/components/modals/NewAmendmentModal.vue create mode 100644 src/generated/typescript/models/AddCommentRequest.ts create mode 100644 src/generated/typescript/models/Amendment.ts create mode 100644 src/generated/typescript/models/AmendmentParagraph.ts create mode 100644 src/generated/typescript/models/AmendmentStatus.ts create mode 100644 src/generated/typescript/models/AmendmentVote.ts create mode 100644 src/generated/typescript/models/AmendmentWithParagraphs.ts create mode 100644 src/generated/typescript/models/AmendmentWithSummary.ts create mode 100644 src/generated/typescript/models/Claims.ts create mode 100644 src/generated/typescript/models/CommentWithUser.ts create mode 100644 src/generated/typescript/models/FreighterLoginResponse.ts create mode 100644 src/generated/typescript/models/Organization.ts create mode 100644 src/generated/typescript/models/ProposalVote.ts create mode 100644 src/generated/typescript/models/RegisterOrganizationRequest.ts create mode 100644 src/generated/typescript/models/RegisteredUser.ts create mode 100644 src/generated/typescript/models/SelectableAmendment.ts create mode 100644 src/generated/typescript/models/SelectableComment.ts create mode 100644 src/generated/typescript/models/UserAmendmentVote.ts create mode 100644 src/generated/typescript/models/UserProposalVotes.ts create mode 100644 src/generated/typescript/models/VoteAmendmentRequest.ts create mode 100644 src/generated/typescript/models/VoteAmendmentResponse.ts create mode 100644 src/generated/typescript/models/VoteChoice.ts create mode 100644 src/generated/typescript/models/VoteProposalRequest.ts create mode 100644 src/generated/typescript/services/CommentService.ts create mode 100644 src/generated/typescript/services/OrganizationService.ts delete mode 100644 src/types/amendment.ts create mode 100644 src/utils/auth.ts create mode 100644 src/utils/comments.ts diff --git a/.junie/plans/add-amendment-carousel-card.md b/.junie/plans/add-amendment-carousel-card.md new file mode 100644 index 0000000..ac0719d --- /dev/null +++ b/.junie/plans/add-amendment-carousel-card.md @@ -0,0 +1,134 @@ +--- +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. \ No newline at end of file diff --git a/.junie/plans/add-auth-header-to-gen-api.md b/.junie/plans/add-auth-header-to-gen-api.md new file mode 100644 index 0000000..ca0c201 --- /dev/null +++ b/.junie/plans/add-auth-header-to-gen-api.md @@ -0,0 +1,143 @@ +--- +sessionId: session-261005-185830-1pry +--- + +# Requirements + +### Overview & Goals +The goal is to ensure that all generated API requests in the frontend application automatically and dynamically include the user's `Authorization: Bearer ` header once authenticated upon login. Currently, the OpenAPI configuration in `src/main.ts` statically evaluates `localStorage` once at application startup using an inconsistent key, preventing subsequent API calls made after login from being authenticated. By configuring `OpenAPI.TOKEN` with a dynamic token resolver in `src/main.ts` and standardizing token persistence across authentication flows, all generated API service calls (e.g. `ProposalService`, `AmendmentService`, `CommentService`, `AuthService`) will seamlessly include the active bearer token. + +### Scope +- **In Scope**: + - Updating `src/main.ts` to configure `OpenAPI.TOKEN` with a dynamic token resolver function using `getAuthToken()` or `localStorage`. + - Standardizing the token storage key across `BrandBar.vue`, `src/utils/auth.ts`, and runtime configurations. + - Ensuring generated service requests (`openapi-typescript-codegen` client) dynamically attach the `Authorization: Bearer ` header on every HTTP request whenever a token is present. + - Verifying clean execution and graceful handling when a user is unauthenticated or logs out. +- **Out of Scope**: + - Changing backend JWT signing, expiration, or validation algorithms. + - Modifying `gen-api.sh` script (handled previously or independently). + +### User Stories +- As an authenticated user, I want all my API actions (such as voting, creating proposals, adding amendments, and posting comments) to automatically include my bearer token so that the backend can authenticate and authorize my operations. +- As a user logging in via Freighter wallet, I want my session JWT to take effect immediately without requiring a full page refresh. +- As a developer, I want the generated OpenAPI client to dynamically resolve the current auth token on each request so that authentication state remains reactive and reliable. + +### Functional Requirements +- **Dynamic Token Resolution**: Configure `OpenAPI.TOKEN` in `src/main.ts` as an asynchronous resolver function `async () => getAuthToken() || ''` (or `() => getAuthToken() ?? undefined`) so that `resolve(options, config.TOKEN)` evaluates per-request. +- **Header Injection**: Ensure `src/generated/typescript/core/request.ts` automatically attaches `Authorization: Bearer ` to request headers whenever `token` is non-empty. +- **Consistent Storage Keys**: Standardize token retrieval through `getAuthToken()` from `src/utils/auth.ts` (which checks `authToken` and `token` fallbacks in `localStorage`). +- **Reactivity on Login**: When `BrandBar.vue` completes `AuthService.loginFreighter`, stored token in `localStorage` is immediately picked up by subsequent API requests without reloading the page. +- **Safe Fallback for Unauthenticated Requests**: When no token exists in storage, `OpenAPI.TOKEN` resolves to empty/undefined and requests are sent without `Authorization` header, preventing malformed headers or runtime errors. + +### Non-Functional Requirements +- **Performance**: Zero overhead beyond a single synchronous/cached `localStorage` read per outgoing request. +- **Reliability & Type Safety**: Preserve strict TypeScript types and compatibility with `openapi-typescript-codegen` generated core contracts. + +# Technical Design + +### Current Implementation +1. **`src/main.ts`**: + Currently sets static headers at module load time: + ```typescript + OpenAPI.BASE = '' + OpenAPI.HEADERS = localStorage.getItem("token") ? {"Authorization": "Bearer " + localStorage.getItem("token")} : {}; + ``` + **Issues**: + - `OpenAPI.HEADERS` is evaluated once when the bundle loads. If the user logs in afterwards, `OpenAPI.HEADERS` remains `{}`. + - It checks key `"token"`, while `BrandBar.vue` saves to `"authToken"`. + - It leaves `OpenAPI.TOKEN` `undefined`, bypassing the built-in token resolver in `request.ts`. + +2. **`src/components/BrandBar.vue`**: + Upon connecting Freighter wallet: + ```typescript + const jwt = await AuthService.loginFreighter({stellarAddress: addr.address}); + try { + localStorage.setItem("authToken", jwt.token as string); + } catch (_) {} + ``` + +3. **`src/utils/auth.ts`**: + Provides helper functions: + ```typescript + export function getAuthToken(): string | null { + try { + return localStorage.getItem('authToken') || localStorage.getItem('token'); + } catch { + return null; + } + } + ``` + +4. **`src/generated/typescript/core/request.ts`**: + The generated client's `getHeaders()` function natively supports dynamic token resolvers: + ```typescript + const [token, username, password, additionalHeaders] = await Promise.all([ + resolve(options, config.TOKEN), + resolve(options, config.USERNAME), + resolve(options, config.PASSWORD), + resolve(options, config.HEADERS), + ]); + ... + if (isStringWithValue(token)) { + headers['Authorization'] = `Bearer ${token}`; + } + ``` + +### Key Decisions +- **Use `OpenAPI.TOKEN` Resolver Function**: Instead of assigning static headers or managing manual interceptors, leverage `openapi-typescript-codegen`'s native `TOKEN` resolver `OpenAPI.TOKEN = async () => getAuthToken() || ''`. This ensures zero architectural changes to generated services and automatic per-request resolution. +- **Centralize Token Lookup in `src/utils/auth.ts`**: Use `getAuthToken()` in `src/main.ts` to maintain unified support for both `authToken` and `token` storage keys and graceful exception handling if storage is restricted. +- **Clean Up Redundant Imports and Static Headers**: Remove the broken `OpenAPI.HEADERS` static assignment and duplicate `./index.css` import in `src/main.ts`. + +### Architecture Diagram +```mermaid +graph LR + User[User / Freighter Wallet] -->|Login| BrandBar[BrandBar.vue] + BrandBar -->|AuthService.loginFreighter| Backend[Backend API] + Backend -->|Return JWT Token| BrandBar + BrandBar -->|localStorage.setItem authToken| Storage[(localStorage)] + Services[Generated API Services\nProposal / Amendment / Comment] -->|__request| RequestCore[core/request.ts] + RequestCore -->|Resolve OpenAPI.TOKEN| AuthUtil[utils/auth.ts: getAuthToken] + AuthUtil -->|Read Token| Storage + RequestCore -->|Headers with Authorization: Bearer token| Backend +``` + +### Proposed Changes +1. **`src/main.ts`**: + - Import `getAuthToken` from `./utils/auth`. + - Set `OpenAPI.TOKEN = async () => getAuthToken() || ''` (or `() => getAuthToken() ?? undefined`). + - Remove obsolete `OpenAPI.HEADERS` static assignment. + - Clean up duplicate `import './index.css'`. + +2. **`src/utils/auth.ts`**: + - Ensure `getAuthToken()` safely handles nulls and retrieves the active token. + +3. **`src/components/BrandBar.vue`**: + - Ensure the token retrieved from `AuthService.loginFreighter` is consistently saved to `localStorage` under `authToken`. + +### File Structure +- `src/main.ts` (modified: configure `OpenAPI.TOKEN` resolver) +- `src/components/BrandBar.vue` (verified / modified: ensure consistent token persistence on login) +- `src/utils/auth.ts` (verified / modified: token utility resolution) + +### Risks +- **Storage Availability**: If local storage is disabled (e.g. private browsing restrictions), `getAuthToken()` returns `null` safely without unhandled exceptions. +- **Token Invalidation / Expiry**: If a token expires, `request.ts` will receive standard 401 response from backend, allowing frontend components to handle re-authentication. + +# Testing + +### Validation Approach +Verify that outgoing HTTP requests dispatched via generated API services include the `Authorization: Bearer ` header when authenticated, and omit it when unauthenticated. + +### Key Scenarios +1. **Authenticated Requests After Login**: + - Connect Freighter wallet and login via `BrandBar.vue`. + - Verify that subsequent calls to `ProposalService.listProposals()`, `AmendmentService.listAmendments()`, `CommentService.addComment()`, etc. include `Authorization: Bearer ` in the request headers. +2. **Initial App Load with Pre-existing Token**: + - With a valid `authToken` already stored in `localStorage`, refresh the page. + - Verify that immediate API calls during initial mount include the bearer token. +3. **Unauthenticated Requests**: + - With empty `localStorage`, verify that public API requests execute without an `Authorization` header and without JavaScript runtime errors. + +### Edge Cases +- Dynamic token change (e.g., logging in as a different user) is immediately reflected in the very next request without requiring application restart. +- Null or whitespace token values do not produce malformed `Authorization: Bearer ` headers. \ No newline at end of file diff --git a/.junie/plans/add-comment-area-proposal-detail.md b/.junie/plans/add-comment-area-proposal-detail.md new file mode 100644 index 0000000..0c943b7 --- /dev/null +++ b/.junie/plans/add-comment-area-proposal-detail.md @@ -0,0 +1,142 @@ +--- +sessionId: session-261005-172001-12ux +--- + +# Requirements + +### Overview & Goals +The objective is to maximize user engagement and utility by introducing an inline "Add Comment" area within `ProposalDetail.vue`. By enabling direct inline commenting without modal disruptions, users can effortlessly discuss and provide feedback on governance proposals, reducing interaction friction and streamlining the feedback loop. + +### Scope +- **In Scope**: + - Embedding an inline comment input area (textarea and action controls) directly inside the Comments section of `src/components/ProposalDetail.vue`. + - Integrating `CommentService.addComment` from `src/generated/typescript/services/CommentService.ts` to submit comments. + - Form validation (preventing empty/whitespace submissions) and loading/error states. + - Refreshing or updating the comment stream upon successful submission. + - Accessible styling consistent with Tailwind CSS guidelines and the existing proposal layout. +- **Out of Scope**: + - Modal-based comment workflows (explicitly avoided per task specification). + - Editing, deleting, or nesting/threading multi-level comments in this phase. + - Backend schema or API endpoint modifications. + +### User Stories +- **As a stakeholder**, I want to post feedback directly beneath a proposal so that I can contribute to the discussion with minimal friction and no context-switching modals. +- **As a user**, I want visual feedback while my comment is being submitted (e.g., loading state, error alert on failure) so that I know the exact status of my action. +- **As a keyboard user**, I want full keyboard accessibility (tab navigation, clear focus states) so that I can participate efficiently without relying solely on mouse input. + +### Functional Requirements +- **Inline Input Form**: + - A clean, expandable textarea with placeholder text (e.g., "Add to the discussion...") rendered directly within the `
` block. + - An "Add Comment" (or "Post Comment") submit button and optional "Cancel" / "Clear" button. +- **Validation & Form State**: + - Disable the submit button when the textarea is empty, whitespace-only, or while a submission is in flight. + - Display clear error messaging if the network or API call fails. +- **API Dispatch**: + - Construct an `AddCommentRequest` with `proposalId`, `content`, and active/fallback `userId`. + - Invoke `CommentService.addComment(requestBody)`. + - On success, reset the input field and update the comment list so the new comment appears immediately. + +### Non-Functional Requirements +- **Usability & Utility**: Minimal layout shift; smooth transition between idle, submitting, and submitted states. +- **Design Consistency**: Match the Tailwind CSS design language (slate borders, amber accents, clear typography) of `ProposalDetail.vue` and `NewAmendmentModal.vue`. +- **Accessibility**: Semantic HTML `
`, ` + +
+ + +
+
+ + +
+ +
+
+ + + diff --git a/src/components/ProposalDetail.vue b/src/components/ProposalDetail.vue index ca045af..57ae829 100644 --- a/src/components/ProposalDetail.vue +++ b/src/components/ProposalDetail.vue @@ -86,7 +86,11 @@ - +
-
- Discussion will appear here. + +
+
+ {{ commentError }} +
+ +
+ + +
+ +
+ + Share your perspective on this proposal. + +
+ + +
+
+
+ + +
+ +
+ Loading comments... +
+ +
+ No comments yet. Be the first to share your feedback! +
+ +
+ +
@@ -123,7 +191,11 @@ class="xl:sticky xl:top-6" aria-labelledby="vote-heading" > - + @@ -131,15 +203,146 @@ diff --git a/src/components/ProposalVotingCard.vue b/src/components/ProposalVotingCard.vue index 4212f59..fc4dda4 100644 --- a/src/components/ProposalVotingCard.vue +++ b/src/components/ProposalVotingCard.vue @@ -19,16 +19,17 @@