-
Notifications
You must be signed in to change notification settings - Fork 0
Donation page UI updates #138
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
Changes from all commits
955345b
411a585
f1e8f34
0cceb9a
6125f6d
c9c8374
2ef5027
9adaa6a
be6c729
78e0b76
753d7b4
ed155ee
94c21e8
35a375f
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 |
|---|---|---|
|
|
@@ -11,8 +11,13 @@ import { Field, FieldError, FieldLabel } from "@/shared/ui/field" | |
| import { MarketingButton } from "@/shared/ui/marketing-button" | ||
| import { Input } from "@/shared/ui/input" | ||
| import { apiFetch } from "@/shared/lib/api-fetch" | ||
| import DonationForm from "./donation-form" | ||
|
|
||
| const donationTypes = ["Fixed Donation", "Tier Donation", "Monthly Pay"] as const | ||
| const donationTypes = [ | ||
| "Fixed Donation", | ||
| "Tier Donation", | ||
| "Monthly Pay", | ||
| ] as const | ||
| const donationAmounts = [100, 200, 1000] | ||
|
|
||
| const donationFormSchema = z.object({ | ||
|
|
@@ -46,7 +51,8 @@ export function DonationSupportSection() { | |
|
|
||
| const watchedDonationType = form.watch("donationType") | ||
| const showAmountSelector = | ||
| watchedDonationType === "Tier Donation" || watchedDonationType === "Monthly Pay" | ||
| watchedDonationType === "Tier Donation" || | ||
| watchedDonationType === "Monthly Pay" | ||
|
|
||
| async function onSubmit(data: z.infer<typeof donationFormSchema>) { | ||
| setSubmitting(true) | ||
|
|
@@ -63,229 +69,31 @@ export function DonationSupportSection() { | |
|
|
||
| window.location.href = result.url | ||
| } catch (err) { | ||
| toast.error(err instanceof Error ? err.message : "Something went wrong. Please try again.") | ||
| toast.error( | ||
| err instanceof Error | ||
| ? err.message | ||
| : "Something went wrong. Please try again." | ||
| ) | ||
| setSubmitting(false) | ||
| } | ||
| } | ||
|
|
||
| return ( | ||
| <section className="bg-white py-12.5 text-ma-text lg:py-25"> | ||
| <div className="mx-auto grid items-start gap-12 px-4 lg:max-w-7xl lg:grid-cols-2 lg:gap-6 lg:px-25 2xl:max-w-360 2xl:px-50"> | ||
| <section className="bg-[#ECE8FF] py-12.5 text-ma-text lg:py-25"> | ||
| <div className="mx-auto grid items-center gap-12 px-4 lg:max-w-7xl lg:grid-cols-2 lg:gap-6 lg:px-25 2xl:max-w-360 2xl:px-50"> | ||
| <div className="pt-0 lg:pt-2"> | ||
| <h2 className="font-sans text-[28px]/[100%] leading-[1.12] font-extrabold text-balance text-primary lg:text-[60px]/[70px] lg:tracking-tight-xl"> | ||
| Support us and make a difference for the future! | ||
| <h2 className="font-sans text-3xl leading-[1.12] font-extrabold text-balance text-primary sm:text-5xl sm:tracking-tight-xl"> | ||
| Invest in Hope | ||
| </h2> | ||
| <p className="mt-[30px] max-w-[506px] text-base leading-normal text-primary lg:text-lg"> | ||
| Together, we can make a real impact in communities around the world. | ||
| Help us bring hope and support. | ||
| Your gift helps fund: <br /> <br /> | ||
| AI & Digital Skills Education, Health Advocacy, Patient Resource | ||
| Navigation, Workforce Readiness, Chronic Illness Support, and | ||
| Endometriosis Education. | ||
| </p> | ||
| </div> | ||
|
|
||
| <form | ||
| onSubmit={form.handleSubmit(onSubmit)} | ||
| className="flex w-full flex-col gap-[30px] rounded-card-2 border border-ma-border-light bg-ma-surface-2 px-4 pt-[30px] pb-7 sm:px-[30px] lg:px-7" | ||
| > | ||
| <div className="border-b border-ma-border-light pb-2.5"> | ||
| <h3 className="text-2xl leading-normal font-bold text-black"> | ||
| Make Your Donation | ||
| </h3> | ||
| </div> | ||
|
|
||
| <Controller | ||
| control={form.control} | ||
| name="donationType" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid}> | ||
| <legend className="text-xl leading-normal font-semibold text-black"> | ||
| Type of Donation | ||
| </legend> | ||
|
|
||
| <div className="grid gap-3 sm:grid-cols-3"> | ||
| {donationTypes.map((type) => ( | ||
| <label | ||
| key={type} | ||
| className="flex min-w-0 items-start gap-2 text-base leading-normal text-muted-foreground" | ||
| > | ||
| <input | ||
| type="radio" | ||
| name={field.name} | ||
| value={type} | ||
| checked={field.value === type} | ||
| onChange={() => field.onChange(type)} | ||
| aria-invalid={fieldState.invalid} | ||
| className="mt-0.5 size-5 shrink-0 accent-ma-glow-violet" | ||
| /> | ||
| <span>{type}</span> | ||
| </label> | ||
| ))} | ||
| </div> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
|
|
||
| {showAmountSelector ? ( | ||
| <Controller | ||
| control={form.control} | ||
| name="amount" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid}> | ||
| <label className="text-xl leading-normal font-semibold text-black"> | ||
| Select Donation Amount | ||
| </label> | ||
| <div className="flex flex-col gap-2"> | ||
| {donationAmounts.map((amount) => ( | ||
| <label | ||
| key={amount} | ||
| className={`relative cursor-pointer rounded-[6px] ${ | ||
| field.value === amount | ||
| ? "bg-linear-[90deg] from-[#4F7CF7] from-[0%] to-[#7B5CFF] to-[68.27%] p-[1.1px] pb-[1.3px]" | ||
| : "border border-border" | ||
| }`} | ||
| > | ||
| <div | ||
| className={`flex items-center justify-between rounded-[5px] bg-white px-4 py-2.5 text-base/[100%] font-medium ${ | ||
| field.value === amount | ||
| ? "text-ma-text" | ||
| : "text-ma-text" | ||
| }`} | ||
| > | ||
| <span>${amount}</span> | ||
| <input | ||
| type="radio" | ||
| name={field.name} | ||
| value={amount} | ||
| checked={field.value === amount} | ||
| onChange={() => field.onChange(amount)} | ||
| aria-invalid={fieldState.invalid} | ||
| className="size-5 accent-ma-glow-violet" | ||
| /> | ||
| </div> | ||
| </label> | ||
| ))} | ||
| </div> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
| ) : ( | ||
| <Controller | ||
| control={form.control} | ||
| name="amount" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid}> | ||
| <label htmlFor={field.name} className="sr-only"> | ||
| Donation amount | ||
| </label> | ||
| <div className="relative"> | ||
| <Input | ||
| id={field.name} | ||
| type="number" | ||
| inputMode="decimal" | ||
| placeholder="Enter Amount" | ||
| value={field.value || ""} | ||
| onChange={(e) => field.onChange(parseFloat(e.target.value) || 0)} | ||
| aria-invalid={fieldState.invalid} | ||
| className="h-10 rounded-[6px] border-border bg-white px-4 py-2.5 pr-10 text-base placeholder:text-muted-foreground" | ||
| /> | ||
| <span | ||
| className="pointer-events-none absolute top-1/2 right-4 -translate-y-1/2 text-xl leading-none font-bold text-ma-text" | ||
| aria-hidden="true" | ||
| > | ||
| $ | ||
| </span> | ||
| </div> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
| )} | ||
|
|
||
| <div className="flex flex-col gap-4"> | ||
| <h3 className="text-xl leading-normal font-semibold text-black"> | ||
| Personal Info | ||
| </h3> | ||
|
|
||
| <Controller | ||
| control={form.control} | ||
| name="donorName" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid}> | ||
| <FieldLabel htmlFor={field.name}> | ||
| Full Name | ||
| </FieldLabel> | ||
| <Input | ||
| {...field} | ||
| id={field.name} | ||
| autoComplete="name" | ||
| placeholder="Enter full name" | ||
| aria-invalid={fieldState.invalid} | ||
| className="h-10 rounded-md border-border bg-white px-4 py-2.5 text-base placeholder:text-muted-foreground" | ||
| /> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
|
|
||
| <Controller | ||
| control={form.control} | ||
| name="donorEmail" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid}> | ||
| <FieldLabel htmlFor={field.name}> | ||
| </FieldLabel> | ||
| <Input | ||
| {...field} | ||
| id={field.name} | ||
| type="email" | ||
| autoComplete="email" | ||
| placeholder="Enter email" | ||
| aria-invalid={fieldState.invalid} | ||
| className="h-10 rounded-md border-border bg-white px-4 py-2.5 text-base placeholder:text-muted-foreground" | ||
| /> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
| </div> | ||
|
|
||
| <Controller | ||
| control={form.control} | ||
| name="confirmation" | ||
| render={({ field, fieldState }) => ( | ||
| <Field data-invalid={fieldState.invalid} orientation="horizontal"> | ||
| <input | ||
| type="checkbox" | ||
| id={field.name} | ||
| name={field.name} | ||
| checked={field.value === true} | ||
| onChange={(e) => field.onChange(e.target.checked)} | ||
| aria-invalid={fieldState.invalid} | ||
| className="mt-1 size-[18px] shrink-0 rounded border-border bg-white accent-ma-text" | ||
| /> | ||
| <label htmlFor={field.name} className="text-base leading-normal text-muted-foreground"> | ||
| By submitting this form, you confirm the accuracy of the donation | ||
| amount and authorize the payment processing via the checkout page. | ||
| </label> | ||
| {fieldState.invalid && <FieldError errors={[fieldState.error]} />} | ||
| </Field> | ||
| )} | ||
| /> | ||
|
|
||
| <MarketingButton | ||
| type="submit" | ||
| disabled={submitting} | ||
| className="w-full" | ||
| > | ||
| {submitting && <LoaderCircle className="size-4 animate-spin" aria-hidden="true" />} | ||
| Donate Now | ||
| <ArrowRight | ||
| className="size-5 transition-transform duration-300 group-hover:rotate-[-30deg]" | ||
| aria-hidden="true" | ||
| /> | ||
| </MarketingButton> | ||
| </form> | ||
| <DonationForm /> | ||
|
Comment on lines
81
to
+96
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Description: Verify DonationForm props/usage and confirm /api/donations callers.
set -euo pipefail
echo "=== DonationForm definition and props ==="
fd -t f 'donation-form.tsx' --exec cat -n {}
echo "=== All DonationForm usages ==="
rg -nP --type=tsx --type=ts -C4 '<DonationForm\b|from ["'"'"'].*donation-form["'"'"']'
echo "=== Callers of /api/donations ==="
rg -nP -C6 "['\"\`]/api/donations"
echo "=== Any <form> element in the donation feature ==="
rg -nP -C3 '<form\b' --glob '**/donation*'Repository: Smartlify07/Modern-Advocates Length of output: 5446 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo "=== donation-support-section outline ==="
ast-grep outline src/features/marketing/components/donation-support-section.tsx || true
echo "=== donation-support-section relevant source ==="
cat -n src/features/marketing/components/donation-support-section.tsx | sed -n '1,130p'
echo "=== DonationForm usage and donationForm import search ==="
rg -n --glob '**/*.{ts,tsx}' -C3 '<DonationForm\b|donation-form|donationTypes|donationFormSchema|useForm|api/donations|authorize-checkbox|custom-amount' src
echo "=== form/button submit wiring in donation-related files ==="
rg -n --glob '**/*.{ts,tsx}' -C2 '<form\b|type="submit"|onClick=|onSubmit=|donation.*submit|Donate Now|authorize-checkbox|custom-amount' src/features/marketing/components src/features --glob '!**/node_modules/**'Repository: Smartlify07/Modern-Advocates Length of output: 50384 Wire the donation form to its submission handler.
Move the form contract into [high_effort_and_HIGH_reward] 🤖 Prompt for AI Agents |
||
| </div> | ||
| </section> | ||
| ) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,7 @@ export function Footer() { | |
| return ( | ||
| <footer className="overflow-hidden bg-white text-ma-text"> | ||
| <div className="relative mx-auto min-h-125 px-4 pt-12.5 lg:max-w-7xl lg:px-25 lg:pt-17.5 2xl:max-w-360"> | ||
| <div className="grid gap-10 md:grid-cols-[0.9fr_0.6fr_1.5fr] lg:gap-10 xl:grid-cols-[254px_180px_1fr] xl:justify-between xl:gap-[154px] 2xl:gap-50"> | ||
| <div className="grid gap-10 md:grid-cols-[0.9fr_0.6fr_1.5fr] lg:gap-10 xl:grid-cols-[254px_180px_1fr] xl:justify-between xl:gap-[140px] 2xl:gap-50"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Use a spacing token instead of This class introduces an arbitrary spacing value. The repository guidelines prohibit arbitrary spacing. Reuse an existing spacing token. If 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| <section className=""> | ||
| <h2 className="font-sans text-lg font-extrabold lg:text-2xl"> | ||
| Get in Touch | ||
|
|
@@ -80,7 +80,7 @@ export function Footer() { | |
| /> | ||
| <button | ||
| type="submit" | ||
| className="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-pill bg-ma-text px-[15px] py-3 text-[12.31px] font-semibold text-white transition-transform duration-300 hover:scale-[1.1]" | ||
| className="group relative inline-flex items-center justify-center gap-2 overflow-hidden rounded-pill bg-ma-admin-primary px-[15px] py-3 text-[12.31px] font-semibold text-white transition-transform duration-300 hover:scale-[1.1]" | ||
| > | ||
| <span className="relative z-10 inline-flex items-center gap-2"> | ||
| Join | ||
|
|
||
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.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Update the
alttext to match the new image.The image now shows both founders, and the card below reads "Melanie And Will" / "Founders". The
alttext still names only Melanie Reyes as the founder. Screen reader users receive incorrect information.♿ Proposed fix for the alt text
<Image src="/figma-home/melanie-and-will.png" - alt="Melanie Reyes, founder of ModernAdvocates Inc." + alt="Melanie and Will, founders of ModernAdvocates Inc." priority📝 Committable suggestion
🤖 Prompt for AI Agents