Commit graph

14 commits

Author SHA1 Message Date
Vadym Samoilenko
d5b0a5d5e2 feat: add Figma assets — logo, hero, zone photos, real park photos
Downloaded and optimized 34 images from Figma via API (figd token):
- public/logo.png — official Шуміленд logo (28KB)
- public/hero.jpg — hero background from Figma design (210KB, 1920px)
- public/zone-*.jpg — 6 AI-generated zone images (Dinopark, Dyvo-lis, Labiryn, Birthday, Grupovi, Park)
- public/photo-*.jpg — 6 real park photos (IMG_6468/6472/6474/6475/6480/6481)

CMS updated (via Payload REST API):
- site-settings.logo → logo.png (ID 1)
- homepage heroBlock: backgroundType=image, badge='🌿 Відкрито пт–нд · 11:00–20:00'
- Added locationCardBlock with 6 zone images after hero
- Added galleryBlock with 10 real photos before CTA

seed.ts: uploadMedia() helper auto-uploads public/ assets to Payload on pnpm seed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 20:31:49 +01:00
Vadym Samoilenko
ffe26ba432 feat: Phase 10 — design system redesign per design.md
- globals.css: add surface-light/dark, text-secondary, border-light tokens + shadow-card/hover/btn + pulse-ring animation + section-py utility
- Header: dark green bg (#223D17), white/85 nav links with orange border-bottom hover, brand-orange CTA with shadow-btn, h-[72px]
- MobileNav: fullscreen overlay bg-surface-dark, centered Montserrat 700 28px links with stagger animation
- Footer: bg-surface-dark (#1A2E0F) replacing bg-brand-green
- HeroBlock: green overlay rgba(34,61,23,0.72), badge field support, clamp(48px,7vw,88px) H1, orange CTA with pulse-ring animation
- LocationCardBlock: attraction-card design — aspect-ratio 4/3, gradient overlay, yellow badge, hover scale/shadow
- PricingBlock: surface-light bg, rounded-[20px] table with border-light, orange CTA with shadow-btn
- HeroBlock schema: add badge text field (admin: над заголовком)
- NEW StatsStripComponent: 4 animated counters with IntersectionObserver + requestAnimationFrame
- NEW ConversionCardsComponent: 3 quick-action cards with -mt-16 hero overlap, featured card orange border
- NEW FloatingContacts: Telegram + WhatsApp fixed bottom-right buttons, appear after 300px scroll
- Homepage: inject ConversionCards + StatsStrip between hero and rest of blocks
- 5 new route pages: /dinopark, /dyvo-lis, /labiryn, /dni-narodzhennia, /grupovi-vidviduvannia

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 20:04:04 +01:00
Vadym Samoilenko
a33b0883fc fix: Payload 3.81 admin panel + HeroBlock RSC errors
- layout.tsx: replace minimal html wrapper with RootLayout + handleServerFunctions (fixes ConfigProvider not being initialized)
- HeroBlockComponent.tsx: add 'use client' (fixes onClick event handler RSC serialization error)
- patches/@payloadcms__ui@3.81.0.patch: safe optional chaining for useConfig() in PageConfigProvider + ThemeProvider (fixes 'Cannot destructure config/sort' hydration errors)
- ListQueryContext: add proper default value with query:{} (fixes 'Cannot destructure sort of undefined' on users/labels list)
- package.json: register pnpm patch + add dotenv devDependency + remove --turbopack from dev script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 19:50:20 +01:00
Vadym Samoilenko
80eea727f9 fix: Payload REST API route, sharp, BlogPreviewBlock enum length
- src/app/(payload)/api/[...slug]/route.ts: add missing Payload REST API handler (GET/POST/PUT/PATCH/DELETE/OPTIONS) — required for /api/globals/*, /api/* endpoints and middleware maintenance check
- src/blocks/BlogPreviewBlock.ts: add dbName: 'enum_blog_preview_bg' to backgroundColor select field — prevents PostgreSQL 63-char identifier limit error on versioned landing_pages table
- src/payload.config.ts: add sharp import and pass to buildConfig — enables image resizing in Payload media uploads

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:54:39 +01:00
Vadym Samoilenko
54205244ad perf: Phase 9 — mobile optimization + animations (Lighthouse 90+)
Bundle size:
- MobileNav: framer-motion → LazyMotion+domAnimation (~79KB saved on mobile)
- FeaturesBlock: remove `import * as LucideIcons` (entire icon lib), replace with emoji detector + single Star fallback

Images:
- Add `sizes` prop to all fill images (blog, events, blog-post, hero, blog-preview)
- Prevents downloading desktop-sized images on mobile

Mobile CSS (globals.css):
- font-size: 16px on body (prevents iOS Safari auto-zoom on inputs)
- scroll-behavior: smooth only under prefers-reduced-motion: no-preference
- overflow-x: hidden (no horizontal scroll on mobile)
- touch-action: manipulation on a/button (removes 300ms tap delay)
- min-height: 44px on buttons (WCAG 2.5.5 touch targets)
- Container: px-4 on mobile, px-6 on sm+ (better on 320px screens)
- scrollbar-gutter: stable (prevents CLS from scrollbar appearing)

Form inputs: text-base (16px) prevents iOS auto-zoom

Layout:
- Cache SiteSettings with unstable_cache (revalidate: 60s) — reduces Payload DB hits
- Add preconnect/dns-prefetch hints for Google Fonts, GTM, analytics, Binotel, ezy

Mobile touch targets:
- Hamburger + close buttons: w-12 h-12 (48px, up from 40px)

Animations (Framer Motion with LazyMotion):
- FadeIn.tsx: viewport-triggered fade+slide, respects prefers-reduced-motion
- StaggerContainer.tsx: stagger children on scroll-into-view
- Hero: FadeIn wrapper on content
- Features: StaggerContainer + StaggerItem on each card

GTM events:
- GTMPageEvent.tsx: reusable client component for page-level events
- /thank-you: fires `purchase` event on load
- /blog/[slug]: fires `blog_view` event with post_title + post_slug

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:48:18 +01:00
Vadym Samoilenko
4654bc0e28 feat: Phase 8 — seed, security, loading skeleton, CSP
- src/seed.ts: idempotent seed script (admin, labels, SiteSettings, Navigation, TicketsConfig, homepage, sample blog/event)
- src/app/(frontend)/loading.tsx: page skeleton loader with animate-pulse
- next.config.ts: CSP + security headers (X-Frame-Options, Referrer-Policy, Permissions-Policy), localhost added to image remotePatterns
- src/app/api/tickets/create/route.ts: rate limiting (10 req/min/IP)
- src/app/api/tickets/webhook/route.ts: X-Webhook-Secret or ?secret query param verification
- .env.example: EZY_WEBHOOK_SECRET, SEED_ADMIN_*, portal URLs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:40:25 +01:00
Vadym Samoilenko
22b38c5939 feat: Phase 7 — GTM event wiring + Employee Portal
- Employee Portal (/portal): role-based card grid with auth via Payload token cookie
- HeroBlockComponent: import pushGTMEvent from @/lib/gtm instead of inline dataLayer push
- CTABlockComponent: add 'use client' + pushGTMEvent on button clicks
- FormBlockComponent: replace inline dataLayer push with pushGTMEvent helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:34:39 +01:00
Vadym Samoilenko
eafa994484 feat: Phase 6 — Ticket payment (ezy.com.ua → Monobank)
- src/lib/ezy.ts — getTariffs() (5min cache), createPayment() via multipart/form-data
- src/lib/qr.ts — generateQRDataUrl(), getTicketVerifyUrl()
- src/lib/email.ts — Nodemailer HTML confirmation email
- src/app/(frontend)/payments/page.tsx — 3-step checkout (select → buyer → pay)
- src/app/(frontend)/thank-you/page.tsx — simple confirmation (ezy handles ticket display)
- src/app/api/tickets/create/route.ts — Order(PENDING) → ezy.createPayment() → paymentUrl
- src/app/api/tickets/webhook/route.ts — mark PAID, create Ticket records with QR URLs
- src/app/api/tickets/verify/[code]/route.ts — staff scanner endpoint, marks isUsed
- Fix: orderNumber/ticketCode not required in schema (hooks generate them)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:27:22 +01:00
Vadym Samoilenko
67140a9ec4 feat: Phase 5 — Lead API, Binotel, Twenty CRM
- src/app/api/leads/route.ts — POST /api/leads with Zod validation,
  UTM + GA Client ID from cookies, rate limiting (5 req/min/IP),
  async Twenty CRM sync (fire-and-forget)
- src/app/api/binotel/webhook/route.ts — HMAC-SHA256 verification,
  matches call to most recent lead by phone, saves binotelCallId
- src/lib/twenty.ts — GraphQL mutation to create Person in Twenty CRM
- src/lib/binotel.ts — HMAC verify, phone normalizer, payload parser
- src/lib/gtm.ts — pushGTMEvent() client helper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:23:17 +01:00
Vadym Samoilenko
6451ffe292 feat: Phase 4 complete — public pages, block components, layout
Layout:
- Header (sticky, dropdown nav, mobile hamburger)
- Footer (columns from CMS, social links, phone)
- MobileNav (Framer Motion slide-out)
- GTMScript, GA4Script, UmamiScript, BinotelWidget

Block components (all 11):
- HeroBlockComponent — video/image/gradient bg, CTA, scroll indicator
- TextBlockComponent — richtext with prose styles
- FeaturesBlockComponent — Lucide icons or images, 2/3/4 cols
- LocationCardBlockComponent — cards with image, badge, price
- PricingBlockComponent — TicketsConfig table or custom table
- GalleryBlockComponent — grid/masonry/carousel + lightbox
- FormBlockComponent — 4 form types (birthday/group/callback/generic), RHF+Zod
- CTABlockComponent — gradient/image/solid backgrounds, multi-button
- CountdownBlockComponent — client-side live timer
- BlogPreviewBlockComponent — fetches blog/events, 3 layouts
- MapBlockComponent — iframe embed, working hours, transport info

Pages & SEO:
- Home page, [slug] catch-all, 404
- /blog, /blog/[slug] with Article structured data
- /events, /events/[slug]
- /landing/[slug] with sticky CTA bar
- sitemap.ts, robots.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:20:52 +01:00
Vadym Samoilenko
9d4333cf92 fix: bug fixes + Phase 3 middleware/UTM
Bug fixes:
- Add beforeValidate slug hooks to Blog, Events, Pages, LandingPages (slugify)
- Add auto-generation hooks for orderNumber (Orders) and ticketCode (Tickets)
- Restrict Tickets.create to isAuthenticated (was publicly accessible)
- Rename isAdminOrPublished → isAuthenticatedOrPublished for clarity
- Remove redundant brand CSS utilities (Tailwind v4 generates them from @theme)
- Remove autoprefixer (redundant with @tailwindcss/postcss)
- Replace real API keys/IDs with placeholders in .env.example

Phase 3:
- src/middleware.ts — UTM first-touch cookies + maintenance mode redirect
- src/lib/utm.ts — getUtmFromCookies(), getGoogleClientId()
- src/app/(frontend)/maintenance/page.tsx — maintenance page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 17:09:36 +01:00
Vadym Samoilenko
dbc7e15426 docs: add implementation_plan.md and CONTEXT_HANDOVER.md
Full phased implementation plan with specs, file trees, and technical
decisions. Context handover document for session continuity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:56:12 +01:00
Vadym Samoilenko
61e73033fe feat: Phase 2 complete — content model, blocks, collections, globals
- 11 Page Builder blocks: HeroBlock, TextBlock, FeaturesBlock,
  LocationCardBlock, PricingBlock, GalleryBlock, FormBlock (with label
  relation for lead tracking), CTABlock, CountdownBlock, BlogPreviewBlock,
  MapBlock
- Collections: Labels, Pages, LandingPages, Blog, Events, Leads (UTM +
  googleClientId + label relation), Orders, Tickets
- Globals: SiteSettings (GTM/GA4/Binotel/Umami), TicketsConfig, Navigation
- Added "type": "module" to package.json for Payload CLI ESM compatibility
- payload-types.ts generated (1874 lines)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:52:54 +01:00
Vadym Samoilenko
04b112d476 feat: Фаза 1 — фундамент проєкту Shumiland
- Ініціалізація Next.js 15.4.11 + Payload CMS 3.x (PostgreSQL/Drizzle)
- Docker Compose: PostgreSQL, Twenty CRM, Umami, Metabase
- Tailwind CSS 4 з brand кольорами (green/orange/blue/purple/yellow)
- Шрифти: Montserrat Alternates + Rubik через next/font/google
- shadcn/ui компоненти: Button, Input, Label, Textarea, Badge, Card, Separator
- Payload колекції: Users (ролі SUPER_ADMIN/ADMIN/EDITOR), Media (upload з sizes)
- Access control: isAdmin, isEditor, isSuperAdmin, isAdminOrPublished
- .env.example з усіма змінними середовища
- Dockerfile (multi-stage, output: standalone)
- Білд успішно проходить перевірку TypeScript

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 14:54:13 +01:00