diff --git a/package.json b/package.json index e22f4b5..dae4f10 100644 --- a/package.json +++ b/package.json @@ -83,9 +83,15 @@ "prettier-plugin-tailwindcss": "^0.8.0", "supertest": "^7.1.0", "tailwindcss": "^4.3.0", - "tsx": "^4.21.0", + "tsx": "^4.22.0", "typescript": "^6.0.3", "vite-tsconfig-paths": "^6.1.1", "vitest": "^4.1.6" + }, + "pnpm": { + "overrides": { + "tsx": "4.22.0", + "payload>tsx": "4.22.0" + } } } diff --git a/payload.config.ts b/payload.config.ts index 5379811..3287d7d 100644 --- a/payload.config.ts +++ b/payload.config.ts @@ -28,6 +28,8 @@ import { Footer } from './src/globals/Footer' import { SiteSettings } from './src/globals/SiteSettings' import { DyvoLisPage } from './src/globals/DyvoLisPage' import { GroupVisitsPage } from './src/globals/GroupVisitsPage' +import { BirthdayPage } from './src/globals/BirthdayPage' +import { TicketsPage } from './src/globals/TicketsPage' const filename = fileURLToPath(import.meta.url) const dirname = path.dirname(filename) @@ -78,6 +80,8 @@ export default buildConfig({ SiteSettings, DyvoLisPage, GroupVisitsPage, + BirthdayPage, + TicketsPage, ], plugins: [ @@ -107,6 +111,8 @@ export default buildConfig({ 'site-settings', 'dyvolis-page', 'group-visits-page', + 'birthday-page', + 'tickets-page', ], }, }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fccb48..6e517a1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -113,7 +113,7 @@ importers: version: 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3) '@vitejs/plugin-react': specifier: ^6.0.1 - version: 6.0.1(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)) + version: 6.0.1(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)) '@vitest/coverage-v8': specifier: ^4.1.6 version: 4.1.6(vitest@4.1.6) @@ -154,17 +154,17 @@ importers: specifier: ^4.3.0 version: 4.3.0 tsx: - specifier: ^4.21.0 - version: 4.21.0 + specifier: ^4.22.0 + version: 4.22.0 typescript: specifier: ^6.0.3 version: 6.0.3 vite-tsconfig-paths: specifier: ^6.1.1 - version: 6.1.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)) + version: 6.1.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)) vitest: specifier: ^4.1.6 - version: 4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)) + version: 4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)) packages: @@ -5306,6 +5306,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.22.0: + resolution: {integrity: sha512-8ccZMPD69s1AbKXx0C5ddTNZfNjwV04iIKgjZmKfKxMynEtSYcK0Lh7iQFh53fI5Yu4pb9usgAiqyPmEONaALg==} + engines: {node: '>=18.0.0'} + hasBin: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -7760,10 +7765,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true - '@vitejs/plugin-react@6.0.1(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4))': + '@vitejs/plugin-react@6.0.1(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.7 - vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4) '@vitest/coverage-v8@4.1.6(vitest@4.1.6)': dependencies: @@ -7777,7 +7782,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)) + vitest: 4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)) '@vitest/expect@4.1.6': dependencies: @@ -7788,13 +7793,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4))': + '@vitest/mocker@4.1.6(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4))': dependencies: '@vitest/spy': 4.1.6 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4) '@vitest/pretty-format@4.1.6': dependencies: @@ -11148,6 +11153,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.22.0: + dependencies: + esbuild: 0.28.0 + optionalDependencies: + fsevents: 2.3.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 @@ -11314,17 +11325,17 @@ snapshots: '@types/unist': 3.0.3 unist-util-stringify-position: 4.0.0 - vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)): + vite-tsconfig-paths@6.1.1(typescript@6.0.3)(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)): dependencies: debug: 4.4.3 globrex: 0.1.2 tsconfck: 3.1.6(typescript@6.0.3) - vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4) transitivePeerDependencies: - supports-color - typescript - vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4): + vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -11338,13 +11349,13 @@ snapshots: jiti: 2.7.0 lightningcss: 1.32.0 sass: 1.77.4 - tsx: 4.21.0 + tsx: 4.22.0 yaml: 2.8.4 - vitest@4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)): + vitest@4.1.6(@types/node@25.7.0)(@vitest/coverage-v8@4.1.6)(happy-dom@20.9.0)(jsdom@29.1.1(@noble/hashes@1.8.0))(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)): dependencies: '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4)) + '@vitest/mocker': 4.1.6(vite@7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4)) '@vitest/pretty-format': 4.1.6 '@vitest/runner': 4.1.6 '@vitest/snapshot': 4.1.6 @@ -11361,7 +11372,7 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.21.0)(yaml@2.8.4) + vite: 7.3.3(@types/node@25.7.0)(jiti@2.7.0)(lightningcss@1.32.0)(sass@1.77.4)(tsx@4.22.0)(yaml@2.8.4) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 25.7.0 diff --git a/src/app/(frontend)/dni-narodzhennia/page.tsx b/src/app/(frontend)/dni-narodzhennia/page.tsx index 5f7f7cc..f2c903b 100644 --- a/src/app/(frontend)/dni-narodzhennia/page.tsx +++ b/src/app/(frontend)/dni-narodzhennia/page.tsx @@ -4,14 +4,17 @@ import configPromise from '@payload-config' import { PageHero } from '@/components/ui/PageHero' import { BirthdayBookingForm } from '@/components/forms/BirthdayBookingForm' -export const metadata: Metadata = { - title: 'Дні народження — Шуміленд', - description: - 'Святкуйте день народження у Шуміленді! Пакети для дітей та дорослих з розвагами, аніматорами та кейтерингом.', -} - export const revalidate = 60 +async function getBirthdayPageData() { + try { + const payload = await getPayload({ config: configPromise }) + return await payload.findGlobal({ slug: 'birthday-page', depth: 0 }) + } catch { + return null + } +} + async function getPackages() { try { const payload = await getPayload({ config: configPromise }) @@ -30,6 +33,16 @@ function formatPrice(price: number): string { return price.toLocaleString('uk-UA').replace(/,/g, ' ') } +export async function generateMetadata(): Promise { + const pageData = await getBirthdayPageData() + return { + title: pageData?.metaTitle ?? 'Дні народження — Шуміленд', + description: + pageData?.metaDescription ?? + 'Святкуйте день народження у Шуміленді! Пакети для дітей та дорослих з розвагами, аніматорами та кейтерингом.', + } +} + export default async function BirthdayPage({ searchParams, }: { @@ -37,13 +50,16 @@ export default async function BirthdayPage({ }) { const params = await searchParams const defaultPackage = params.package - const packages = await getPackages() + const [pageData, packages] = await Promise.all([getBirthdayPageData(), getPackages()]) return (
@@ -100,13 +116,14 @@ export default async function BirthdayPage({ className="mb-2 text-[28px] font-bold text-white" style={{ fontFamily: 'var(--font-montserrat, Montserrat), sans-serif' }} > - Замовити святкування + {pageData?.formTitle ?? 'Замовити святкування'}

- Залиште заявку і наш менеджер зв'яжеться з вами протягом 30 хвилин + {pageData?.formSubtitle ?? + "Залиште заявку і наш менеджер зв'яжеться з вами протягом 30 хвилин"}

diff --git a/src/app/(frontend)/grupovi-vidviduvannia/page.tsx b/src/app/(frontend)/grupovi-vidviduvannia/page.tsx index cba7091..75bddc5 100644 --- a/src/app/(frontend)/grupovi-vidviduvannia/page.tsx +++ b/src/app/(frontend)/grupovi-vidviduvannia/page.tsx @@ -6,6 +6,14 @@ import { GroupRequestForm } from '@/components/forms/GroupRequestForm' export const revalidate = 60 +interface Group { + icon?: string | null + title: string + description: string + minPeople: string + discount: string +} + async function getGroupVisitsData() { try { const payload = await getPayload({ config: configPromise }) @@ -16,40 +24,15 @@ async function getGroupVisitsData() { } export async function generateMetadata(): Promise { + const data = await getGroupVisitsData() return { - title: 'Групові відвідування — Шуміленд', + title: data?.metaTitle ?? 'Групові відвідування — Шуміленд', description: + data?.metaDescription ?? 'Організуйте групове відвідування Шуміленду. Спеціальні ціни для шкіл, дитячих садків і корпоративних груп.', } } -const DEFAULT_GROUPS = [ - { - title: 'Шкільні екскурсії', - description: - 'Пізнавальні екскурсії для учнів початкової та середньої школи. Екскурсовод, адаптована програма, безпечний маршрут.', - minPeople: '15 осіб', - discount: '15%', - icon: '🏫', - }, - { - title: 'Дитячі садки', - description: - 'Програма для наймолодших — безпечний формат, розвивальні активності, відповідальний супровід.', - minPeople: '10 осіб', - discount: '20%', - icon: '🎒', - }, - { - title: 'Корпоративи', - description: - 'Тімбілдинг та корпоративний відпочинок у форматі парку розваг. Ексклюзивні зони, кейтеринг, програма на замовлення.', - minPeople: '20 осіб', - discount: '10%', - icon: '🏢', - }, -] - export default async function GroupVisitsPage() { const data = await getGroupVisitsData() @@ -61,46 +44,47 @@ export default async function GroupVisitsPage() { const formSubtitle = data?.formSubtitle ?? 'Вкажіть кількість учасників та бажану дату — менеджер зателефонує і погодить деталі.' - const groups = - data?.groups && data.groups.length > 0 ? (data.groups as typeof DEFAULT_GROUPS) : DEFAULT_GROUPS + const groups = (data?.groups ?? []) as Group[] return (
-
- {groups.map((g) => ( -
- {g.icon} -

0 && ( +
+ {groups.map((g) => ( +
- {g.title} -

-

- {g.description} -

-
-
-

Від

-

{g.minPeople}

-
-
-

Знижка

-

{g.discount}

+ {g.icon} +

+ {g.title} +

+

+ {g.description} +

+
+
+

Від

+

{g.minPeople}

+
+
+

Знижка

+

{g.discount}

+
-
- ))} -
+ ))} +
+ )}

= { - dyno: 'ДиноПарк', - dyvolis: 'Диво Ліс', - maze: 'Дзеркальний Лабіринт', - combo: 'Комбо', - family: 'Сімейний', +async function getBirthdayPackages() { + try { + const payload = await getPayload({ config: configPromise }) + const result = await payload.find({ + collection: 'birthday-packages', + sort: 'sort', + limit: 10, + }) + return result.docs + } catch { + return [] + } +} + +async function getGroupVisitsData() { + try { + const payload = await getPayload({ config: configPromise }) + return await payload.findGlobal({ slug: 'group-visits-page', depth: 0 }) + } catch { + return null + } +} + +export async function generateMetadata(): Promise { + const pageData = await getPageData() + return { + title: pageData?.metaTitle ?? 'Купити квиток — Шуміленд', + description: + pageData?.metaDescription ?? + 'Придбайте квитки до Шуміленду онлайн. Вхідні квитки на всі зони парку.', + } } export default async function TicketsPage() { - const data = await getTariffs() - const tariffs = data?.tariffs ?? [] - const hasWarning = !!data?.warning + const [tariffData, pageData, birthdayPackages, groupVisitsData, siteSettings] = await Promise.all( + [getTariffs(), getPageData(), getBirthdayPackages(), getGroupVisitsData(), getSiteSettings()] + ) + + const tariffs = tariffData?.tariffs ?? [] + const hasWarning = !!tariffData?.warning + + const categoryLabelsMap = Object.fromEntries( + (siteSettings.tariffCategoryLabels ?? []).map(({ key, label }) => [key, label]) + ) - // Group by category const grouped = tariffs.reduce>((acc, t) => { const key = t.categoryTag ?? 'other' acc[key] ??= [] @@ -57,8 +95,10 @@ export default async function TicketsPage() { return (
@@ -89,7 +129,7 @@ export default async function TicketsPage() { className="mb-6 text-[24px] font-bold text-[#272727] uppercase" style={FONT_MONT} > - {CATEGORY_LABELS[category] ?? category} + {categoryLabelsMap[category] ?? category}

{items.map((tariff) => ( @@ -107,111 +147,99 @@ export default async function TicketsPage() { )) )} - {/* Birthday packages */} -
-

- Дні народження -

-
- {[ - { - name: 'Стандарт', - desc: 'До 10 дітей · Аніматор 2 год · Торт від закладу', - href: '/dni-narodzhennia#order-form', - }, - { - name: 'Преміум', - desc: 'До 20 дітей · Аніматор 3 год · Декор + фотограф', - href: '/dni-narodzhennia#order-form', - }, - { - name: 'VIP', - desc: 'До 40 гостей · Аніматор 4 год · Повне меню + відео', - href: '/dni-narodzhennia#order-form', - }, - ].map((pkg) => ( -
-
-

- {pkg.name} -

-

- {pkg.desc} -

-
- 0 && ( +
+

+ {pageData?.sectionTitleBirthday ?? 'Дні народження'} +

+
+ {birthdayPackages.map((pkg) => ( +
- Дізнатися ціну - -
- ))} +
+

+ {pkg.name} +

+ {pkg.features && pkg.features.length > 0 && ( +

+ {pkg.features + .slice(0, 3) + .map((f: { text: string }) => f.text) + .join(' · ')} +

+ )} +
+ + {pkg.ctaLabel ?? 'Дізнатися ціну'} + +
+ ))} +
-
+ )} - {/* Group visits */} -
-

- Групові відвідування -

-
- {[ - { - name: 'Шкільні екскурсії', - desc: 'Від 15 осіб · Знижка 15% · Екскурсовод', - href: '/grupovi-vidviduvannia#order-form', - }, - { - name: 'Дитячі садки', - desc: 'Від 10 осіб · Знижка 20% · Безпечний формат', - href: '/grupovi-vidviduvannia#order-form', - }, - { - name: 'Корпоративи', - desc: 'Від 20 осіб · Знижка 10% · Ексклюзивні зони', - href: '/grupovi-vidviduvannia#order-form', - }, - ].map((grp) => ( -
-
-

0 && ( +
+

+ {pageData?.sectionTitleGroups ?? 'Групові відвідування'} +

+
+ {groupVisitsData.groups.map( + (grp: { + title: string + description: string + minPeople: string + discount: string + }) => ( +
- {grp.name} -

-

- {grp.desc} -

-
- - Дізнатися ціну - -
- ))} +
+

+ {grp.title} +

+

+ Від {grp.minPeople} · Знижка {grp.discount} +

+
+ + Дізнатися ціну + +
+ ) + )} +
-
+ )}
diff --git a/src/app/(frontend)/lokatsii/dyvolis/page.tsx b/src/app/(frontend)/lokatsii/dyvolis/page.tsx index c64e787..d461897 100644 --- a/src/app/(frontend)/lokatsii/dyvolis/page.tsx +++ b/src/app/(frontend)/lokatsii/dyvolis/page.tsx @@ -20,8 +20,10 @@ async function getDyvoLisData() { export async function generateMetadata(): Promise { const data = await getDyvoLisData() return { - title: data?.heroTitle ? `${data.heroTitle} — Шуміленд` : 'ДивоЛіс — Шуміленд', + title: + data?.metaTitle ?? (data?.heroTitle ? `${data.heroTitle} — Шуміленд` : 'ДивоЛіс — Шуміленд'), description: + data?.metaDescription ?? 'Казковий топіарний ліс у Шуміленді: фігури з безпечних матеріалів, унікальна ландшафтна композиція та повна свобода для дітей.', } } diff --git a/src/globals/BirthdayPage.ts b/src/globals/BirthdayPage.ts new file mode 100644 index 0000000..314213f --- /dev/null +++ b/src/globals/BirthdayPage.ts @@ -0,0 +1,46 @@ +import type { GlobalConfig } from 'payload' +import { isAdminOrEditor } from '@/access/isAdminOrEditor' +import { revalidateGlobalAfterChange } from '@/hooks/revalidatePath' + +export const BirthdayPage: GlobalConfig = { + slug: 'birthday-page', + label: 'Дні народження — сторінка', + access: { read: () => true, update: isAdminOrEditor }, + hooks: { afterChange: [revalidateGlobalAfterChange] }, + fields: [ + { + name: 'heroTitle', + type: 'text', + defaultValue: 'Дні народження', + }, + { + name: 'heroSubtitle', + type: 'text', + defaultValue: + "Зробіть свято незабутнім! Оберіть пакет і наші менеджери зв'яжуться з вами для уточнення деталей.", + }, + { + name: 'formTitle', + type: 'text', + defaultValue: 'Замовити святкування', + }, + { + name: 'formSubtitle', + type: 'text', + defaultValue: "Залиште заявку і наш менеджер зв'яжеться з вами протягом 30 хвилин", + }, + { + name: 'metaTitle', + type: 'text', + label: 'SEO: Meta Title', + defaultValue: 'Дні народження — Шуміленд', + }, + { + name: 'metaDescription', + type: 'textarea', + label: 'SEO: Meta Description', + defaultValue: + 'Святкуйте день народження у Шуміленді! Пакети для дітей та дорослих з розвагами, аніматорами та кейтерингом.', + }, + ], +} diff --git a/src/globals/DyvoLisPage.ts b/src/globals/DyvoLisPage.ts index 609e33b..ecc1f8b 100644 --- a/src/globals/DyvoLisPage.ts +++ b/src/globals/DyvoLisPage.ts @@ -1,10 +1,12 @@ import type { GlobalConfig } from 'payload' import { isAdminOrEditor } from '@/access/isAdminOrEditor' +import { revalidateGlobalAfterChange } from '@/hooks/revalidatePath' export const DyvoLisPage: GlobalConfig = { slug: 'dyvolis-page', label: 'Диво Ліс — сторінка', access: { read: () => true, update: isAdminOrEditor }, + hooks: { afterChange: [revalidateGlobalAfterChange] }, fields: [ // Hero section { @@ -118,5 +120,18 @@ export const DyvoLisPage: GlobalConfig = { defaultValue: 'Динопарк + Диволіс із казковими топіарними фігурами + Дзеркальний лабіринт', admin: { description: 'Підзаголовок під секцією "Комбо" в блоці квитків' }, }, + { + name: 'metaTitle', + type: 'text', + label: 'SEO: Meta Title', + defaultValue: 'ДивоЛіс — Шуміленд', + }, + { + name: 'metaDescription', + type: 'textarea', + label: 'SEO: Meta Description', + defaultValue: + 'Казковий топіарний ліс у Шуміленді: фігури з безпечних матеріалів, унікальна ландшафтна композиція та повна свобода для дітей.', + }, ], } diff --git a/src/globals/GroupVisitsPage.ts b/src/globals/GroupVisitsPage.ts index 9891ddc..f110ddf 100644 --- a/src/globals/GroupVisitsPage.ts +++ b/src/globals/GroupVisitsPage.ts @@ -1,10 +1,12 @@ import type { GlobalConfig } from 'payload' import { isAdminOrEditor } from '@/access/isAdminOrEditor' +import { revalidateGlobalAfterChange } from '@/hooks/revalidatePath' export const GroupVisitsPage: GlobalConfig = { slug: 'group-visits-page', label: 'Групові відвідування — сторінка', access: { read: () => true, update: isAdminOrEditor }, + hooks: { afterChange: [revalidateGlobalAfterChange] }, fields: [ { name: 'heroTitle', @@ -69,5 +71,18 @@ export const GroupVisitsPage: GlobalConfig = { }, ], }, + { + name: 'metaTitle', + type: 'text', + label: 'SEO: Meta Title', + defaultValue: 'Групові відвідування — Шуміленд', + }, + { + name: 'metaDescription', + type: 'textarea', + label: 'SEO: Meta Description', + defaultValue: + 'Організуйте групове відвідування Шуміленду. Спеціальні ціни для шкіл, дитячих садків і корпоративних груп.', + }, ], } diff --git a/src/globals/SiteSettings.ts b/src/globals/SiteSettings.ts index ca1cb1b..3dca7a6 100644 --- a/src/globals/SiteSettings.ts +++ b/src/globals/SiteSettings.ts @@ -14,5 +14,34 @@ export const SiteSettings: GlobalConfig = { { name: 'defaultMetaTitle', type: 'text' }, { name: 'defaultMetaDescription', type: 'textarea' }, { name: 'defaultOgImage', type: 'upload', relationTo: 'media' }, + { + name: 'tariffCategoryLabels', + type: 'array', + label: 'Назви категорій тарифів', + admin: { + description: 'Відповідність ключа з ezy API → назва для відображення на сайті', + }, + fields: [ + { + name: 'key', + type: 'text', + required: true, + admin: { description: 'Ключ з ezy API (напр. dyno, dyvolis, maze)' }, + }, + { + name: 'label', + type: 'text', + required: true, + admin: { description: 'Назва для відображення (напр. ДиноПарк)' }, + }, + ], + defaultValue: [ + { key: 'dyno', label: 'ДиноПарк' }, + { key: 'dyvolis', label: 'Диво Ліс' }, + { key: 'maze', label: 'Дзеркальний Лабіринт' }, + { key: 'combo', label: 'Комбо' }, + { key: 'family', label: 'Сімейний' }, + ], + }, ], } diff --git a/src/globals/TicketsPage.ts b/src/globals/TicketsPage.ts new file mode 100644 index 0000000..13aef73 --- /dev/null +++ b/src/globals/TicketsPage.ts @@ -0,0 +1,46 @@ +import type { GlobalConfig } from 'payload' +import { isAdminOrEditor } from '@/access/isAdminOrEditor' +import { revalidateGlobalAfterChange } from '@/hooks/revalidatePath' + +export const TicketsPage: GlobalConfig = { + slug: 'tickets-page', + label: 'Квитки — сторінка', + access: { read: () => true, update: isAdminOrEditor }, + hooks: { afterChange: [revalidateGlobalAfterChange] }, + fields: [ + { + name: 'heroTitle', + type: 'text', + defaultValue: 'Купити квиток', + }, + { + name: 'heroSubtitle', + type: 'text', + defaultValue: 'Оберіть квиток та придбайте онлайн — без черги на касі', + }, + { + name: 'sectionTitleBirthday', + type: 'text', + label: 'Заголовок секції "Дні народження"', + defaultValue: 'Дні народження', + }, + { + name: 'sectionTitleGroups', + type: 'text', + label: 'Заголовок секції "Групові відвідування"', + defaultValue: 'Групові відвідування', + }, + { + name: 'metaTitle', + type: 'text', + label: 'SEO: Meta Title', + defaultValue: 'Купити квиток — Шуміленд', + }, + { + name: 'metaDescription', + type: 'textarea', + label: 'SEO: Meta Description', + defaultValue: 'Придбайте квитки до Шуміленду онлайн. Вхідні квитки на всі зони парку.', + }, + ], +} diff --git a/src/lib/getSiteSettings.ts b/src/lib/getSiteSettings.ts index bd40c67..965d3a2 100644 --- a/src/lib/getSiteSettings.ts +++ b/src/lib/getSiteSettings.ts @@ -7,6 +7,7 @@ export interface SiteSettingsData { binotelId?: string | null defaultMetaTitle?: string | null defaultMetaDescription?: string | null + tariffCategoryLabels?: Array<{ key: string; label: string }> | null } export const getSiteSettings = cache(async (): Promise => {