feat: min/max participants — shared ParticipantLimits component

- New ParticipantLimits component in FormField.tsx (reusable)
- Used in both Open Day settings and MC editor — identical layout
- Open Day: event-level min/max (DB migration 15)
- MC: per-event min/max (JSON fields)
- Public: waiting list when full, spots counter, amber success modal
This commit is contained in:
2026-03-24 22:11:10 +03:00
parent 4acc88c1ab
commit d08905ee93
13 changed files with 484 additions and 50 deletions

View File

@@ -148,19 +148,44 @@ export function SignupModal({
{success ? (
<div className="py-4 text-center">
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-500/10">
<CheckCircle size={28} className="text-emerald-500" />
</div>
<h3 className="text-lg font-bold text-white">
{successMessage || "Вы записаны!"}
</h3>
{subtitle && <p className="mt-1 text-sm text-neutral-400">{subtitle}</p>}
{successData?.totalBookings !== undefined && (
<p className="mt-3 text-sm text-white">
Вы записаны на <span className="text-gold font-semibold">{String(successData.totalBookings)}</span> занятий.
<br />
Стоимость: <span className="text-gold font-semibold">{String(successData.pricePerClass)} BYN</span> за занятие
</p>
{successData?.isWaiting ? (
<>
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-amber-500/10">
<CheckCircle size={28} className="text-amber-500" />
</div>
<h3 className="text-lg font-bold text-white">Вы в листе ожидания</h3>
<p className="mt-2 text-sm text-neutral-400 leading-relaxed">
Все места заняты, но мы добавили вас в лист ожидания.
<br />
Если кто-то откажется мы предложим место вам.
</p>
<a
href={BRAND.instagram}
target="_blank"
rel="noopener noreferrer"
className="mt-3 inline-flex items-center gap-1.5 text-sm text-pink-400 hover:text-pink-300"
>
<Instagram size={14} />
По вопросам пишите в Instagram
</a>
</>
) : (
<>
<div className="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-500/10">
<CheckCircle size={28} className="text-emerald-500" />
</div>
<h3 className="text-lg font-bold text-white">
{successMessage || "Вы записаны!"}
</h3>
{subtitle && <p className="mt-1 text-sm text-neutral-400">{subtitle}</p>}
{successData?.totalBookings !== undefined && (
<p className="mt-3 text-sm text-white">
Вы записаны на <span className="text-gold font-semibold">{String(successData.totalBookings)}</span> занятий.
<br />
Стоимость: <span className="text-gold font-semibold">{String(successData.pricePerClass)} BYN</span> за занятие
</p>
)}
</>
)}
<button
onClick={handleClose}