@@ -117,7 +120,10 @@ export function ContactHeroSection({
name="name"
render={({ field, fieldState }) => (
-
+
Full Name
- {fieldState.invalid && }
+ {fieldState.invalid && (
+
+ )}
)}
/>
@@ -139,7 +147,10 @@ export function ContactHeroSection({
name="email"
render={({ field, fieldState }) => (
-
+
Email
- {fieldState.invalid && }
+ {fieldState.invalid && (
+
+ )}
)}
/>
@@ -161,7 +174,10 @@ export function ContactHeroSection({
name="phone"
render={({ field, fieldState }) => (
-
+
Phone number
- {fieldState.invalid && }
+ {fieldState.invalid && (
+
+ )}
)}
/>
@@ -184,7 +202,10 @@ export function ContactHeroSection({
name="message"
render={({ field, fieldState }) => (
-
+
Message
- {fieldState.invalid && }
+ {fieldState.invalid && (
+
+ )}
)}
/>
@@ -204,7 +227,12 @@ export function ContactHeroSection({
disabled={submitting}
className="mt-1 w-full"
>
- {submitting && }
+ {submitting && (
+
+ )}
Send your message
-
+
diff --git a/src/features/marketing/components/course-detail-hero-section.tsx b/src/features/marketing/components/course-detail-hero-section.tsx
index 6e4575d..d9d329a 100644
--- a/src/features/marketing/components/course-detail-hero-section.tsx
+++ b/src/features/marketing/components/course-detail-hero-section.tsx
@@ -16,15 +16,28 @@ type CourseHeroData = {
enrollmentCount: number
}
-export function CourseDetailHeroSection({ course, breadcrumbHref = "/courses" }: { course: CourseHeroData; breadcrumbHref?: string }) {
+export function CourseDetailHeroSection({
+ course,
+ breadcrumbHref = "/courses",
+}: {
+ course: CourseHeroData
+ breadcrumbHref?: string
+}) {
const displayPrice = course.discountedPrice ?? course.price
return (
-
+
{course.thumbnailUrl ? (
-
+
) : (
)}
@@ -32,15 +45,30 @@ export function CourseDetailHeroSection({ course, breadcrumbHref = "/courses" }:
-
- Course
-
+
+
+ Course
+
+
{course.title}
-
{course.title}
-
{course.instructorName ?? "Instructor"}
+
+ {course.title}
+
+
+ {course.instructorName ?? "Instructor"}
+
@@ -51,7 +79,10 @@ export function CourseDetailHeroSection({ course, breadcrumbHref = "/courses" }:
{course.enrollmentCount} Students Enrolled
-
+
{course.avgRating.toFixed(1)}
@@ -60,9 +91,13 @@ export function CourseDetailHeroSection({ course, breadcrumbHref = "/courses" }:
-
$ {displayPrice.toFixed(2)} USD
+
+ $ {displayPrice.toFixed(2)} USD
+
{course.discountedPrice && (
-
$ {course.price.toFixed(2)} USD
+
+ $ {course.price.toFixed(2)} USD
+
)}
diff --git a/src/features/marketing/components/course-information-card.tsx b/src/features/marketing/components/course-information-card.tsx
index 75e4a79..6d07309 100644
--- a/src/features/marketing/components/course-information-card.tsx
+++ b/src/features/marketing/components/course-information-card.tsx
@@ -1,7 +1,10 @@
import { Clock, Languages, Layers, PlayCircle } from "lucide-react"
import { EnrollNowButton } from "@/features/courses/components/enroll-now-button"
-import { minutesToDuration, type DurationUnit } from "@/features/courses/lib/duration"
+import {
+ minutesToDuration,
+ type DurationUnit,
+} from "@/features/courses/lib/duration"
type CourseContentData = {
id: string
@@ -56,8 +59,6 @@ export function CourseInformationCard({
{ label: "Language:", value: languageDisplay, icon: Languages },
]
- console.log(course)
-
return (
diff --git a/src/features/marketing/components/courses-hero-section.tsx b/src/features/marketing/components/courses-hero-section.tsx
index 9153fe1..ee6d9b2 100644
--- a/src/features/marketing/components/courses-hero-section.tsx
+++ b/src/features/marketing/components/courses-hero-section.tsx
@@ -16,30 +16,30 @@ export function CoursesHeroSection() {
})
return (
-
-
+
+
- Courses
+ Trainings
Build the Skills That Keep You Moving Forward
-
+
Our AI training sessions are designed for people who are managing
life's challenges and still showing up. No tech background
required.
-
+
{isLoading
? Array.from({ length: 3 }).map((_, i) => (
diff --git a/src/features/marketing/components/cta-section.tsx b/src/features/marketing/components/cta-section.tsx
index eebcc50..80ab406 100644
--- a/src/features/marketing/components/cta-section.tsx
+++ b/src/features/marketing/components/cta-section.tsx
@@ -4,8 +4,8 @@ import Link from "next/link"
export function CtaSection() {
return (
-
-
+
+
Help Someone Find Their Way Forward{" "}
diff --git a/src/features/marketing/components/donation-cta-section.tsx b/src/features/marketing/components/donation-cta-section.tsx
index 6b8a16f..c374e12 100644
--- a/src/features/marketing/components/donation-cta-section.tsx
+++ b/src/features/marketing/components/donation-cta-section.tsx
@@ -3,7 +3,7 @@ import Link from "next/link"
export function DonationCtaSection() {
return (
-
+
diff --git a/src/features/marketing/components/donation-form.tsx b/src/features/marketing/components/donation-form.tsx
index c5b0610..ac0a92a 100644
--- a/src/features/marketing/components/donation-form.tsx
+++ b/src/features/marketing/components/donation-form.tsx
@@ -1,137 +1,273 @@
-"use client"
-import { Button } from "@/shared/ui/button"
-import { Checkbox } from "@/shared/ui/checkbox"
-import { Field } from "@/shared/ui/field"
-import { Input } from "@/shared/ui/input"
-import { Label } from "@/shared/ui/label"
-import {
- Select,
- SelectContent,
- SelectGroup,
- SelectItem,
- SelectLabel,
- SelectTrigger,
- SelectValue,
-} from "@/shared/ui/select"
-import { cn } from "@/shared/utils"
-import { ArrowRight } from "lucide-react"
-import React, { useState } from "react"
-
-const prices: { id: number; amount: number }[] = [
- {
- id: 1,
- amount: 10,
- },
- {
- id: 2,
- amount: 20,
- },
- {
- id: 3,
- amount: 30,
- },
- {
- id: 4,
- amount: 40,
- },
-]
-
-type DonationType = { label: string; value: string }
-const donationTypes = [
- { label: "One time Donation", value: "one time" },
- { label: "Monthly Donation", value: "monthly_donation" },
- { label: "Fixed Donation", value: "fixed_donation" },
-]
-
-const DonationForm = () => {
- const [selectedPrice, setSelectedPrice] = useState(null)
- const [selectedDonationType, setSelectedDonationType] =
- useState("one_time")
- return (
-
-
-
- Make a Donation
-
-
- Help us bring hope. support, and real impact in communities
-
-
-
-
-
- {prices.map((price) => (
-
- ${price.amount}
-
- ))}
-
-
-
-
-
- Frequency
-
-
-
-
-
-
- {donationTypes.map((item) => (
-
- {item.label}
-
- ))}
-
-
-
-
-
-
-
-
- Authorize payment processing at the checkout page{" "}
-
-
-
-
-
- 3% Administration fee
- $0.6
-
-
- Total
- $300
-
-
-
- Donate Now
-
-
- )
-}
-
-export default DonationForm
+"use client"
+
+import { zodResolver } from "@hookform/resolvers/zod"
+import { ArrowRight, LoaderCircle } from "lucide-react"
+import { useState } from "react"
+import { Controller, useForm, useWatch } from "react-hook-form"
+import { toast } from "sonner"
+import * as z from "zod"
+
+import { apiFetch } from "@/shared/lib/api-fetch"
+import { Button } from "@/shared/ui/button"
+import { Checkbox } from "@/shared/ui/checkbox"
+import { Field, FieldError, FieldGroup, FieldLabel } from "@/shared/ui/field"
+import { Input } from "@/shared/ui/input"
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/shared/ui/select"
+import { cn } from "@/shared/utils"
+import { computeAdminFee, computeDonationTotal } from "@/features/marketing/lib/donation-pricing"
+
+const prices = [
+ { id: 1, amount: 10 },
+ { id: 2, amount: 20 },
+ { id: 3, amount: 30 },
+ { id: 4, amount: 40 },
+]
+
+const donationTypeOptions = [
+ { label: "Fixed Donation", value: "fixed" },
+ { label: "Tier Donation", value: "tier" },
+ { label: "Monthly Donation", value: "monthly" },
+] as const
+
+const donationFormSchema = z.object({
+ donationType: z.enum(["fixed", "tier", "monthly"]),
+ amount: z
+ .number({ message: "Enter a donation amount" })
+ .positive("Amount must be greater than 0"),
+ donorName: z.string().min(1, "Full name is required"),
+ donorEmail: z.email("Please enter a valid email address"),
+ confirmation: z.boolean(),
+})
+
+type DonationFormValues = z.infer
+
+const DonationForm = () => {
+ const [submitting, setSubmitting] = useState(false)
+ const form = useForm({
+ resolver: zodResolver(donationFormSchema),
+ defaultValues: {
+ donationType: "fixed",
+ amount: prices[1].amount,
+ donorName: "",
+ donorEmail: "",
+ confirmation: false,
+ },
+ })
+
+ const watchedAmount = useWatch({ control: form.control, name: "amount" })
+ const isConfirmed = useWatch({ control: form.control, name: "confirmation" })
+
+ const fee = computeAdminFee(watchedAmount)
+ const total = computeDonationTotal(watchedAmount)
+
+ async function onSubmit(data: DonationFormValues) {
+ setSubmitting(true)
+ try {
+ const result = await apiFetch<{ url: string }>("/api/donations", {
+ method: "POST",
+ body: {
+ amount: data.amount,
+ donorName: data.donorName,
+ donorEmail: data.donorEmail,
+ donationType: data.donationType,
+ },
+ })
+
+ window.location.assign(result.url)
+ } catch (err) {
+ toast.error(
+ err instanceof Error
+ ? err.message
+ : "Something went wrong. Please try again."
+ )
+ setSubmitting(false)
+ }
+ }
+
+ return (
+
+
+
+ Make a Donation
+
+
+ Help us bring hope. support, and real impact in communities
+
+
+
+
+
+
+
+ 3% Administration fee
+ ${fee.toFixed(2)}
+
+
+ Total
+ ${total.toFixed(2)}
+
+
+
+
+ {submitting && (
+
+ )}
+ Donate Now
+
+
+ )
+}
+
+export default DonationForm
diff --git a/src/features/marketing/components/donation-hero-section.tsx b/src/features/marketing/components/donation-hero-section.tsx
index c252243..b5236e6 100644
--- a/src/features/marketing/components/donation-hero-section.tsx
+++ b/src/features/marketing/components/donation-hero-section.tsx
@@ -2,8 +2,8 @@ import Image from "next/image"
export function DonationHeroSection() {
return (
-
-
+
+
Donation
@@ -25,12 +25,12 @@ export function DonationHeroSection() {
src="/figma-home/melanie-and-will.png"
alt="Melanie Reyes, founder of ModernAdvocates Inc."
priority
- className="rounded-card-2 object-cover sm:h-full"
+ className="w-full rounded-card-2 object-cover sm:h-full sm:w-auto"
width={510}
height={560}
/>
-
+
Melanie And Will
diff --git a/src/features/marketing/components/donation-support-section.tsx b/src/features/marketing/components/donation-support-section.tsx
index f7bde26..540cd11 100644
--- a/src/features/marketing/components/donation-support-section.tsx
+++ b/src/features/marketing/components/donation-support-section.tsx
@@ -1,86 +1,9 @@
-"use client"
-
-import { ArrowRight, LoaderCircle } from "lucide-react"
-import { useState } from "react"
-import { Controller, useForm } from "react-hook-form"
-import { zodResolver } from "@hookform/resolvers/zod"
-import { toast } from "sonner"
-import * as z from "zod"
-
-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 donationAmounts = [100, 200, 1000]
-
-const donationFormSchema = z.object({
- donationType: z.enum(donationTypes),
- amount: z.number().positive("Amount must be greater than 0"),
- donorName: z.string().min(1, "Full name is required"),
- donorEmail: z.email("Please enter a valid email address"),
- confirmation: z.boolean().refine((val) => val === true, {
- message: "You must confirm the donation to proceed",
- }),
-})
-
-const donationTypeMap: Record = {
- "Fixed Donation": "fixed",
- "Tier Donation": "tier",
- "Monthly Pay": "monthly",
-}
-
export function DonationSupportSection() {
- const [submitting, setSubmitting] = useState(false)
- const form = useForm>({
- resolver: zodResolver(donationFormSchema),
- defaultValues: {
- donationType: "Fixed Donation",
- amount: 0,
- donorName: "",
- donorEmail: "",
- confirmation: false,
- },
- })
-
- const watchedDonationType = form.watch("donationType")
- const showAmountSelector =
- watchedDonationType === "Tier Donation" ||
- watchedDonationType === "Monthly Pay"
-
- async function onSubmit(data: z.infer) {
- setSubmitting(true)
- try {
- const result = await apiFetch<{ url: string }>("/api/donations", {
- method: "POST",
- body: {
- amount: data.amount,
- donorName: data.donorName,
- donorEmail: data.donorEmail,
- donationType: donationTypeMap[data.donationType] ?? "fixed",
- },
- })
-
- window.location.href = result.url
- } catch (err) {
- toast.error(
- err instanceof Error
- ? err.message
- : "Something went wrong. Please try again."
- )
- setSubmitting(false)
- }
- }
-
return (
-
-
+
+
Invest in Hope
diff --git a/src/features/marketing/components/empowerment-section.tsx b/src/features/marketing/components/empowerment-section.tsx
index 8eaca03..153f9e6 100644
--- a/src/features/marketing/components/empowerment-section.tsx
+++ b/src/features/marketing/components/empowerment-section.tsx
@@ -7,7 +7,7 @@ import { useEffect, useState } from "react"
export function EmpowermentSection() {
return (
-
+
Empowering People to Move Forward
diff --git a/src/features/marketing/components/faq.tsx b/src/features/marketing/components/faq.tsx
index b0f50f8..f0ff8e7 100644
--- a/src/features/marketing/components/faq.tsx
+++ b/src/features/marketing/components/faq.tsx
@@ -44,8 +44,8 @@ export function Faq() {
}
return (
-
-
+
+
diff --git a/src/features/marketing/components/footer.tsx b/src/features/marketing/components/footer.tsx
index 639ac6c..935dab0 100644
--- a/src/features/marketing/components/footer.tsx
+++ b/src/features/marketing/components/footer.tsx
@@ -12,8 +12,8 @@ const quickLinks = [
export function Footer() {
return (
-
-
+
+
Get in Touch
@@ -59,11 +59,11 @@ export function Footer() {
-
+
Join our News letter
-
+
Stay connected and informed: Join our newsletter for the latest
updates, inspiration, and design insights.
diff --git a/src/features/marketing/components/how-can-we-help.tsx b/src/features/marketing/components/how-can-we-help.tsx
index 090eafc..3d917dd 100644
--- a/src/features/marketing/components/how-can-we-help.tsx
+++ b/src/features/marketing/components/how-can-we-help.tsx
@@ -27,8 +27,8 @@ const cardsData = [
const HowCanWeHelpSection = () => {
return (
-
-
+
+
diff --git a/src/features/marketing/components/how-can-we-support.tsx b/src/features/marketing/components/how-can-we-support.tsx
index de4f249..bcb30a4 100644
--- a/src/features/marketing/components/how-can-we-support.tsx
+++ b/src/features/marketing/components/how-can-we-support.tsx
@@ -6,8 +6,8 @@ import React from "react"
const SupportSection = () => {
return (
-
-
+
+
How can we support your healthcare journey?{" "}
diff --git a/src/features/marketing/components/invest-in-hope-section.tsx b/src/features/marketing/components/invest-in-hope-section.tsx
index 71551f7..cb22022 100644
--- a/src/features/marketing/components/invest-in-hope-section.tsx
+++ b/src/features/marketing/components/invest-in-hope-section.tsx
@@ -3,8 +3,8 @@ import DonationForm from "./donation-form"
const InvestInHopeSection = () => {
return (
-
-
+
+
Invest in Hope
diff --git a/src/features/marketing/components/mission-sections.tsx b/src/features/marketing/components/mission-sections.tsx
index 947e5c2..5140d3a 100644
--- a/src/features/marketing/components/mission-sections.tsx
+++ b/src/features/marketing/components/mission-sections.tsx
@@ -28,7 +28,7 @@ export function MissionBridgeSection() {
return (
-
+
Bridging the Gap between Technology and Care
@@ -193,7 +193,7 @@ export function MissionBridgeSection() {
export function FounderStorySection() {
return (
-
+
{/* Large screens */}
{
return (
-
+
Everyone deserves the opportunity to move forward{" "}
diff --git a/src/features/marketing/components/practical-pathways-section.tsx b/src/features/marketing/components/practical-pathways-section.tsx
index e723ff9..7a32a69 100644
--- a/src/features/marketing/components/practical-pathways-section.tsx
+++ b/src/features/marketing/components/practical-pathways-section.tsx
@@ -3,7 +3,7 @@ import Image from "next/image"
const PracticalPathwaysSection = () => {
return (
-
+
Practical pathways to improved health outcomes
diff --git a/src/features/marketing/components/stories-from-our-community-section.tsx b/src/features/marketing/components/stories-from-our-community-section.tsx
index 5243043..52f11c5 100644
--- a/src/features/marketing/components/stories-from-our-community-section.tsx
+++ b/src/features/marketing/components/stories-from-our-community-section.tsx
@@ -71,8 +71,8 @@ export const StoriesSection = () => {
}
return (
-
-
+
+
Stories from our community
diff --git a/src/features/marketing/components/testimonials.tsx b/src/features/marketing/components/testimonials.tsx
index fcc4652..d26064a 100644
--- a/src/features/marketing/components/testimonials.tsx
+++ b/src/features/marketing/components/testimonials.tsx
@@ -59,8 +59,8 @@ export function Testimonials() {
}
return (
-
-
+
+
diff --git a/src/features/marketing/lib/donation-pricing.ts b/src/features/marketing/lib/donation-pricing.ts
new file mode 100644
index 0000000..a311322
--- /dev/null
+++ b/src/features/marketing/lib/donation-pricing.ts
@@ -0,0 +1,9 @@
+export const ADMIN_FEE_RATE = 0.03
+
+export function computeAdminFee(amount: number) {
+ return Math.round(amount * ADMIN_FEE_RATE * 100) / 100
+}
+
+export function computeDonationTotal(amount: number) {
+ return Math.round((amount + computeAdminFee(amount)) * 100) / 100
+}
\ No newline at end of file
diff --git a/src/features/user-dashboard/components/course-player-navbar.tsx b/src/features/user-dashboard/components/course-player-navbar.tsx
index a7ff301..5b5bd27 100644
--- a/src/features/user-dashboard/components/course-player-navbar.tsx
+++ b/src/features/user-dashboard/components/course-player-navbar.tsx
@@ -21,7 +21,9 @@ export default function CoursePlayerNavbar() {
const { data: enrollment } = useQuery({
queryKey: queryKeys.enrollment.progress(courseId),
queryFn: () =>
- apiFetch<{ id: string; progress: number }>(`/api/enrollments/by-course/${courseId}`),
+ apiFetch<{ id: string; progress: number }>(
+ `/api/enrollments/by-course/${courseId}`
+ ),
enabled: !!courseId,
})
@@ -34,7 +36,7 @@ export default function CoursePlayerNavbar() {
return (
-
+
-
+
-
+
-
+
(
{ status: error.status }
)
}
+ console.error(error)
Sentry.captureException(error)
return NextResponse.json(
{ error: "Internal server error" },
diff --git a/src/shared/ui/marketing-button.tsx b/src/shared/ui/marketing-button.tsx
index 0f0b892..855e16a 100644
--- a/src/shared/ui/marketing-button.tsx
+++ b/src/shared/ui/marketing-button.tsx
@@ -18,7 +18,7 @@ export function MarketingButton({
className={cn(
"group relative h-pill overflow-hidden rounded-pill px-5 py-4 text-base font-semibold disabled:opacity-60",
tone === "solid"
- ? "bg-ma-text text-white"
+ ? "text-white"
: "border-ma-text/20 bg-white text-ma-text",
className
)}
@@ -30,4 +30,4 @@ export function MarketingButton({
)
-}
\ No newline at end of file
+}
diff --git a/src/shared/ui/marketing-container.tsx b/src/shared/ui/marketing-container.tsx
index 81e14b2..5a34674 100644
--- a/src/shared/ui/marketing-container.tsx
+++ b/src/shared/ui/marketing-container.tsx
@@ -8,10 +8,7 @@ export function MarketingContainer({
return (
)