feat(home): CMS-managed birthday card background patterns
Some checks are pending
CI / Type Check (push) Waiting to run
CI / Lint (push) Waiting to run
CI / Unit Tests (push) Waiting to run
Deploy / Build & Push Image (push) Waiting to run
Deploy / Deploy to VPS (push) Blocked by required conditions

Admins can now upload custom pattern images for green and orange
pricing cards via Home Page → День народження → Паттерн зелених/оранжевої карток.
Falls back to static /images/figma/card-pattern-*.png if not set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-06-05 12:39:05 +01:00
parent 7371462eab
commit 83283ed43c
3 changed files with 50 additions and 7 deletions

View file

@ -0,0 +1,15 @@
-- 0014: birthday card background pattern media fields on home_page global
-- Live table: two nullable FK columns referencing media
ALTER TABLE "home_page"
ADD COLUMN IF NOT EXISTS "birthday_intro_pattern_green_id" integer
REFERENCES "media"("id") ON DELETE SET NULL ON UPDATE NO ACTION,
ADD COLUMN IF NOT EXISTS "birthday_intro_pattern_orange_id" integer
REFERENCES "media"("id") ON DELETE SET NULL ON UPDATE NO ACTION;
-- Versions table: same columns on the version snapshot table
ALTER TABLE "_home_page_v"
ADD COLUMN IF NOT EXISTS "version_birthday_intro_pattern_green_id" integer
REFERENCES "media"("id") ON DELETE SET NULL ON UPDATE NO ACTION,
ADD COLUMN IF NOT EXISTS "version_birthday_intro_pattern_orange_id" integer
REFERENCES "media"("id") ON DELETE SET NULL ON UPDATE NO ACTION;

View file

@ -73,15 +73,20 @@ export default async function HomePage() {
src={home?.video?.src ?? undefined}
/>
)
case 'birthday':
case 'birthday': {
const pg = home?.birthdayIntro?.patternGreen
const po = home?.birthdayIntro?.patternOrange
return (
<BirthdayPricing
key={key}
packages={birthdayPackages.length > 0 ? birthdayPackages : undefined}
title={home?.sectionTitles?.birthday ?? undefined}
intro={home?.birthdayIntro?.text ?? undefined}
patternGreen={pg && typeof pg === 'object' ? (pg.url ?? undefined) : (pg ?? undefined)}
patternOrange={po && typeof po === 'object' ? (po.url ?? undefined) : (po ?? undefined)}
/>
)
}
case 'gallery':
return (
<Gallery

View file

@ -3,17 +3,27 @@ import Link from 'next/link'
import type { BirthdayPackageCMS } from '@/types/globals'
const IMG_CHECK = '/images/figma/check-mark.webp'
const IMG_PATTERN_GREEN = '/images/figma/card-pattern-light.png' // lighter green on transparent → visible on dark green bg
const IMG_PATTERN_ORANGE = '/images/figma/card-pattern-dark.png' // dark green on transparent → visible on orange bg
const DEFAULT_PATTERN_GREEN = '/images/figma/card-pattern-light.png'
const DEFAULT_PATTERN_ORANGE = '/images/figma/card-pattern-dark.png'
interface BirthdayPricingProps {
packages?: BirthdayPackageCMS[]
title?: string
intro?: string
patternGreen?: string
patternOrange?: string
}
export function BirthdayPricing({ packages, title, intro }: BirthdayPricingProps) {
export function BirthdayPricing({
packages,
title,
intro,
patternGreen,
patternOrange,
}: BirthdayPricingProps) {
const activePackages = packages ?? []
const imgPatternGreen = patternGreen ?? DEFAULT_PATTERN_GREEN
const imgPatternOrange = patternOrange ?? DEFAULT_PATTERN_ORANGE
return (
<section className="bg-[#f1fbeb] py-[20px] md:py-[60px] lg:py-[40px]">
@ -37,7 +47,12 @@ export function BirthdayPricing({ packages, title, intro }: BirthdayPricingProps
{/* Figma 1:231 — non-featured wrappers get pt-84 so featured card stands 84px taller */}
<div className="flex flex-col justify-center gap-10 lg:flex-row lg:items-start lg:gap-[59px]">
{activePackages.map((pkg) => (
<PricingCard key={pkg.id} pkg={pkg} />
<PricingCard
key={pkg.id}
pkg={pkg}
imgPatternGreen={imgPatternGreen}
imgPatternOrange={imgPatternOrange}
/>
))}
</div>
</div>
@ -49,7 +64,15 @@ function formatPrice(price: number, currency: string | null | undefined): string
return `${price.toLocaleString('uk-UA')} ${currency ?? '₴'}`
}
function PricingCard({ pkg }: { pkg: BirthdayPackageCMS }) {
function PricingCard({
pkg,
imgPatternGreen,
imgPatternOrange,
}: {
pkg: BirthdayPackageCMS
imgPatternGreen: string
imgPatternOrange: string
}) {
const {
name,
price,
@ -116,7 +139,7 @@ function PricingCard({ pkg }: { pkg: BirthdayPackageCMS }) {
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
backgroundImage: `url('${featured ? IMG_PATTERN_ORANGE : IMG_PATTERN_GREEN}')`,
backgroundImage: `url('${featured ? imgPatternOrange : imgPatternGreen}')`,
backgroundSize: '100% auto',
backgroundRepeat: 'repeat',
opacity: 0.2,