-
Notifications
You must be signed in to change notification settings - Fork 11
Survey details re-design #172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: kc-pit-2026-test
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -352,38 +352,47 @@ export const useApi = () => { | |||||||||||||||||||||||||
| type SurveyWithUser = SurveyDocument & { | ||||||||||||||||||||||||||
| employeeName: string; | ||||||||||||||||||||||||||
| employeeId: string; | ||||||||||||||||||||||||||
| locationName: string; | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const useSurveyWithUser = ( | ||||||||||||||||||||||||||
| surveyObjectId: string | ||||||||||||||||||||||||||
| ): SWRResponse<SurveyWithUser> => { | ||||||||||||||||||||||||||
| surveyObjectId: string | undefined | ||||||||||||||||||||||||||
| ): SWRResponse<SurveyWithUser | null> => { | ||||||||||||||||||||||||||
| return useSWR( | ||||||||||||||||||||||||||
| surveyObjectId ? `/api/surveys/${surveyObjectId}` : null, | ||||||||||||||||||||||||||
| () => fetchSurveyWithUser(surveyObjectId) | ||||||||||||||||||||||||||
| () => fetchSurveyWithUser(surveyObjectId!) | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const fetchSurveyWithUser = async ( | ||||||||||||||||||||||||||
| surveyObjectId: string | ||||||||||||||||||||||||||
| ): Promise<SurveyWithUser> => { | ||||||||||||||||||||||||||
| ): Promise<SurveyWithUser | null> => { | ||||||||||||||||||||||||||
| const survey = await fetchAndDeserialize<SurveyDocument>( | ||||||||||||||||||||||||||
| `/api/surveys/${surveyObjectId}` | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| if (!survey) { | ||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||
| 'Survey not found or you do not have permission to view it' | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| const user = await fetchAndDeserialize<UserDocument>( | ||||||||||||||||||||||||||
| `/api/users/${survey.createdByUserObjectId}` | ||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Fetch user and location in parallel for efficiency | ||||||||||||||||||||||||||
| const [user, location] = await Promise.all([ | ||||||||||||||||||||||||||
| fetchAndDeserialize<UserDocument>( | ||||||||||||||||||||||||||
| `/api/users/${survey.createdByUserObjectId}` | ||||||||||||||||||||||||||
| ), | ||||||||||||||||||||||||||
| survey.locationObjectId | ||||||||||||||||||||||||||
| ? fetchAndDeserialize<LocationDocument>( | ||||||||||||||||||||||||||
| `/api/locations/${survey.locationObjectId}` | ||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||
| : Promise.resolve(null) | ||||||||||||||||||||||||||
|
Comment on lines
+381
to
+386
|
||||||||||||||||||||||||||
| ), | |
| 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) |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -18,7 +18,13 @@ export const initializeSurvey = ( | |||||
| if (isEditMode) { | ||||||
| // Edit mode only uses first 3 pages: volunteer-pre-screen, consent, survey-validation | ||||||
|
||||||
| // 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 |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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); |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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))).
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,9 +19,9 @@ export default function SurveyDetails() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { surveyService } = useApi(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: survey, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isLoading: loading, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| error | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = surveyService.useSurveyWithUser(id ?? '') || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| isLoading: loading | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } = surveyService.useSurveyWithUser(id) || {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const error = !loading && !survey; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const navigate = useNavigate(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const qrRefs = useRef<(HTMLDivElement | null)[]>([]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const ability = useAbility(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -33,40 +33,23 @@ export default function SurveyDetails() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const canEdit = survey | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? ability.can(ACTIONS.CASL.UPDATE, subject(SUBJECTS.SURVEY, survey)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fields to display in survey responses section | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const displayFields = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'first_two_letters_fname', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'first_two_letters_lname', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'date_of_birth', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'email_phone_consent', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'email', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'phone' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const labelMap: Record<string, string> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_two_letters_fname: 'First two letters of first name', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_two_letters_lname: 'First two letters of last name', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| year_born: 'Year born', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| month_born: 'Month born', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| location: 'Location', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| interpreter: 'Using interpreter?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| language: 'Language (if using interpreter)', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| phone_number: 'Phone number', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_two_letters_fname: 'First name initials', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_two_letters_lname: 'Last name initials', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| date_of_birth: 'Date of Birth', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email_phone_consent: 'Email/Phone Consent', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email: 'Email', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| email_consent: 'Consent to email', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| age_for_consent: 'Age 18 or over?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| consent_given: 'Oral consent given?', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| homeless_people_count: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'Number of people experiencing homelessness you know', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| people_you_know: 'People you know experiencing homelessness', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sleeping_location_last_night: 'Sleeping Location Last Night', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| homeless_duration_since_housing: 'Homeless Duration Since Housing', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| homeless_occurrences_past_3_years: 'Homeless Occurrences Past 3 Years', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| months_homeless: 'Months Homeless', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| age: 'Age', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hispanic_latino: 'Hispanic/Latino', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| veteran: 'Veteran', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fleeing_dv: 'Fleeing Domestic Violence', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| disability: 'Disability', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| mental_illness: 'Mental Illness', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| substance_abuse: 'Substance Abuse', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| city_lasthoused: 'City Last Housed', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| minutes_traveled: 'Minutes Traveled', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| events_conditions: 'Events/Conditions', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| shelter_preferences: 'Shelter Preferences', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| person_name: 'Name', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| relationship: 'Relationship', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| current_sleeping_location: 'Current Sleeping Location' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| phone: 'Phone' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (loading) return <p>Loading...</p>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -119,17 +102,53 @@ export default function SurveyDetails() { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="survey-info"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Employee ID:</strong> {survey.employeeId} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Survey Code:</strong> {survey.surveyCode} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Staff Name:</strong> {survey.employeeName} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Employee Name:</strong> {survey.employeeName} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Location:</strong> {survey.locationName} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Submitted At:</strong>{' '} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <strong>Date and Time:</strong>{' '} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {new Date(survey.createdAt).toLocaleString()} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </p> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| </div> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {/* Gift Card Information */} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <div className="responses-section"> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <h3>Gift Card Information</h3> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <pre> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| {survey.responses && | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| displayFields | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .filter(field => field in survey.responses) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(field => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const answer = survey.responses[field]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const label = labelMap[field] || field; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return `${label}: ${answer ?? 'N/A'}`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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}`; |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 */} |
Copilot
AI
Jan 26, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.