Skip to content

Commit 02d96b5

Browse files
committed
ranking form: implement two-column variant for the caregiver flow
1 parent 8598044 commit 02d96b5

File tree

4 files changed

+283
-19
lines changed

4 files changed

+283
-19
lines changed

frontend/src/components/ranking/caregiver-matching-form.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { COLORS } from '@/constants/form';
99
interface CaregiverMatchingFormProps {
1010
volunteerType: string;
1111
onVolunteerTypeChange: (type: string) => void;
12-
onNext: () => void;
12+
onNext: (type: string) => void;
1313
}
1414

1515
export function CaregiverMatchingForm({
@@ -126,7 +126,7 @@ export function CaregiverMatchingForm({
126126
color="white"
127127
_hover={{ bg: COLORS.teal }}
128128
_active={{ bg: COLORS.teal }}
129-
onClick={onNext}
129+
onClick={() => onNext(volunteerType)}
130130
disabled={!volunteerType}
131131
w="auto"
132132
h="40px"
@@ -139,4 +139,4 @@ export function CaregiverMatchingForm({
139139
</Box>
140140
</Box>
141141
);
142-
}
142+
}
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import React from 'react';
2+
import { Box, Heading, Button, VStack, HStack, Text, SimpleGrid } from '@chakra-ui/react';
3+
import { Checkbox } from '@/components/ui/checkbox';
4+
import { COLORS } from '@/constants/form';
5+
6+
interface CaregiverTwoColumnQualitiesFormProps {
7+
selectedQualities: string[];
8+
onQualityToggle: (quality: string) => void;
9+
onNext: () => void;
10+
}
11+
12+
// Left column options – The volunteer is/has… ("…as me" phrasing)
13+
const VOLUNTEER_OPTIONS = [
14+
'the same age as me',
15+
'the same gender identity as me',
16+
'the same ethnic or cultural group as me',
17+
'the same marital status as me',
18+
'the same parental status as me',
19+
'the same diagnosis as me',
20+
'experience with PTSD',
21+
'experience with Relapse',
22+
'experience with Anxiety / Depression',
23+
'experience with Fertility Issues',
24+
];
25+
26+
// Right column options – Their loved one is/has… ("…as my loved one" phrasing)
27+
const LOVED_ONE_OPTIONS = [
28+
'the same age as my loved one',
29+
'the same gender identity as my loved one',
30+
'the same diagnosis as my loved one',
31+
'experience with Oral Chemotherapy',
32+
'experience with Radiation Therapy',
33+
'experience with PTSD',
34+
'experience with Relapse',
35+
'experience with Anxiety / Depression',
36+
'experience with Fertility Issues',
37+
];
38+
39+
export function CaregiverTwoColumnQualitiesForm({
40+
selectedQualities,
41+
onQualityToggle,
42+
onNext,
43+
}: CaregiverTwoColumnQualitiesFormProps) {
44+
const maxSelected = 5;
45+
const reachedMax = selectedQualities.length >= maxSelected;
46+
47+
return (
48+
<Box>
49+
<Heading
50+
as="h1"
51+
fontFamily="system-ui, -apple-system, sans-serif"
52+
fontWeight={600}
53+
color={COLORS.veniceBlue}
54+
fontSize="28px"
55+
mb={8}
56+
>
57+
Volunteer Matching Preferences
58+
</Heading>
59+
60+
<Box mb={10}>
61+
<HStack gap={3}>
62+
<Box flex="1">
63+
<Box h="3px" bg={COLORS.progressGray} borderRadius="full" />
64+
</Box>
65+
<Box flex="1">
66+
<Box h="3px" bg={COLORS.teal} borderRadius="full" />
67+
</Box>
68+
<Box flex="1">
69+
<Box h="3px" bg={COLORS.progressGray} borderRadius="full" />
70+
</Box>
71+
</HStack>
72+
</Box>
73+
74+
<Box mb={10}>
75+
<Heading
76+
as="h2"
77+
fontFamily="system-ui, -apple-system, sans-serif"
78+
fontWeight={600}
79+
color={COLORS.veniceBlue}
80+
fontSize="20px"
81+
mb={3}
82+
>
83+
Relevant Qualities in a Volunteer
84+
</Heading>
85+
<Text
86+
color={COLORS.fieldGray}
87+
fontFamily="system-ui, -apple-system, sans-serif"
88+
fontSize="15px"
89+
mb={2}
90+
>
91+
You will be ranking these qualities in the next step.
92+
</Text>
93+
<Text
94+
color={COLORS.veniceBlue}
95+
fontFamily="system-ui, -apple-system, sans-serif"
96+
fontSize="15px"
97+
fontWeight={600}
98+
mb={8}
99+
>
100+
Note that your volunteer is guaranteed to speak your language and have the same availability.
101+
</Text>
102+
103+
<VStack gap={5} align="start">
104+
<Box w="full">
105+
<Text
106+
color={COLORS.veniceBlue}
107+
fontFamily="system-ui, -apple-system, sans-serif"
108+
fontWeight={500}
109+
fontSize="14px"
110+
mb={1}
111+
>
112+
I would prefer that...
113+
</Text>
114+
<Text
115+
color={COLORS.fieldGray}
116+
fontFamily="system-ui, -apple-system, sans-serif"
117+
fontSize="12px"
118+
mb={4}
119+
>
120+
You can select a maximum of 5 across both categories. Please select at least one quality.
121+
</Text>
122+
123+
<SimpleGrid columns={{ base: 1, md: 2 }} gap={8} w="full">
124+
<Box>
125+
<Text
126+
color={COLORS.fieldGray}
127+
fontFamily="system-ui, -apple-system, sans-serif"
128+
fontSize="12px"
129+
mb={3}
130+
>
131+
The volunteer is/has...
132+
</Text>
133+
<VStack align="start" gap={3}>
134+
{VOLUNTEER_OPTIONS.map((quality) => (
135+
<Checkbox
136+
key={`vol-${quality}`}
137+
checked={selectedQualities.includes(quality)}
138+
onChange={() => onQualityToggle(quality)}
139+
disabled={!selectedQualities.includes(quality) && reachedMax}
140+
>
141+
<Text
142+
fontFamily="system-ui, -apple-system, sans-serif"
143+
fontSize="14px"
144+
color={COLORS.veniceBlue}
145+
>
146+
{quality}
147+
</Text>
148+
</Checkbox>
149+
))}
150+
</VStack>
151+
</Box>
152+
153+
<Box>
154+
<Text
155+
color={COLORS.fieldGray}
156+
fontFamily="system-ui, -apple-system, sans-serif"
157+
fontSize="12px"
158+
mb={3}
159+
>
160+
Their loved one is/has...
161+
</Text>
162+
<VStack align="start" gap={3}>
163+
{LOVED_ONE_OPTIONS.map((quality) => (
164+
<Checkbox
165+
key={`loved-${quality}`}
166+
checked={selectedQualities.includes(quality)}
167+
onChange={() => onQualityToggle(quality)}
168+
disabled={!selectedQualities.includes(quality) && reachedMax}
169+
>
170+
<Text
171+
fontFamily="system-ui, -apple-system, sans-serif"
172+
fontSize="14px"
173+
color={COLORS.veniceBlue}
174+
>
175+
{quality}
176+
</Text>
177+
</Checkbox>
178+
))}
179+
</VStack>
180+
</Box>
181+
</SimpleGrid>
182+
</Box>
183+
</VStack>
184+
</Box>
185+
186+
<Box w="full" display="flex" justifyContent="flex-end">
187+
<Button
188+
bg={COLORS.teal}
189+
color="white"
190+
_hover={{ bg: COLORS.teal }}
191+
_active={{ bg: COLORS.teal }}
192+
onClick={onNext}
193+
disabled={selectedQualities.length === 0}
194+
w="auto"
195+
h="40px"
196+
fontSize="14px"
197+
fontWeight={500}
198+
px={6}
199+
>
200+
Next Section →
201+
</Button>
202+
</Box>
203+
</Box>
204+
);
205+
}
206+
207+

frontend/src/components/ranking/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export { VolunteerMatchingForm } from './volunteer-matching-form';
22
export { VolunteerRankingForm } from './volunteer-ranking-form';
33
export { CaregiverMatchingForm } from './caregiver-matching-form';
44
export { CaregiverQualitiesForm } from './caregiver-qualities-form';
5-
export { CaregiverRankingForm } from './caregiver-ranking-form';
5+
export { CaregiverRankingForm } from './caregiver-ranking-form';
6+
export { CaregiverTwoColumnQualitiesForm } from './caregiver-two-column-qualities-form';

frontend/src/pages/participant/ranking.tsx

Lines changed: 71 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { useState } from 'react';
2-
import { Box, Flex, Heading, Text, Button, VStack, HStack } from '@chakra-ui/react';
3-
import { UserIcon, CheckMarkIcon, DragIcon, WelcomeScreen } from '@/components/ui';
4-
import { VolunteerMatchingForm, VolunteerRankingForm, CaregiverMatchingForm, CaregiverQualitiesForm, CaregiverRankingForm } from '@/components/ranking';
2+
import { Box, Flex, Heading, Text, VStack } from '@chakra-ui/react';
3+
import { UserIcon, CheckMarkIcon, WelcomeScreen } from '@/components/ui';
4+
import { VolunteerMatchingForm, VolunteerRankingForm, CaregiverMatchingForm, CaregiverQualitiesForm, CaregiverRankingForm, CaregiverTwoColumnQualitiesForm } from '@/components/ranking';
55
import { COLORS } from '@/constants/form';
66

77
const RANKING_STATEMENTS = [
@@ -24,20 +24,24 @@ interface RankingFormData {
2424
selectedQualities: string[];
2525
rankedPreferences: string[];
2626
volunteerType?: string;
27+
isCaregiverVolunteerFlow?: boolean;
2728
}
2829

2930
interface ParticipantRankingPageProps {
3031
participantType?: 'cancerPatient' | 'caregiver';
32+
caregiverHasCancer?: boolean;
3133
}
3234

3335
export default function ParticipantRankingPage({
34-
participantType = 'cancerPatient'
36+
participantType = 'caregiver',
37+
caregiverHasCancer = true,
3538
}: ParticipantRankingPageProps) {
3639
const [currentStep, setCurrentStep] = useState(1);
3740
const [formData, setFormData] = useState<RankingFormData>({
3841
selectedQualities: [],
3942
rankedPreferences: participantType === 'caregiver' ? [...CAREGIVER_RANKING_STATEMENTS] : [...RANKING_STATEMENTS],
4043
volunteerType: participantType === 'caregiver' ? '' : undefined,
44+
isCaregiverVolunteerFlow: undefined,
4145
});
4246

4347
const WelcomeScreenStep = () => (
@@ -64,7 +68,9 @@ export default function ParticipantRankingPage({
6468
const handleVolunteerTypeChange = (type: string) => {
6569
setFormData(prev => ({
6670
...prev,
67-
volunteerType: type
71+
volunteerType: type,
72+
// Derive explicit flow flag to avoid any async state timing issues
73+
isCaregiverVolunteerFlow: type === 'caringForLovedOne',
6874
}));
6975
};
7076

@@ -82,13 +88,25 @@ export default function ParticipantRankingPage({
8288
<CaregiverMatchingForm
8389
volunteerType={formData.volunteerType || ''}
8490
onVolunteerTypeChange={handleVolunteerTypeChange}
85-
onNext={() => setCurrentStep(3)}
91+
onNext={(type) => {
92+
// Ensure state is set before navigating
93+
setFormData(prev => ({
94+
...prev,
95+
volunteerType: type,
96+
isCaregiverVolunteerFlow: type === 'caringForLovedOne',
97+
}));
98+
setCurrentStep(3);
99+
}}
86100
/>
87101
) : (
88102
<VolunteerMatchingForm
89103
selectedQualities={formData.selectedQualities}
90104
onQualityToggle={toggleQuality}
91-
onNext={() => setCurrentStep(3)}
105+
onNext={() => {
106+
// Build ranking list from selected qualities
107+
setFormData(prev => ({ ...prev, rankedPreferences: [...prev.selectedQualities] }));
108+
setCurrentStep(3);
109+
}}
92110
/>
93111
)}
94112
</Box>
@@ -118,11 +136,51 @@ export default function ParticipantRankingPage({
118136
boxShadow="0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)"
119137
p={10}
120138
>
121-
<CaregiverQualitiesForm
122-
selectedQualities={formData.selectedQualities}
123-
onQualityToggle={toggleQuality}
124-
onNext={() => setCurrentStep(4)}
125-
/>
139+
{(
140+
// Prefer explicit flag; otherwise infer from value
141+
(formData.isCaregiverVolunteerFlow ?? false) ||
142+
(formData.volunteerType === 'caringForLovedOne') ||
143+
// Fallback: any non-similarDiagnosis value implies the loved-one flow
144+
(!!formData.volunteerType && formData.volunteerType !== 'similarDiagnosis')
145+
) ? (
146+
<CaregiverTwoColumnQualitiesForm
147+
selectedQualities={formData.selectedQualities}
148+
onQualityToggle={toggleQuality}
149+
onNext={() => {
150+
setFormData(prev => ({ ...prev, rankedPreferences: [...prev.selectedQualities] }));
151+
setCurrentStep(4);
152+
}}
153+
/>
154+
) : caregiverHasCancer ? (
155+
formData.volunteerType === 'similarDiagnosis' ? (
156+
<CaregiverQualitiesForm
157+
selectedQualities={formData.selectedQualities}
158+
onQualityToggle={toggleQuality}
159+
onNext={() => {
160+
setFormData(prev => ({ ...prev, rankedPreferences: [...prev.selectedQualities] }));
161+
setCurrentStep(4);
162+
}}
163+
/>
164+
) : (
165+
<VolunteerMatchingForm
166+
selectedQualities={formData.selectedQualities}
167+
onQualityToggle={toggleQuality}
168+
onNext={() => {
169+
setFormData(prev => ({ ...prev, rankedPreferences: [...prev.selectedQualities] }));
170+
setCurrentStep(4);
171+
}}
172+
/>
173+
)
174+
) : (
175+
<CaregiverQualitiesForm
176+
selectedQualities={formData.selectedQualities}
177+
onQualityToggle={toggleQuality}
178+
onNext={() => {
179+
setFormData(prev => ({ ...prev, rankedPreferences: [...prev.selectedQualities] }));
180+
setCurrentStep(4);
181+
}}
182+
/>
183+
)}
126184
</Box>
127185
</Flex>
128186
);
@@ -138,9 +196,7 @@ export default function ParticipantRankingPage({
138196
});
139197
};
140198

141-
const participantType = 'caregiver';
142-
143-
const nextStep = participantType === 'caregiver' ? 5 : 4;
199+
const nextStep = (participantType === 'caregiver') ? 5 : 4;
144200

145201
return (
146202
<Flex minH="100vh" bg={COLORS.lightGray} justify="center" py={12}>

0 commit comments

Comments
 (0)