Skip to content

Commit 76e47eb

Browse files
committed
t1
1 parent b8c291d commit 76e47eb

File tree

10 files changed

+712
-1
lines changed

10 files changed

+712
-1
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { VolunteerMatchingForm } from './volunteer-matching-form';
2+
export { VolunteerRankingForm } from './volunteer-ranking-form';
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import React from 'react';
2+
import { Box, Heading, Button, VStack, HStack, Text } from '@chakra-ui/react';
3+
import { Checkbox } from '@/components/ui/checkbox';
4+
import { COLORS } from '@/constants/form';
5+
const MATCHING_QUALITIES = [
6+
'the same age as me',
7+
'the same gender identity as me',
8+
'the same ethnic or cultural group as me',
9+
'the same marital status as me',
10+
'the same parental status as me',
11+
'the same diagnosis as me',
12+
'experience with Oral Chemotherapy',
13+
'experience with Radiation Therapy',
14+
'experience with PTSD',
15+
'experience with Relapse',
16+
'experience with Anxiety / Depression',
17+
'experience with Fertility Issues',
18+
];
19+
20+
interface VolunteerMatchingFormProps {
21+
selectedQualities: string[];
22+
onQualityToggle: (quality: string) => void;
23+
onNext: () => void;
24+
}
25+
26+
export function VolunteerMatchingForm({
27+
selectedQualities,
28+
onQualityToggle,
29+
onNext
30+
}: VolunteerMatchingFormProps) {
31+
return (
32+
<Box>
33+
<Heading
34+
as="h1"
35+
fontFamily="system-ui, -apple-system, sans-serif"
36+
fontWeight={600}
37+
color={COLORS.veniceBlue}
38+
fontSize="28px"
39+
mb={8}
40+
>
41+
Volunteer Matching Preferences
42+
</Heading>
43+
44+
<Box mb={10}>
45+
<HStack gap={3}>
46+
<Box flex="1">
47+
<Box h="3px" bg={COLORS.teal} borderRadius="full" />
48+
</Box>
49+
<Box flex="1">
50+
<Box h="3px" bg={COLORS.progressGray} borderRadius="full" />
51+
</Box>
52+
</HStack>
53+
</Box>
54+
55+
<Box mb={10}>
56+
<Heading
57+
as="h2"
58+
fontFamily="system-ui, -apple-system, sans-serif"
59+
fontWeight={600}
60+
color={COLORS.veniceBlue}
61+
fontSize="20px"
62+
mb={3}
63+
>
64+
Relevant Qualities in a Volunteer
65+
</Heading>
66+
<Text
67+
color={COLORS.fieldGray}
68+
fontFamily="system-ui, -apple-system, sans-serif"
69+
fontSize="15px"
70+
mb={2}
71+
>
72+
You will be ranking these qualities in the next step.
73+
</Text>
74+
<Text
75+
color={COLORS.veniceBlue}
76+
fontFamily="system-ui, -apple-system, sans-serif"
77+
fontSize="15px"
78+
fontWeight={600}
79+
mb={8}
80+
>
81+
Note that your volunteer is guaranteed to speak your language and have the same availability.
82+
</Text>
83+
84+
<VStack gap={5}>
85+
<Box w="full">
86+
<Text
87+
color={COLORS.veniceBlue}
88+
fontFamily="system-ui, -apple-system, sans-serif"
89+
fontWeight={500}
90+
fontSize="14px"
91+
mb={2}
92+
>
93+
I would prefer a volunteer with...
94+
</Text>
95+
<Text
96+
color={COLORS.fieldGray}
97+
fontFamily="system-ui, -apple-system, sans-serif"
98+
fontSize="12px"
99+
mb={4}
100+
>
101+
You can select a maximum of 5. Please select at least one quality.
102+
</Text>
103+
104+
<VStack align="start" gap={3}>
105+
{MATCHING_QUALITIES.map((quality) => (
106+
<Checkbox
107+
key={quality}
108+
checked={selectedQualities.includes(quality)}
109+
onChange={() => onQualityToggle(quality)}
110+
disabled={!selectedQualities.includes(quality) && selectedQualities.length >= 5}
111+
>
112+
<Text
113+
fontFamily="system-ui, -apple-system, sans-serif"
114+
fontSize="14px"
115+
color={COLORS.veniceBlue}
116+
>
117+
{quality}
118+
</Text>
119+
</Checkbox>
120+
))}
121+
</VStack>
122+
</Box>
123+
</VStack>
124+
</Box>
125+
126+
<Box w="full" display="flex" justifyContent="flex-end">
127+
<Button
128+
bg={COLORS.teal}
129+
color="white"
130+
_hover={{ bg: COLORS.teal }}
131+
_active={{ bg: COLORS.teal }}
132+
onClick={onNext}
133+
disabled={selectedQualities.length === 0}
134+
w="auto"
135+
h="40px"
136+
fontSize="14px"
137+
fontWeight={500}
138+
px={6}
139+
>
140+
Next Section →
141+
</Button>
142+
</Box>
143+
</Box>
144+
);
145+
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
import React from 'react';
2+
import { Box, Heading, Button, VStack, HStack, Text } from '@chakra-ui/react';
3+
import { DragIcon } from '@/components/ui';
4+
import { COLORS } from '@/constants/form';
5+
6+
7+
8+
interface VolunteerRankingFormProps {
9+
rankedPreferences: string[];
10+
onMoveItem: (fromIndex: number, toIndex: number) => void;
11+
onSubmit: () => void;
12+
}
13+
14+
export function VolunteerRankingForm({
15+
rankedPreferences,
16+
onMoveItem,
17+
onSubmit
18+
}: VolunteerRankingFormProps) {
19+
const [draggedIndex, setDraggedIndex] = React.useState<number | null>(null);
20+
const [dropTargetIndex, setDropTargetIndex] = React.useState<number | null>(null);
21+
22+
const renderStatementWithBold = (statement: string) => {
23+
const boldPhrases = [
24+
'the same age as me',
25+
'the same diagnosis as me',
26+
'the same marital status as me',
27+
'the same ethnic or cultural group as me',
28+
'the same parental status as me'
29+
];
30+
31+
const phraseToBold = boldPhrases.find(phrase => statement.includes(phrase));
32+
33+
if (!phraseToBold) {
34+
return statement;
35+
}
36+
37+
const parts = statement.split(phraseToBold);
38+
39+
return (
40+
<>
41+
{parts[0]}
42+
<Text as="span" fontWeight={700}>
43+
{phraseToBold}
44+
</Text>
45+
{parts[1]}
46+
</>
47+
);
48+
};
49+
50+
const handleDragStart = (e: React.DragEvent, index: number) => {
51+
setDraggedIndex(index);
52+
e.dataTransfer.effectAllowed = 'move';
53+
e.dataTransfer.setData('text/html', index.toString());
54+
};
55+
56+
const handleDragOver = (e: React.DragEvent, index: number) => {
57+
e.preventDefault();
58+
e.dataTransfer.dropEffect = 'move';
59+
setDropTargetIndex(index);
60+
};
61+
62+
const handleDragLeave = () => {
63+
setDropTargetIndex(null);
64+
};
65+
66+
const handleDrop = (e: React.DragEvent, dropIndex: number) => {
67+
e.preventDefault();
68+
if (draggedIndex !== null && draggedIndex !== dropIndex) {
69+
onMoveItem(draggedIndex, dropIndex);
70+
}
71+
setDraggedIndex(null);
72+
setDropTargetIndex(null);
73+
};
74+
75+
const handleDragEnd = () => {
76+
setDraggedIndex(null);
77+
setDropTargetIndex(null);
78+
};
79+
return (
80+
<Box>
81+
<Heading
82+
as="h1"
83+
fontFamily="system-ui, -apple-system, sans-serif"
84+
fontWeight={600}
85+
color={COLORS.veniceBlue}
86+
fontSize="28px"
87+
mb={8}
88+
>
89+
Volunteer Matching Preferences
90+
</Heading>
91+
92+
<Box mb={10}>
93+
<HStack gap={3}>
94+
<Box flex="1">
95+
<Box h="3px" bg={COLORS.progressGray} borderRadius="full" />
96+
</Box>
97+
<Box flex="1">
98+
<Box h="3px" bg={COLORS.teal} borderRadius="full" />
99+
</Box>
100+
</HStack>
101+
</Box>
102+
103+
<Box mb={10}>
104+
<Heading
105+
as="h2"
106+
fontFamily="system-ui, -apple-system, sans-serif"
107+
fontWeight={600}
108+
color={COLORS.veniceBlue}
109+
fontSize="20px"
110+
mb={3}
111+
>
112+
Ranking Match Preferences
113+
</Heading>
114+
<Text
115+
color={COLORS.fieldGray}
116+
fontFamily="system-ui, -apple-system, sans-serif"
117+
fontSize="15px"
118+
mb={2}
119+
>
120+
This information will be used to match you with a suitable volunteer.
121+
</Text>
122+
<Text
123+
color={COLORS.veniceBlue}
124+
fontFamily="system-ui, -apple-system, sans-serif"
125+
fontSize="15px"
126+
fontWeight={600}
127+
mb={8}
128+
>
129+
Note that your volunteer is guaranteed to speak your language and have the same availability.
130+
</Text>
131+
132+
<VStack gap={5}>
133+
<Box w="full">
134+
<Text
135+
color={COLORS.fieldGray}
136+
fontFamily="system-ui, -apple-system, sans-serif"
137+
fontSize="14px"
138+
mb={2}
139+
>
140+
Rank the following statements in the order that you agree with them:
141+
</Text>
142+
<Text
143+
color={COLORS.fieldGray}
144+
fontFamily="system-ui, -apple-system, sans-serif"
145+
fontSize="12px"
146+
mb={6}
147+
>
148+
1 is most agreed, 5 is least agreed.
149+
</Text>
150+
151+
<VStack gap={3} align="start">
152+
{rankedPreferences.map((statement, index) => {
153+
const isDragging = draggedIndex === index;
154+
const isDropTarget = dropTargetIndex === index;
155+
156+
return (
157+
<HStack
158+
key={`ranking-item-${index}-${statement.slice(0, 20)}`}
159+
w="full"
160+
gap={4}
161+
align="center"
162+
>
163+
<Text
164+
fontFamily="system-ui, -apple-system, sans-serif"
165+
fontSize="16px"
166+
fontWeight={600}
167+
color={COLORS.veniceBlue}
168+
minW="20px"
169+
>
170+
{index + 1}.
171+
</Text>
172+
173+
<HStack
174+
flex="1"
175+
p={4}
176+
bg={isDragging ? "#e5e7eb" : isDropTarget ? "#dbeafe" : "#f9fafb"}
177+
border={`1px solid ${isDropTarget ? COLORS.teal : "#e5e7eb"}`}
178+
borderRadius="6px"
179+
cursor={isDragging ? 'grabbing' : 'grab'}
180+
gap={3}
181+
opacity={isDragging ? 0.5 : 1}
182+
transition="all 0.2s ease"
183+
draggable
184+
onDragStart={(e) => handleDragStart(e, index)}
185+
onDragOver={(e) => handleDragOver(e, index)}
186+
onDragLeave={handleDragLeave}
187+
onDrop={(e) => handleDrop(e, index)}
188+
onDragEnd={handleDragEnd}
189+
_hover={{
190+
borderColor: COLORS.teal,
191+
boxShadow: `0 0 0 1px ${COLORS.teal}20`,
192+
bg: isDragging ? "#e5e7eb" : "#f3f4f6"
193+
}}
194+
>
195+
<Box
196+
cursor={isDragging ? 'grabbing' : 'grab'}
197+
p={1}
198+
_hover={{ opacity: 0.7 }}
199+
>
200+
<DragIcon />
201+
</Box>
202+
203+
<Text
204+
fontFamily="system-ui, -apple-system, sans-serif"
205+
fontSize="14px"
206+
color={COLORS.veniceBlue}
207+
flex="1"
208+
userSelect="none"
209+
>
210+
{renderStatementWithBold(statement)}
211+
</Text>
212+
</HStack>
213+
</HStack>
214+
);
215+
})}
216+
</VStack>
217+
</Box>
218+
</VStack>
219+
</Box>
220+
221+
<Box w="full" display="flex" justifyContent="flex-end">
222+
<Button
223+
bg={COLORS.teal}
224+
color="white"
225+
_hover={{ bg: COLORS.teal }}
226+
_active={{ bg: COLORS.teal }}
227+
onClick={onSubmit}
228+
w="auto"
229+
h="40px"
230+
fontSize="14px"
231+
fontWeight={500}
232+
px={6}
233+
>
234+
Submit Preferences →
235+
</Button>
236+
</Box>
237+
</Box>
238+
);
239+
}

0 commit comments

Comments
 (0)