Conversation
…component to enhance data handling and display
There was a problem hiding this comment.
Pull request overview
This PR redesigns the Survey Details page with a focus on improving the user experience and updating terminology from "referral code" to "coupon code" throughout the application. The changes reorganize the Survey Details page layout, add location filtering to the dashboard, update the survey form with new questions and conditional logic, and update deployment configurations.
Changes:
- Redesigned Survey Details page to prioritize gift card information and improve QR code display
- Updated terminology from "referral code" to "coupon code" across multiple files
- Added location-based filtering to the Survey Entry Dashboard
- Enhanced survey form with winter shelter tracking, conditional field visibility based on age, and calculated values
- Updated survey-core and survey-react-ui dependencies from 2.3.15 to 2.5.7
- Modified deployment workflows to target new branch names and Azure slots
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
client/src/pages/SurveyDetails/SurveyDetails.tsx |
Restructured page to show gift card info first, changed data fetching from single survey to filtering all surveys, updated UI labels |
client/src/pages/Survey/utils/survey.json |
Added winter shelter tracking, calculated values for minor children, conditional visibility for adult-only questions, age range updates, new end page for comments |
client/src/pages/Survey/utils/surveyUtils.tsx |
Updated edit mode to include gift card pages (indices 16-17), updated comments |
client/src/pages/SurveyEntryDashboard/SurveyEntryDashboard.tsx |
Added location filtering capability, reordered table columns (survey code, location, staff name) |
client/src/pages/SurveyEntryDashboard/components/FilterDialog.tsx |
Added location dropdown to filter dialog |
client/src/pages/SurveyEntryDashboard/components/SurveyEntryDashboardRow.tsx |
Reordered columns, moved "View Details" button to end |
client/src/pages/SurveyEntryDashboard/utils/SurveyEntryDashboardUtils.tsx |
Added filterSurveysByLocation function, updated searchable fields |
client/src/pages/ApplyReferral/ApplyReferral.tsx |
Updated all text references from "referral code" to "coupon code" |
client/src/pages/QrPage/QrPage.tsx |
Updated labels and button text to use "coupon code" terminology |
client/src/pages/Login/Login.tsx |
Updated welcome message to "Point-in-Time Count 2026" |
client/src/components/Header.tsx |
Restructured header with centered title, removed "RDS Mobile" branding |
client/src/styles/*.css |
Formatting improvements (indentation, whitespace), added qr-buttons class, updated header styling |
client/package.json |
Updated survey-core and survey-react-ui to version 2.5.7 |
server/src/database/survey/survey.controller.ts |
Updated comment from "referral code" to "coupon code" |
server/src/routes/*.ts |
Updated error messages to use "coupon code" terminology |
server/src/middleware/auth.ts |
Updated error messages to say "Administration" instead of "admin" |
server/src/scripts/generateSeeds.ts |
Updated comments to use "coupon code" terminology |
server/src/scripts/locations.yaml |
New file with 26 survey location definitions for King County |
.github/workflows/*.yml |
Updated deployment workflows to target kc-pit-2026 branches and new Azure app slots |
| if (isEditMode) { | ||
| // Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation | ||
| surveyJson.title = 'Homelessness Experience Survey (Edit Mode)'; | ||
| surveyJson.pages = surveyJson.pages.slice(0, 3); | ||
| // surveyJson.pages = surveyJson.pages.slice(0, 4); | ||
| surveyJson.pages = [ | ||
| ...surveyJson.pages.slice(0, 4), | ||
| surveyJson.pages[16], | ||
| surveyJson.pages[17] | ||
| ].filter(page => page !== undefined); |
There was a problem hiding this comment.
Hardcoded page indices (16 and 17) are fragile and will break if pages are reordered in survey.json. The comment says "Edit mode only uses first 3 pages" but the code now includes 6 pages (0-3, plus 16-17). Consider using page names instead of indices for better maintainability, or update the comment to accurately reflect the new behavior.
| {/* Coupon Code Information */} | ||
| <div className="referral-info"> | ||
| <h3>Referral Information</h3> |
There was a problem hiding this comment.
Inconsistent terminology: The comment says "Coupon Code Information" but the actual heading in the UI is "Referral Information" and uses terms like "Referred By Code" and "Generated Referral Codes". For consistency with the terminology change from "referral" to "coupon" throughout this PR, consider updating this section to use "Coupon" terminology (e.g., "Coupon Information", "Received From Coupon Code", "Generated Coupon Codes").
| data: surveys, | ||
| isLoading: loading | ||
| } = surveyService.useSurveysWithUsersAndLocations() || {}; | ||
| const survey = surveys?.find(s => s._id === id); |
There was a problem hiding this comment.
This change fetches ALL surveys and then filters by ID, instead of fetching a single survey. This is inefficient and will cause performance issues as the number of surveys grows. The original useSurveyWithUser hook fetched only the specific survey needed. Consider keeping the original approach or creating a more efficient method to fetch a single survey with location data.
| data: surveys, | |
| isLoading: loading | |
| } = surveyService.useSurveysWithUsersAndLocations() || {}; | |
| const survey = surveys?.find(s => s._id === id); | |
| data: survey, | |
| isLoading: loading | |
| } = surveyService.useSurveyWithUser(id ?? ''); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
- Updated fetchSurveyWithUser to include location data - Modified useSurveyWithUser to fetch user and location in parallel - Changed SurveyDetails to use useSurveyWithUser instead of useSurveysWithUsersAndLocations - Now fetches only the specific survey by ID, not all surveys - Added locationName to SurveyWithUser type This addresses the review comment about inefficient data fetching in PR #172. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
| ), | ||
| survey.locationObjectId | ||
| ? fetchAndDeserialize<LocationDocument>( | ||
| `/api/locations/${survey.locationObjectId}` | ||
| ) | ||
| : Promise.resolve(null) |
There was a problem hiding this comment.
The parallel fetch of user and location doesn't handle potential errors gracefully. If either fetchAndDeserialize call fails (e.g., user or location not found), the entire Promise.all will reject, causing the whole function to fail even if the survey itself was retrieved successfully. Consider adding error handling within the Promise.all to catch individual fetch failures and return null for that specific field while still returning the survey data with "Unknown" values for failed fetches.
| ), | |
| survey.locationObjectId | |
| ? fetchAndDeserialize<LocationDocument>( | |
| `/api/locations/${survey.locationObjectId}` | |
| ) | |
| : Promise.resolve(null) | |
| ).catch(() => null), | |
| survey.locationObjectId | |
| ? fetchAndDeserialize<LocationDocument>( | |
| `/api/locations/${survey.locationObjectId}` | |
| ).catch(() => null) | |
| : Promise.resolve<LocationDocument | null>(null) |
| surveyJson.pages = [ | ||
| ...surveyJson.pages.slice(0, 4), | ||
| surveyJson.pages[16], | ||
| surveyJson.pages[17] | ||
| ].filter(page => page !== undefined); |
There was a problem hiding this comment.
The code uses hardcoded indices 16 and 17 to access specific survey pages without validating that these indices exist in the array. While the filter removes undefined values, if the survey.json structure changes and these pages are moved or removed, this code will silently exclude them without any error or warning. Consider adding validation to ensure these pages exist, or better yet, select pages by their name property rather than hardcoded indices (e.g., surveyJson.pages.filter(page => ['volunteer-pre-screen', 'age_check', 'consent', 'survey-validation', 'giftCards', 'giftCards2'].includes(page.name))).
| .join('\n\n')} | ||
| </pre> | ||
| </div> | ||
| {/* Edit Pre-screen Questions Button */} |
There was a problem hiding this comment.
The comment on line 134 is outdated. It says "Edit Pre-screen Questions Button" but the button text has been changed to "Edit Gift Card Information" and its purpose has been updated to edit gift card related pages. The comment should be updated to reflect this: "Edit Gift Card Information Button"
| {/* Edit Pre-screen Questions Button */} | |
| {/* Edit Gift Card Information Button */} |
| // Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation | ||
| surveyJson.title = 'Homelessness Experience Survey (Edit Mode)'; | ||
| surveyJson.pages = surveyJson.pages.slice(0, 3); | ||
| // surveyJson.pages = surveyJson.pages.slice(0, 4); |
There was a problem hiding this comment.
There is commented-out code on line 21 that should be removed. Commented code in production codebase can cause confusion about intent and makes the code harder to maintain. If this line is no longer needed, it should be deleted entirely.
| // surveyJson.pages = surveyJson.pages.slice(0, 4); |
| @@ -18,7 +18,13 @@ export const initializeSurvey = ( | |||
| if (isEditMode) { | |||
| // Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation | |||
There was a problem hiding this comment.
The comment on line 19 is outdated and incorrect. It states "Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation" but the code now includes pages 0-3 (4 pages total) plus pages 16 and 17 (giftCards and giftCards2). The comment should be updated to accurately reflect the new behavior: "Edit mode includes volunteer-pre-screen, age_check, consent, survey-validation, giftCards, and giftCards2 pages"
| // Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation | |
| // Edit mode includes volunteer-pre-screen, age_check, consent, survey-validation, giftCards, and giftCards2 pages |
| throw new Error( | ||
| 'Survey not found or you do not have permission to view it' | ||
| ); | ||
| return null; |
There was a problem hiding this comment.
The error handling has been changed in a way that loses important information. Previously, the API would throw an error with a specific message ("Survey not found or you do not have permission to view it") that would be captured in the error variable from SWR. Now, the function returns null without distinguishing between a 404 (not found) and a 403 (no permission). This makes debugging more difficult and provides less helpful feedback. Consider checking the response status in fetchSurveyWithUser and throwing appropriate errors for different failure cases, or at minimum preserve the error information that SWR naturally provides.
| return null; | |
| throw new Error('Survey not found or you do not have permission to view it'); |
| .map(field => { | ||
| const answer = survey.responses[field]; | ||
| const label = labelMap[field] || field; | ||
| return `${label}: ${answer ?? 'N/A'}`; |
There was a problem hiding this comment.
The field 'email_phone_consent' is a checkbox type that returns an array of values (either ['email'], ['phone'], ['email', 'phone'], or an empty array/null). The current display code treats it as a simple value and will display it as comma-separated array items or "[object Object]" rather than in a user-friendly format. Consider adding special handling to display this as "Email, Phone", "Email", "Phone", or "None" based on the array contents.
| return `${label}: ${answer ?? 'N/A'}`; | |
| let formattedAnswer: string; | |
| if (field === 'email_phone_consent') { | |
| const value = answer as unknown; | |
| if ( | |
| !value || | |
| (Array.isArray(value) && value.length === 0) | |
| ) { | |
| formattedAnswer = 'None'; | |
| } else if (Array.isArray(value)) { | |
| const parts: string[] = []; | |
| if (value.includes('email')) { | |
| parts.push('Email'); | |
| } | |
| if (value.includes('phone')) { | |
| parts.push('Phone'); | |
| } | |
| formattedAnswer = parts.join(', ') || 'None'; | |
| } else { | |
| formattedAnswer = String(value); | |
| } | |
| } else { | |
| formattedAnswer = String(answer ?? 'N/A'); | |
| } | |
| return `${label}: ${formattedAnswer}`; |
📄 Description
I updated the Survey Details page.
✅ Checklist
🔗 Related Issues
Resolves #<issue-number>
💡 Type of change
🧪 How to test
Please go the Survey Dashboard, click on Survey Details and play around.
📝 Notes to reviewers