Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
955345b
feat: redesign marketing homepage sections
Smartlify07 Aug 6, 2026
411a585
fix: marketing section UI polish and responsive tweaks
Smartlify07 Aug 6, 2026
f1e8f34
fix: footer responsive spacing and typography tweaks
Smartlify07 Aug 6, 2026
0cceb9a
feat: add community imagery and responsive section improvements
Smartlify07 Aug 6, 2026
6125f6d
fix: use admin primary color for marketing CTA buttons
Smartlify07 Aug 6, 2026
c9c8374
fix: highlight active nav item and format api-handler
Smartlify07 Aug 6, 2026
2ef5027
feat: redesign about page sections and hero content
Smartlify07 Aug 7, 2026
9adaa6a
fix: responsive tweaks on about sections
Smartlify07 Aug 7, 2026
be6c729
merge: bring in latest main (client-side CSV export)
Smartlify07 Aug 7, 2026
78e0b76
ch: update donation page ui
Smartlify07 Aug 7, 2026
753d7b4
Merge branch 'main' of https://github.com/Smartlify07/Modern-Advocate…
Smartlify07 Aug 7, 2026
ed155ee
fix: donation page ui tweaks and button color
Smartlify07 Aug 7, 2026
94c21e8
fix: center donation support section content
Smartlify07 Aug 7, 2026
35a375f
fix: donation page typography tweaks
Smartlify07 Aug 7, 2026
b549ce4
feat: wire donation form to donations API with RHF validation
Smartlify07 Aug 8, 2026
c17e01f
refactor: marketing container utility and course detail page updates
Smartlify07 Aug 8, 2026
01e8b82
Merge remote-tracking branch 'origin/main' into feat/donation-form-api
Smartlify07 Aug 8, 2026
123073c
feat: include admin fee in Stripe donation charge and apply container…
Smartlify07 Aug 8, 2026
8008dc6
fix: adjust dashboard, dashboard-navbar, donation hero, and footer la…
Smartlify07 Aug 8, 2026
b732cca
refactor: apply container and cleanup in dashboard, footer, and playe…
Smartlify07 Aug 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app/(marketing)/donation/success/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { Button } from "@/shared/ui/button"
import { apiFetch } from "@/shared/lib/api-fetch"
import { queryKeys } from "@/shared/lib/query-keys"
import type { Donation } from "@/features/admin/donations/types"
import { computeDonationTotal } from "@/features/marketing/lib/donation-pricing"

function DonationSuccessContent() {
const searchParams = useSearchParams()
Expand Down Expand Up @@ -58,7 +59,7 @@ function DonationSuccessContent() {
<p className="max-w-md text-lg text-muted-foreground">
Your donation of{" "}
<span className="font-semibold text-ma-text">
${Number(donation?.amount ?? 0).toFixed(2)} USD
${computeDonationTotal(Number(donation?.amount ?? 0)).toFixed(2)} USD
</span>{" "}
has been received. Your support helps us make a real impact.
</p>
Expand Down
14 changes: 9 additions & 5 deletions src/app/(user)/dashboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function UserDashboardPage() {
}, [enrollments])

return (
<div className="mx-auto px-4 py-8 lg:max-w-7xl lg:px-25 lg:py-19.25 2xl:max-w-360 2xl:px-50">
<div className="marketing-container px-0! py-8! sm:py-19.25!">
<div className="mb-[70px] flex items-center gap-4 lg:mb-26.75">
<UserAvatar user={user} className="size-[50px] lg:size-12.5" />
<p className="text-xl font-bold text-ma-text lg:text-2xl">
Expand All @@ -73,11 +73,11 @@ export default function UserDashboardPage() {
</div>

{isLoading ? (
<div className="grid justify-items-center gap-5 md:grid-cols-2 lg:grid-cols-3">
<div className="grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{Array.from({ length: 6 }).map((_, i) => (
<div
key={i}
className="flex w-full flex-col gap-5 sm:max-w-[334px]"
className="flex w-full flex-col gap-5 sm:max-w-[300px]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use a design-token width for the loading card.

Line 80 changes the card width to another bracketed arbitrary value, sm:max-w-[300px]. Replace it with the repository’s named width token for 300px, or define that token before using it.

As per coding guidelines: “Do not use arbitrary hex colors or spacing; use the repository's design tokens.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(user)/dashboard/page.tsx at line 80, Update the loading card class
in the dashboard page to replace sm:max-w-[300px] with the repository’s existing
named 300px width design token; if no such token exists, define it in the
appropriate design-token configuration before referencing it.

Source: Coding guidelines

>
<Skeleton className="h-[254px] rounded-card-2" />
<div className="flex flex-col gap-2 px-2.5">
Expand Down Expand Up @@ -116,15 +116,19 @@ export default function UserDashboardPage() {
</ErrorStateAction>
</ErrorState>
) : (
<div className="grid justify-items-center gap-5 md:grid-cols-2 lg:grid-cols-3">
<div className="grid gap-5 md:grid-cols-2 lg:grid-cols-3">
{courses?.map((course) => {
const enrolledProgress = enrollmentMap.get(course.id)
const isEnrolled = enrolledProgress !== undefined

return (
<CourseCard.Root
key={course.id}
href={isEnrolled ? `/my-learning/${course.id}` : `/dashboard/course/${course.id}`}
href={
isEnrolled
? `/my-learning/${course.id}`
: `/dashboard/course/${course.id}`
}
>
<CourseCard.Thumbnail
src={course.thumbnailUrl}
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/donations/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { eq } from "drizzle-orm"
import { db } from "@/infrastructure/database/client"
import { donations } from "@/infrastructure/database/schema/donation"
import { getStripe } from "@/infrastructure/payment/stripe"
import { computeDonationTotal } from "@/features/marketing/lib/donation-pricing"
import * as Sentry from "@sentry/nextjs"

export async function POST(request: Request) {
Expand Down Expand Up @@ -65,14 +66,15 @@ export async function POST(request: Request) {
`${request.headers.get("x-forwarded-proto") ?? "https"}://${request.headers.get("host") ?? "localhost:3000"}`

const stripe = getStripe()
const total = computeDonationTotal(amount)
const session = await stripe.checkout.sessions.create({
mode: "payment",
customer_email: donorEmail,
line_items: [
{
price_data: {
currency: "usd",
unit_amount: Math.round(amount * 100),
unit_amount: Math.round(total * 100),
product_data: {
name: "Donation to Modern Advocates",
},
Expand Down
3 changes: 3 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@
}

@layer utilities {
.marketing-container {
@apply mx-auto px-4 py-12.5 sm:py-25 lg:max-w-7xl lg:px-25 2xl:max-w-360;
}
.hide-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
Expand Down
15 changes: 8 additions & 7 deletions src/features/courses/components/course-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ function Root({ href, children }: RootProps) {
return (
<Link
href={href}
className="group flex w-full flex-col gap-5 rounded-card-2 border border-ma-border-light bg-white px-2.5 pt-2.5 pb-7.5 transition-colors duration-300 hover:bg-gray-50 sm:max-w-[334px]"
className="group flex w-full flex-col gap-5 rounded-card-2 border border-ma-border-light bg-white px-2.5 pt-2.5 pb-7.5 transition-colors duration-300 hover:bg-gray-50 sm:max-w-[400px]"
>
{children}
</Link>
Expand Down Expand Up @@ -73,18 +73,17 @@ function Title({

function Tutor({ name }: { name: string | null }) {
return (
<p className="text-sm leading-normal font-medium text-muted-foreground">{name}</p>
<p className="text-sm leading-normal font-medium text-muted-foreground">
{name}
</p>
)
}

function Rating({ avg, count }: { avg: number; count: number }) {
return (
<div className="flex flex-wrap items-center gap-2">
<span className="inline-flex items-center gap-1 rounded-md border border-border p-[5px] text-sm leading-normal font-medium text-muted-foreground">
<Star
className="size-5 fill-ma-star text-ma-star"
aria-hidden="true"
/>
<Star className="size-5 fill-ma-star text-ma-star" aria-hidden="true" />
{Number(avg).toFixed(1)}
</span>
<span className="inline-flex items-center rounded-md border border-border px-[5px] py-1.5 text-sm leading-normal font-medium text-muted-foreground">
Expand Down Expand Up @@ -138,7 +137,9 @@ function DisplayPrice({
className?: string
}) {
return (
<p className={cn("text-base text-muted-foreground line-through", className)}>
<p
className={cn("text-base text-muted-foreground line-through", className)}
>
$ {displayPrice?.toFixed(2)} USD
</p>
)
Expand Down
6 changes: 4 additions & 2 deletions src/features/courses/components/enroll-now-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ export function EnrollNowButton({
queryKey: queryKeys.enrollment.check(courseId),
queryFn: async () => {
try {
return await apiFetch<{ enrolled: boolean }>(`/api/enrollments/check/${courseId}`)
return await apiFetch<{ enrolled: boolean }>(
`/api/enrollments/check/${courseId}`
)
} catch {
return { enrolled: false }
}
Expand Down Expand Up @@ -80,7 +82,7 @@ export function EnrollNowButton({
<Button asChild className="group relative overflow-hidden rounded-pill">
<Link
href={href}
className="flex h-pill w-full items-center justify-center gap-2.5 rounded-pill bg-ma-text px-5 py-4 text-base font-semibold text-white"
className="flex h-pill w-full items-center justify-center gap-2.5 rounded-pill bg-ma-admin-primary px-5 py-4 text-base font-semibold text-white"
>
<span className="relative z-10 inline-flex items-center gap-2.5">
{label}
Expand Down
4 changes: 2 additions & 2 deletions src/features/marketing/components/about-hero-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ const supporters = [

export function AboutHeroSection() {
return (
<section className="bg-white py-12.5 text-ma-text lg:py-25">
<div className="mx-auto px-4 lg:max-w-7xl lg:px-25 2xl:max-w-360">
<section className="bg-white text-ma-text">
<div className="marketing-container">
<header className="mx-auto mb-15 max-w-230 text-center">
<h4 className="mb-7.5 text-base text-muted-foreground uppercase">
Our story
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { Gift } from "lucide-react"

const AboutOurValuesSection = () => {
return (
<section id="about" className="bg-white py-12.5 lg:py-25">
<div className="mx-auto px-4 lg:max-w-7xl lg:px-25 2xl:max-w-360 2xl:px-50">
<section id="about" className="bg-white">
<div className="marketing-container">
<p className="mb-5 text-center text-sm leading-normal font-medium tracking-[10%] text-muted-foreground uppercase sm:text-base">
OUR VALUES{" "}
</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import React from "react"

const AboutSupportMissionSection = () => {
return (
<section className="">
<div className="mx-auto flex flex-col items-center px-4 py-12.5 sm:gap-50 sm:py-25 lg:max-w-7xl lg:px-25 2xl:max-w-360">
<div className="flex flex-col gap-12 sm:flex-row sm:items-center sm:gap-25">
<section>
<div className="marketing-container flex flex-col items-center border sm:gap-50">
<div className="flex w-full flex-col gap-12 sm:flex-row sm:items-center sm:gap-25">
<Image
src={"/figma-home/community.png"}
alt="People putting hands together"
Expand All @@ -26,7 +26,7 @@ const AboutSupportMissionSection = () => {
education, and health advocacy.
</p>

<Button className="h-13 w-full self-start rounded-pill sm:w-75 sm:max-w-75">
<Button className="h-15 w-full self-start rounded-pill sm:w-75 sm:max-w-75">
Support our mission <ArrowRight />
</Button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/features/marketing/components/community-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from "react"
const CommunitySection = () => {
return (
<section className="">
<div className="mx-auto flex flex-col-reverse items-center gap-12 px-4 py-12.5 sm:flex-row sm:gap-25 sm:py-25 lg:max-w-7xl lg:px-25 2xl:max-w-360">
<div className="marketing-container flex flex-col-reverse items-center gap-12 sm:flex-row sm:gap-25">
<Image
src="/figma-home/community.png"
alt="People putting hands together"
Expand Down
52 changes: 40 additions & 12 deletions src/features/marketing/components/contact-hero-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,14 @@ export function ContactHeroSection({
defaultName = "",
defaultEmail = "",
}: ContactHeroSectionProps) {
const { form, submitting, onSubmit } = useContactForm({ name: defaultName, email: defaultEmail })
const { form, submitting, onSubmit } = useContactForm({
name: defaultName,
email: defaultEmail,
})

return (
<section id="contact" className="bg-white py-12.5 text-ma-text lg:py-25">
<div className="mx-auto grid items-start gap-14 px-4 lg:max-w-7xl lg:grid-cols-[444px_1fr] lg:gap-[88px] lg:px-25 2xl:max-w-360 2xl:px-50">
<section id="contact" className="bg-white text-ma-text">
<div className="marketing-container grid items-start gap-14 lg:grid-cols-[444px_1fr] lg:gap-[88px]">
<div className="flex flex-col gap-10">
<div className="flex flex-col gap-5 lg:gap-[30px]">
<p className="text-base leading-normal font-medium tracking-[0.1em] text-muted-foreground uppercase">
Expand Down Expand Up @@ -117,7 +120,10 @@ export function ContactHeroSection({
name="name"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="text-lg font-normal text-ma-text">
<FieldLabel
htmlFor={field.name}
className="text-lg font-normal text-ma-text"
>
Full Name
</FieldLabel>
<Input
Expand All @@ -128,7 +134,9 @@ export function ContactHeroSection({
aria-invalid={fieldState.invalid}
className="h-9 rounded-md border-border bg-white px-2.5 py-2.5 text-base placeholder:text-muted-foreground"
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
Expand All @@ -139,7 +147,10 @@ export function ContactHeroSection({
name="email"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="text-lg font-normal text-ma-text">
<FieldLabel
htmlFor={field.name}
className="text-lg font-normal text-ma-text"
>
Email
</FieldLabel>
<Input
Expand All @@ -151,7 +162,9 @@ export function ContactHeroSection({
aria-invalid={fieldState.invalid}
className="h-9 rounded-md border-border bg-white px-2.5 py-2.5 text-base placeholder:text-muted-foreground"
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
Expand All @@ -161,7 +174,10 @@ export function ContactHeroSection({
name="phone"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="text-lg font-normal text-ma-text">
<FieldLabel
htmlFor={field.name}
className="text-lg font-normal text-ma-text"
>
Phone number
</FieldLabel>
<Input
Expand All @@ -173,7 +189,9 @@ export function ContactHeroSection({
aria-invalid={fieldState.invalid}
className="h-9 rounded-md border-border bg-white px-2.5 py-2.5 text-base placeholder:text-muted-foreground"
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
Expand All @@ -184,7 +202,10 @@ export function ContactHeroSection({
name="message"
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor={field.name} className="text-lg font-normal text-ma-text">
<FieldLabel
htmlFor={field.name}
className="text-lg font-normal text-ma-text"
>
Message
</FieldLabel>
<textarea
Expand All @@ -194,7 +215,9 @@ export function ContactHeroSection({
aria-invalid={fieldState.invalid}
className="h-[180px] w-full min-w-0 resize-none rounded-md border border-border bg-white px-2.5 py-2.5 text-base transition-colors outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20"
/>
{fieldState.invalid && <FieldError errors={[fieldState.error]} />}
{fieldState.invalid && (
<FieldError errors={[fieldState.error]} />
)}
</Field>
)}
/>
Expand All @@ -204,7 +227,12 @@ export function ContactHeroSection({
disabled={submitting}
className="mt-1 w-full"
>
{submitting && <LoaderCircle className="size-4 animate-spin" aria-hidden="true" />}
{submitting && (
<LoaderCircle
className="size-4 animate-spin"
aria-hidden="true"
/>
)}
Send your message
<ArrowRight
className="size-5 transition-transform duration-300 group-hover:rotate-[-30deg]"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,24 @@ type CourseContentData = {
avgRating: number
reviewCount: number
enrollmentCount: number
modules: { id: string; title: string; sortOrder: number; topics: { id: string; title: string; format: string; content: string | null }[] }[]
reviews: { id: string; body: string | null; rating: number; studentName: string | null; studentImage: string | null }[]
modules: {
id: string
title: string
sortOrder: number
topics: {
id: string
title: string
format: string
content: string | null
}[]
}[]
reviews: {
id: string
body: string | null
rating: number
studentName: string | null
studentImage: string | null
}[]
Comment on lines +25 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Move CourseContentData to one shared type definition.

CourseContentData is duplicated in src/features/marketing/components/course-information-card.tsx. Move the course payload type to one shared module and import it from both components.

As per coding guidelines, do not redefine DTOs or query keys in multiple places.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/features/marketing/components/course-detail-content-section.tsx` around
lines 25 - 42, Move the shared CourseContentData type from
course-detail-content-section.tsx into a common module, then import and reuse it
in both CourseDetailContentSection and CourseInformationCard. Remove the
duplicate local definition from course-information-card.tsx while preserving the
existing payload shape.

Source: Coding guidelines

}

export function CourseDetailContentSection({
Expand All @@ -33,7 +49,7 @@ export function CourseDetailContentSection({
}) {
return (
<section className="bg-white py-10 text-ma-text lg:py-20">
<div className="mx-auto flex flex-col-reverse gap-12 px-4 lg:grid lg:max-w-7xl lg:grid-cols-[0.6fr_0.4fr] lg:items-start lg:justify-between lg:px-25 xl:grid-cols-[598px_335px] 2xl:max-w-360 2xl:px-50">
<div className="marketing-container flex flex-col-reverse gap-12 py-0! sm:py-0 lg:grid lg:grid-cols-[0.6fr_0.4fr] lg:items-start lg:justify-between xl:grid-cols-[598px_335px] 2xl:grid-cols-[650px_400px]">
<div className="flex w-full max-w-[598px] flex-col gap-[30px]">
<section className="flex flex-col gap-4">
<h2 className="text-xl/[100%] leading-normal font-extrabold text-ma-text sm:text-2xl">
Expand Down
Loading