Skip to content

Commit 53de608

Browse files
authored
fixed styling and refactored step1 and step2 of the form to reduce redundancy and keep code DRY (#93)
1 parent 9dbf4d9 commit 53de608

12 files changed

Lines changed: 546 additions & 403 deletions

File tree

apps/frontend/src/components/GrowingGoal/GrowingGoal.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
white-space: nowrap;
2727
height: 12%;
2828
padding: 2%;
29-
background-color: #3d3e6e;
29+
background-color: #650D77;
3030
font-size: 5cqw;
3131
}
3232

apps/frontend/src/components/GrowingGoal/GrowingGoal.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export const GrowingGoal = (props: GrowingGoalProps) => {
9797
return (
9898
<div className={styles['goal-container']}>
9999
<div className={styles['description-label']}>{message}</div>
100+
100101
<div className={styles['growth-container']}>
101102
<div
102103
ref={growthContainerRef}
@@ -108,11 +109,14 @@ export const GrowingGoal = (props: GrowingGoalProps) => {
108109
mask: `conic-gradient(black 0deg ${progress}deg, transparent ${progress}deg 360deg)`,
109110
}}
110111
></div>
112+
111113
<Plant />
114+
112115
<div
113116
style={{ ...startHandleStyle, backgroundColor: '#650d77' }}
114117
className={styles['progress-bar-handle']}
115118
></div>
119+
116120
<div
117121
style={{
118122
...endHandleStyle,
@@ -140,6 +144,7 @@ export const GrowingGoal = (props: GrowingGoalProps) => {
140144
) : (
141145
<div className={styles['sample-donor-profile']}></div>
142146
)}
147+
143148
<div className={styles['sample-donor-amount']}>
144149
<b>
145150
{props.sampleDonation.name.length > 8
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Label } from '@components/ui/label';
2+
3+
type ToggleSwitchProps = {
4+
label: string;
5+
description: string;
6+
checked: boolean;
7+
onToggle: () => void;
8+
disabled?: boolean;
9+
};
10+
11+
export const ToggleSwitch = ({
12+
label,
13+
description,
14+
checked,
15+
onToggle,
16+
disabled = false,
17+
}: ToggleSwitchProps) => {
18+
return (
19+
<div className="flex flex-col w-full">
20+
<Label className="text-lg text-[#57585c] font-normal">{label}</Label>
21+
22+
<div className="flex flex-row items-center justify-between w-full">
23+
<span className="text-base text-[#333] whitespace-nowrap">
24+
{description}
25+
</span>
26+
27+
<div
28+
className={`flex items-center ml-4 ${disabled ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}`}
29+
role="switch"
30+
aria-checked={checked}
31+
onClick={() => !disabled && onToggle()}
32+
>
33+
<div
34+
className={`relative flex-shrink-0 w-10 aspect-[2/1] rounded-full transition-all duration-300 ease-in-out
35+
${checked ? 'border-2 border-[#2C8974] bg-[#F0F0F0]' : 'bg-gray-300'}`}
36+
>
37+
<div
38+
className={`absolute top-1/2 w-[40%] h-[70%] rounded-full -translate-y-1/2 transition-all duration-300 ease-in-out
39+
${checked ? 'bg-[#2C8974] left-[50%]' : 'bg-white left-[10%]'}`}
40+
/>
41+
</div>
42+
</div>
43+
</div>
44+
</div>
45+
);
46+
};

apps/frontend/src/containers/donations/DonationForm.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export const DonationForm: React.FC<DonationFormProps> = ({
2828
if (stepParam === '4') return 4;
2929
return 1;
3030
});
31+
3132
const [formData, setFormData] = useState<DonationFormData>({
3233
firstName: '',
3334
lastName: '',
@@ -258,6 +259,7 @@ export const DonationForm: React.FC<DonationFormProps> = ({
258259
onChange={handleInputChange}
259260
/>
260261
);
262+
261263
case 2:
262264
return (
263265
<Step2Details
@@ -267,9 +269,10 @@ export const DonationForm: React.FC<DonationFormProps> = ({
267269
onChange={handleInputChange}
268270
/>
269271
);
272+
270273
case 3:
271274
return <Step3Confirm formData={formData} />;
272-
case 4:
275+
273276
default:
274277
return <Step4Receipt receiptId={receiptId} />;
275278
}
@@ -290,17 +293,19 @@ export const DonationForm: React.FC<DonationFormProps> = ({
290293
>
291294
<div
292295
className={`w-[31%] aspect-[14/1] rounded-[10px] ${
293-
currentStep === 1 ? 'bg-[#650d77]' : 'bg-[#b3b3b3]'
296+
currentStep === 1 ? 'bg-[#650D77]' : 'bg-[#B3B3B3]'
294297
}`}
295298
></div>
299+
296300
<div
297301
className={`w-[31%] aspect-[14/1] rounded-[10px] ${
298-
currentStep === 2 ? 'bg-[#650d77]' : 'bg-[#b3b3b3]'
302+
currentStep === 2 ? 'bg-[#650D77]' : 'bg-[#B3B3B3]'
299303
}`}
300304
></div>
305+
301306
<div
302307
className={`w-[31%] aspect-[14/1] rounded-[10px] ${
303-
currentStep === 3 ? 'bg-[#650d77]' : 'bg-[#b3b3b3]'
308+
currentStep === 3 ? 'bg-[#650D77]' : 'bg-[#B3B3B3]'
304309
}`}
305310
></div>
306311
</div>
Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,79 @@
11
import { useState } from 'react';
2-
import './donations.css';
32

4-
interface DonationSummaryData {
3+
type DonationSummaryProps = {
54
setCurrentAmount?: React.Dispatch<React.SetStateAction<number>>;
65
baseAmount: number;
76
feeRate?: number;
87
fixedFee?: number;
9-
}
8+
};
109

1110
export const DONATION_FEE_RATE = 2.9;
1211
export const DONATION_FIXED_FEE = 0.3;
1312

14-
export const DonationSummary: React.FC<DonationSummaryData> = ({
13+
export const DonationSummary = ({
1514
setCurrentAmount,
1615
baseAmount,
1716
feeRate,
1817
fixedFee,
19-
}) => {
20-
const [feeApplied, setFeeApplied] = useState<boolean>(false);
18+
}: DonationSummaryProps) => {
19+
const [feeApplied, setFeeApplied] = useState(false);
2120

2221
const rate = feeRate ?? DONATION_FEE_RATE;
2322
const fee = fixedFee ?? DONATION_FIXED_FEE;
2423
const feeTotal = (baseAmount * rate) / 100 + fee;
2524

25+
const handleToggle = () => {
26+
const next = !feeApplied;
27+
setFeeApplied(next);
28+
setCurrentAmount?.(next ? baseAmount + feeTotal : baseAmount);
29+
};
30+
31+
const feeText = `Add $${feeTotal.toFixed(2)} to cover transaction fees and tip the fundraising platform to help keep it `;
32+
33+
const toggleClass = feeApplied
34+
? 'border-2 border-[#2C8974] bg-[#F0F0F0]'
35+
: 'bg-gray-300';
36+
37+
const circleClass = feeApplied
38+
? 'bg-[#2C8974] left-[50%]'
39+
: 'bg-white left-[10%]';
40+
2641
return (
27-
<div className="donation-summary-container">
28-
<div className="donation-summary">
42+
<div className="w-full border border-black rounded-xl p-4 flex flex-col gap-3">
43+
<div className="flex items-start gap-3 w-full">
2944
<div
3045
data-testid="fee-toggle"
31-
className="toggle-container"
32-
onClick={() => {
33-
if (setCurrentAmount) {
34-
if (feeApplied) {
35-
setCurrentAmount(baseAmount);
36-
} else {
37-
setCurrentAmount(baseAmount + feeTotal);
38-
}
39-
}
40-
setFeeApplied(!feeApplied);
41-
}}
46+
className="flex-shrink-0 cursor-pointer mt-1"
47+
onClick={handleToggle}
4248
>
43-
<div className={`toggle-slider ${feeApplied ? 'on' : 'off'}`}>
44-
<div className="toggle-circle"></div>
49+
<div
50+
className={`relative w-10 aspect-[2/1] rounded-full transition-all duration-300 ease-in-out shadow-[inset_0_0_3px_rgba(0,0,0,0.2)] ${toggleClass}`}
51+
>
52+
<div
53+
className={`absolute top-1/2 w-[40%] h-[70%] rounded-full -translate-y-1/2 transition-all duration-300 ease-in-out ${circleClass}`}
54+
/>
4555
</div>
46-
<span className="toggle-label">
47-
Add ${feeTotal.toFixed(2)} to cover transaction fees and tip the
48-
fundraising platform to help keep it{' '}
49-
<a
50-
style={{ color: 'black' }}
51-
target="_blank"
52-
href="https://www.givelively.org/free#what-it-means"
53-
rel="noreferrer"
54-
>
55-
free for nonprofits.
56-
</a>
57-
</span>
58-
</div>
59-
<div className="toggle-fee-edit">Edit Fees & Tips</div>
60-
<div className="donation-total" data-testid="donation-total">
61-
${(feeApplied ? baseAmount + feeTotal : baseAmount).toFixed(2)}
6256
</div>
57+
58+
<p className="text-sm text-[#333] pb-2">
59+
{feeText}
60+
<a
61+
className="underline text-black"
62+
target="_blank"
63+
href="https://www.givelively.org/free#what-it-means"
64+
rel="noreferrer"
65+
>
66+
free for nonprofits.
67+
</a>
68+
</p>
6369
</div>
70+
71+
<button
72+
type="button"
73+
className="self-start ml-[52px] px-4 py-1.5 rounded-full bg-gray-100 text-sm text-gray-500 font-medium hover:bg-gray-200 transition-colors"
74+
>
75+
Edit Fees &amp; Tips
76+
</button>
6477
</div>
6578
);
6679
};
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { cn } from '@lib/utils';
2+
import { Label } from '@components/ui/label';
3+
import { Button } from '@components/ui/button';
4+
import { Textarea } from '@components/ui/textarea';
5+
import { Checkbox } from '@components/ui/checkbox';
6+
import type { DedicationKind } from '../donation-form.types';
7+
8+
type DedicationSectionProps = {
9+
dedicationKind: DedicationKind;
10+
dedicationMessage: string;
11+
showDedicationPublicly: boolean;
12+
isSubmitting: boolean;
13+
onDedicationKindClick: (kind: DedicationKind) => void;
14+
onMessageChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
15+
onShowPubliclyToggle: (checked: boolean) => void;
16+
};
17+
18+
export const DedicationSection = ({
19+
dedicationKind,
20+
dedicationMessage,
21+
showDedicationPublicly,
22+
isSubmitting,
23+
onDedicationKindClick,
24+
onMessageChange,
25+
onShowPubliclyToggle,
26+
}: DedicationSectionProps) => {
27+
const buttonClass =
28+
'flex-1 h-12 px-4 whitespace-nowrap rounded border text-base cursor-pointer transition-colors duration-150 ease-in-out font-semibold disabled:opacity-60 disabled:cursor-not-allowed';
29+
const activeClass = 'bg-[#007b64] text-white border-[#007b64]';
30+
const inactiveClass = 'bg-white text-black border-gray-300';
31+
32+
return (
33+
<div className="flex flex-col w-full gap-4">
34+
<div className="flex w-full gap-4">
35+
<Button
36+
type="button"
37+
className={cn(
38+
buttonClass,
39+
dedicationKind === 'honor' ? activeClass : inactiveClass,
40+
)}
41+
onClick={() => onDedicationKindClick('honor')}
42+
disabled={isSubmitting}
43+
>
44+
In Honor Of
45+
</Button>
46+
47+
<Button
48+
type="button"
49+
className={cn(
50+
buttonClass,
51+
dedicationKind === 'memory' ? activeClass : inactiveClass,
52+
)}
53+
onClick={() => onDedicationKindClick('memory')}
54+
disabled={isSubmitting}
55+
>
56+
In Memory Of
57+
</Button>
58+
</div>
59+
60+
<Textarea
61+
id="dedicationMessage"
62+
name="dedicationMessage"
63+
value={dedicationMessage}
64+
onChange={onMessageChange}
65+
rows={4}
66+
disabled={isSubmitting}
67+
placeholder="Write a message.."
68+
/>
69+
70+
<div className="flex items-center gap-2 text-[#57585c] text-base font-normal">
71+
<Checkbox
72+
id="showDedicationPublicly"
73+
name="showDedicationPublicly"
74+
checked={showDedicationPublicly}
75+
onCheckedChange={(checked) => onShowPubliclyToggle(!!checked)}
76+
disabled={isSubmitting}
77+
/>
78+
79+
<Label htmlFor="showDedicationPublicly">
80+
Show dedication message publicly
81+
</Label>
82+
</div>
83+
</div>
84+
);
85+
};

0 commit comments

Comments
 (0)