feat(surveys): show per-user response status on me lens#680
Conversation
LFXV2-1726 Joins survey_response records (keyed by user email/username) against the surveys list so each survey on the Me lens reflects whether the current user has actually submitted a response. response_status='responded' is stamped only when survey_response.response_datetime is populated, which distinguishes invited-only rows from real submissions. Backend: - GET /api/surveys/:uid/my-response — returns the user's submitted response (NPS value or SurveyMonkey question/answer pairs), or 404. - getSurveys and getMySurveys now run the join in parallel with the primary fetch; both stamp response_status='responded' on matched surveys. getMySurveys also renders a stub Survey when the upstream detail fetch fails so invited rows are not silently dropped. UI (My Surveys / Me lens): - Status badge: amber 'Open' (action needed), green 'Submitted' (done), gray 'Closed'. - Actions: 'Take Survey' (primary CTA) only when open and not responded; 'Update' (same SurveyMonkey link) when open + responded; 'View' on every closed survey. - New my-response-drawer component renders the user's NPS score or question/answer pairs for the selected survey. Replaces the aggregate Results drawer on Me lens — aggregate is admin/participant only (Stephan/Jim). - Survey-name click and row-click on Me lens route to the per-user drawer instead of aggregate. UI (Committee Overview pending actions): - 'Submitted' card with green check icon and 'Update' link replaces the amber 'Survey' card when the user has already responded. - pendingSurveys filters out responded; new respondedSurveys signal drives the Submitted cards. Shared: - MySurveyResponse / MySurveyQuestionAnswer / MySurveyAnswer interfaces describe the per-user response payload. - PendingActionType union extended with 'Submitted' (with matching green severity in PENDING_ACTION_SEVERITY). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (2)
WalkthroughAdds a "Me Lens" allowing users to view their personal survey responses: new backend API, server service methods to mark responded surveys, a frontend SurveyService method, a standalone MyResponseDrawer component (UI + styles), and UI updates in dashboard, table, and committee overview to surface responded vs pending surveys. ChangesMe Lens Survey Response Feature
Sequence Diagram(s)sequenceDiagram
participant User
participant Dashboard
participant MyResponseDrawer
participant SurveyServiceClient
participant API
User->>Dashboard: open Me lens / click View Results
Dashboard->>MyResponseDrawer: set surveyId & visible
MyResponseDrawer->>MyResponseDrawer: set loading
MyResponseDrawer->>SurveyServiceClient: getMyResponse(surveyId)
SurveyServiceClient->>API: GET /surveys/:uid/my-response
API-->>SurveyServiceClient: MySurveyResponse | null
SurveyServiceClient-->>MyResponseDrawer: response (or null)
MyResponseDrawer->>MyResponseDrawer: clear loading, render response or empty state
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR adds per-user survey response awareness across the “Me” lens experience by enriching survey rows with whether the current user has actually submitted, and introduces a dedicated “My Response” drawer + API endpoint for viewing an individual’s submitted answers (instead of aggregate results).
Changes:
- Add shared interfaces for per-user survey responses and extend pending-action typing/severity for a new “Submitted” action state.
- Enrich server survey list results with per-user
response_status='responded'(based onsurvey_response.response_datetime) and addGET /api/surveys/:uid/my-response. - Update Surveys UI to open a per-user response drawer on the Me lens and adjust badges/CTAs (Take Survey / Update / View), plus update Committee Overview pending actions to split responded vs unresponded surveys.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/shared/src/interfaces/survey.interface.ts | Adds MySurveyResponse and related answer/question interfaces for the per-user response payload. |
| packages/shared/src/interfaces/components.interface.ts | Extends PendingActionType union with 'Submitted'. |
| packages/shared/src/constants/pending-action.constants.ts | Adds Submitted to PENDING_ACTION_SEVERITY. |
| apps/lfx-one/src/server/services/survey.service.ts | Enriches getSurveys with per-user responded status; enhances getMySurveys; adds getMyResponse endpoint logic. |
| apps/lfx-one/src/server/routes/surveys.route.ts | Registers the new /:uid/my-response route. |
| apps/lfx-one/src/server/controllers/survey.controller.ts | Adds controller handler for GET /surveys/:uid/my-response. |
| apps/lfx-one/src/app/shared/services/survey.service.ts | Adds Angular client method getMyResponse() for the new endpoint. |
| apps/lfx-one/src/app/modules/surveys/surveys-dashboard/surveys-dashboard.component.ts | Routes Me-lens “results” actions to the new per-user drawer. |
| apps/lfx-one/src/app/modules/surveys/surveys-dashboard/surveys-dashboard.component.html | Wires isMeLens to the table and mounts the lfx-my-response-drawer. |
| apps/lfx-one/src/app/modules/surveys/components/surveys-table/surveys-table.component.ts | Adds an isMeLens input to customize table actions. |
| apps/lfx-one/src/app/modules/surveys/components/surveys-table/surveys-table.component.html | Updates status badge + CTAs (Take Survey / Update / View / Results) with Me-lens-specific behavior. |
| apps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.ts | New per-user response drawer component (data loading + computed view state). |
| apps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.html | Drawer UI rendering NPS or question/answer details + empty states. |
| apps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.scss | Drawer styling overrides. |
| apps/lfx-one/src/app/modules/committees/components/committee-overview/committee-overview.component.ts | Splits pending vs responded surveys and introduces “Submitted” pending-action rows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/lfx-one/src/server/services/survey.service.ts`:
- Around line 357-367: The getMyResponse method currently returns the matched
record (`match`) directly, which can include an unvalidated `survey_link`;
update getMyResponse to sanitize/validate `match.survey_link` against the
existing SURVEY_LINK_ALLOWLIST (same approach used in getMySurveys) before
returning: locate where `responses` and `match` are computed, check
`match.survey_link` against SURVEY_LINK_ALLOWLIST and either keep it, normalize
it, or remove/null it if it fails validation, then return the sanitized object
(not the raw `match`) to ensure unsafe URLs do not propagate to the UI.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 8f1f9347-e96d-4101-bcfd-d1fc956c2302
📒 Files selected for processing (15)
apps/lfx-one/src/app/modules/committees/components/committee-overview/committee-overview.component.tsapps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.htmlapps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.scssapps/lfx-one/src/app/modules/surveys/components/my-response-drawer/my-response-drawer.component.tsapps/lfx-one/src/app/modules/surveys/components/surveys-table/surveys-table.component.htmlapps/lfx-one/src/app/modules/surveys/components/surveys-table/surveys-table.component.tsapps/lfx-one/src/app/modules/surveys/surveys-dashboard/surveys-dashboard.component.htmlapps/lfx-one/src/app/modules/surveys/surveys-dashboard/surveys-dashboard.component.tsapps/lfx-one/src/app/shared/services/survey.service.tsapps/lfx-one/src/server/controllers/survey.controller.tsapps/lfx-one/src/server/routes/surveys.route.tsapps/lfx-one/src/server/services/survey.service.tspackages/shared/src/constants/pending-action.constants.tspackages/shared/src/interfaces/components.interface.tspackages/shared/src/interfaces/survey.interface.ts
🚀 Deployment StatusYour branch has been deployed to: https://ui-pr-680.dev.v2.cluster.linuxfound.info Deployment Details:
The deployment will be automatically removed when this PR is closed. |
Address review comments from @copilot-pull-request-reviewer and @coderabbitai: - packages/shared/src/constants/pending-action.constants.ts: change PENDING_ACTION_SEVERITY.Survey from 'success' (green) to 'warn' (amber) so pending Survey cards visually differ from the new 'Submitted' cards on the committee overview (per @copilot) - committee-overview.component.ts: normalize response_status comparisons to lowercase in pendingSurveys / respondedSurveys filters to absorb upstream casing (e.g. 'RESPONDED') (per @copilot) - committee-overview.component.ts: drop buttonLink and rename the Submitted card CTA from 'Update' to 'View' — survey_link is Me-lens- only data, and handlePendingActionClick falls through to surveys-tab navigation for non-Vote items, so the previous label was misleading (per @copilot) - surveys-table.component.html: normalize response_status comparisons via @let isResponded / @let actionIsResponded (per @copilot) - surveys-dashboard.component.ts: onViewResults now explicitly closes the non-target drawer before opening the chosen one so a mid-session lens switch can't leave both visibility signals true (per @copilot) - server survey.service.ts: refresh getMySurveys inline comment to describe current behavior (per-user drawer + 'Update' CTA) instead of the stale 'Take Survey' to 'Results' description (per @copilot) - server survey.service.ts: validate survey_link against SURVEY_LINK_ALLOWLIST in getMyResponse before returning, matching the existing getMySurveys defense-in-depth (per @coderabbitai) - my-response-drawer.component.html: add aria-label='Close' on the icon-only close button (per @copilot) - my-response-drawer.component.html: track key fallback to $index when question_id is missing to avoid DOM reuse collisions (per @copilot) - my-response-drawer.component.html: render answer text with text ?? choice_id ?? '—' fallback to avoid empty bullets (per @copilot) - my-response-drawer.component.ts: infer NPS mode from response.nps_value as a fallback so stub Survey rows (rendered when /surveys/{uid} detail fetch fails) don't mis-render NPS responses as 'no answers' (per @copilot) Resolves 11 review threads. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Review Feedback AddressedCommit: ee2a503 Changes Made
Threads Resolved11 of 11 unresolved threads addressed in this iteration. Follow-up (not in this PR)
🤖 Generated with Claude Code |
|
The follow-up you described (enriching committee/project surveys with the current user's Triggering a re-review now. ✅ Actions performedReview triggered.
|
jordane
left a comment
There was a problem hiding this comment.
One error-handling pattern issue worth fixing before merge — see inline comment. The rest of the feature looks solid; the survey_link sanitization and Update-button issues are already covered by the bot reviews.
Address review comment from @jordane: - apps/lfx-one/src/server/controllers/survey.controller.ts: replace the manual `res.status(404).json(...)` in getMyResponse with `next(new ResourceNotFoundError(...))` so the central apiErrorHandler middleware emits a structured 404 with request_id correlation and a consistent error envelope. Matches the pattern in public-meeting.controller.ts. Frontend behavior is unchanged — the Angular getMyResponse catchError still swallows 404 and the drawer renders its empty state. Resolves 1 review thread. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Review Feedback Addressed — Iteration 2Commit: 73b9be9 Changes Made
Threads Resolved1 of 1 new unresolved thread addressed in this iteration. 🤖 Generated with Claude Code |
Address review comments from @copilot-pull-request-reviewer: - packages/shared/src/interfaces/survey.interface.ts: relax Survey.survey_cutoff_date to `string | null` so the getMySurveys stub can use null instead of an empty string that would crash Angular's DatePipe in the Me-lens table. - apps/lfx-one/src/server/services/survey.service.ts: stub Survey now uses survey_cutoff_date: null; also guard the sort comparator's new Date(...) call against null. - apps/lfx-one/src/server/services/survey.service.ts: rename the get_surveys debug log field from `responded_count` to `responded_in_result` and compute the intersection with the current result set, so the metric can't exceed final_count. - apps/lfx-one/src/server/services/survey.service.ts: clarify getMyResponse JSDoc — survey_uid filtering is in-memory in the service, not "client-side". - apps/lfx-one/src/app/modules/surveys/components/my-response-drawer/ my-response-drawer.component.html: remove doubled padding (p-4 sm:p-6 on the inner wrapper duplicated the SCSS-applied .p-drawer-content padding). - apps/lfx-one/src/app/modules/surveys/components/my-response-drawer/ my-response-drawer.component.{ts,html}: branch the empty state on displayStatus. Open + unresponded surveys now show "You haven't responded yet" with an optional "Take the survey" CTA when survey_link is available; closed surveys keep the "didn't respond before survey closed" copy. - apps/lfx-one/src/app/modules/committees/components/committee-meetings/ committee-meetings.component.ts: handle nullable survey.survey_cutoff_date when constructing the FullCalendar event. Resolves 5 review threads. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Manish Dixit <mdixit@linuxfoundation.org>
Review Feedback Addressed — Iteration 3Commit: 5a55fae Changes Made
Threads Resolved5 of 5 unresolved threads addressed in this iteration. 🤖 Generated with Claude Code |
|
@jordane — your feedback has been addressed across three iterations. Ready for another look when you have a moment. Your thread (
Other review feedback addressed in the same session:
All 17 review threads are resolved. Lint, type-check, and build are green on the latest commit. No new functional behavior since your review — only your requested fix + the Copilot suggestions. 🤖 Generated with Claude Code |
|
Triggering a re-review to pick up commits Also — my earlier offer still stands: happy to open a GitHub issue to track the follow-up (enriching committee/project survey rows with the current user's ✅ Actions performedReview triggered.
|
jordane
left a comment
There was a problem hiding this comment.
All 17 review comments (2 rounds of Copilot + CodeRabbit + my initial REQUEST_CHANGES) verified against HEAD 5a55fae — every item is correctly addressed:
- PENDING_ACTION_SEVERITY.Survey → 'warn', Submitted stays 'success'
- Both pendingSurveys/respondedSurveys and table template use ?.toLowerCase() === 'responded'
- onViewResults explicitly clears the non-target drawer before opening the selected one
- Stale 'Take Survey → Results' comment updated to reflect per-user drawer + Update CTA
- getMyResponse validates survey_link against SURVEY_LINK_ALLOWLIST before returning (security fix)
- Controller 404 now routes through ResourceNotFoundError → apiErrorHandler
- my-response-drawer: aria-label on close button, track qa.question_id ?? $index, a.text ?? a.choice_id ?? '—', isNps falls back to nps_value != null, single-source padding, isOpenUnresponded branch with Take Survey link
- Committee overview: Submitted cards labeled 'View', buttonLink dropped
- Stub survey: survey_cutoff_date: null (DatePipe-safe), downstream sort guards in place
- responded_in_result now counts only the intersection with the current result set
- JSDoc reworded to 'in-memory after the query returns'
No new concerns found.
🧹 Deployment RemovedThe deployment for PR #680 has been removed. |
Summary
survey_responserecords (by user email/username) against the surveys list so each row on the Me lens reflects whether the current user has actually submitted.response_status='responded'is stamped only whensurvey_response.response_datetimeis populated — distinguishing invited-only rows from real submissions.GET /api/surveys/:uid/my-responseendpoint plus a newlfx-my-response-drawerfor per-user response viewing on the Me lens. The aggregate Results drawer is no longer reachable from Me lens (Stephan/Jim feedback — most takers shouldn't see aggregate).getMySurveysnow renders a stub Survey when the upstream/surveys/{uid}detail fetch fails, so invited rows are not silently dropped.JIRA: LFXV2-1726
Shared package changes
MySurveyResponse/MySurveyQuestionAnswer/MySurveyAnswerinterfaces.PendingActionTypeunion extended with'Submitted'; matching green severity added toPENDING_ACTION_SEVERITY.Behavior changes worth calling out
getSurveysnow makes one extra parallel query totype=survey_responseto computeresponse_statusfor the requesting user. Returns an empty set on failure — surveys still render as not-responded.Test plan
0–10caption🤖 Generated with Claude Code