Files
BarevClub/apps/landing/components/flowers.tsx
T

152 lines
6.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client';
import { Icon } from '@barev/ui';
import { useEffect, useState } from 'react';
import imgL from '../assets/flowers/l.jpg';
import imgM from '../assets/flowers/m.jpg';
import imgS from '../assets/flowers/s.jpg';
import { BotLink } from './client';
/** Цены за одну доставку; при еженедельной подписке — своя цена (≈ −10%) */
const PLANS = [
{
id: 's',
letter: 'S',
name: 'Мини',
stems: 10,
stemsLabel: '9–11 стеблей',
photo: imgS,
alt: 'Букет S: розовые кустовые розы, белая альстромерия и эвкалипт в крафте',
base: 9900,
week: 8900,
salon: '13–15 тыс. ֏',
features: ['Сезонный микс от флориста', 'Для кухни или рабочего стола, ~35 см', 'Крафт-упаковка и подкормка'],
},
{
id: 'm',
letter: 'M',
name: 'Классика',
stems: 16,
stemsLabel: '15–17 стеблей',
photo: imgM,
alt: 'Букет M: персиковые пионовидные розы, сиреневая эустома и белые хризантемы в крафте',
base: 14900,
week: 13400,
salon: '19–23 тыс. ֏',
features: ['Сезонный микс с акцентными цветами', 'Для гостиной, ~45 см', 'Ваза к первому букету — остаётся у вас'],
},
{
id: 'l',
letter: 'L',
name: 'Пышный',
stems: 27,
stemsLabel: '25–30 стеблей',
photo: imgL,
alt: 'Букет L: розовые розы, белая гортензия, ранункулюсы и эустома в крафте',
base: 22900,
week: 20600,
salon: '30–42 тыс. ֏',
features: ['Эустома, пионовидные розы и другие премиальные сорта', 'Заметен с порога, ~55 см', 'Ваза к первому букету — остаётся у вас'],
},
] as const;
const FREQ = [
{ id: 'week', label: 'Каждую неделю', perMonth: 4.3 },
{ id: 'biweek', label: 'Раз в 2 недели', perMonth: 2.15 },
{ id: 'month', label: 'Раз в месяц', perMonth: 1 },
] as const;
type FreqId = (typeof FREQ)[number]['id'];
const amd = (n: number) => `${Math.round(n).toLocaleString('ru-RU')} ֏`;
const round10 = (n: number) => Math.round(n / 10) * 10;
const round100 = (n: number) => Math.round(n / 100) * 100;
/** Тарифы S/M/L как клубные карты главной; частота меняет цену и «≈ в месяц» */
export function FlowerPlans() {
const [freq, setFreq] = useState<FreqId>('biweek');
const f = FREQ.find((x) => x.id === freq)!;
return (
<section id="plans" className="h-plans fw-plans" aria-labelledby="plans-title">
<div className="h-section-head">
<h2 id="plans-title">Выберите свой букет</h2>
<p>Цена за одну доставку, доставка по Еревану уже включена. Без договоров: пауза и отмена в один клик.</p>
</div>
<div className="fw-freq" role="radiogroup" aria-label="Как часто привозить">
{FREQ.map((x) => (
<button key={x.id} type="button" role="radio" aria-checked={freq === x.id} onClick={() => setFreq(x.id)}>
{x.label}
{x.id === 'week' && <span className="fw-freq__save">−10%</span>}
</button>
))}
</div>
<div className="h-plans__grid">
{PLANS.map((p) => {
const price = freq === 'week' ? p.week : p.base;
const monthly = price * f.perMonth;
const saving = (p.base - p.week) * f.perMonth;
return (
<article key={p.id} className="h-plan fw-plan" data-size={p.id}>
{p.id === 'm' && <span className="fw-plan__badge">Выбирают чаще всего</span>}
<img className="fw-plan__photo" src={p.photo.src} width={p.photo.width} height={p.photo.height} alt={p.alt} loading="lazy" decoding="async" />
<div className="h-card fw-card">
<span className="h-card__club">Barev Club · Цветы</span>
<span className="fw-card__letter" aria-hidden="true">
{p.letter}
</span>
<h3 className="h-card__name">{p.name}</h3>
<span className="h-card__price">
{amd(price)} <small>/ букет</small>
</span>
</div>
<div className="fw-plan__facts">
<span>{p.stemsLabel}</span>
<span>≈ {amd(price / p.stems)} за стебель</span>
<span>≈ {amd(round10(monthly / 30))} в день</span>
</div>
<p className="fw-plan__anchor">
В салоне похожий букет — <s>{p.salon}</s> + доставка
</p>
<ul className="h-plan__list">
{p.features.map((x) => (
<li key={x}>
<Icon name="check" size={18} strokeWidth={2.6} />
{x}
</li>
))}
</ul>
<p className="fw-plan__month">
{freq === 'month' ? 'Одна доставка в месяц' : `≈ ${amd(round100(monthly))} в месяц`}
{freq === 'week' && <b> · экономия {amd(round100(saving))} в месяц</b>}
</p>
<BotLink className="bc-btn h-plan__cta">Оформить {p.letter}</BotLink>
</article>
);
})}
</div>
<p className="fw-plans__note">Оплата картой ArCa, Visa или Mastercard — за 2 дня до каждой доставки. Предоплаты за месяц нет.</p>
</section>
);
}
/** Ближайшая доставка: через 2 дня, если оформить до 20:00, иначе через 3; в воскресенье не возим. Считается в браузере — страница кешируется */
export function NextDelivery() {
const [text, setText] = useState<{ date: string; when: string } | null>(null);
useEffect(() => {
const now = new Date();
const early = now.getHours() < 20;
const d = new Date(now);
d.setDate(d.getDate() + (early ? 2 : 3));
if (d.getDay() === 0) d.setDate(d.getDate() + 1);
setText({ date: d.toLocaleDateString('ru-RU', { weekday: 'long', day: 'numeric', month: 'long' }), when: early ? 'сегодня' : 'завтра' });
}, []);
if (!text) return null;
return (
<p className="fw-next">
<Icon name="clock" size={18} /> Ближайшая доставка — <b>{text.date}</b>, если оформить {text.when} до 20:00
</p>
);
}