# Payload Schema — Remaining Collections and Globals (Final) ## Purpose Final schema for every Payload collection and global used on the new Eversolo site, excluding Product (see `SCHEMA-PRODUCT.md`). This document is the source of truth for the Payload config. ## Shared Conventions ### Locales - `en`, `zh` - Field-level localization is used (not document-level). - `**localized**` below means the field is per-locale. - `**shared**` means a single value across locales. ### Status Workflow All content-bearing collections share: - `status`: `draft | review | published | archived` - `publishedAt`: auto-set on first publish - `createdAt`, `updatedAt`: auto ### Access Control - Public read: only `status = published`. - Write: authenticated editors and above. - Admin-only fields are marked inline. ### SEO Group Used on all public-facing documents: - `title` — **localized** — text, max 70. - `description` — **localized** — textarea, max 160. - `ogImage` — **shared** — upload. - `canonical` — **shared** — text, optional. - `noIndex` — **shared** — checkbox, admin-only, default false. ### Revalidation Every collection that renders on the public site triggers on-demand revalidation of the affected route paths in its `afterChange` hook. --- ## Collections ### 1. Product Categories Slug: `productCategories` - `slug` — **shared** — text, required, URL-safe, unique. - `name` — **localized** — text, required. - `description` — **localized** — rich text. - `heroImage` — **shared** — upload. - `order` — **shared** — number, default 0. - `seo` — group (see SEO group). #### Validation - `slug` unique. - `name` required per locale if that locale is published. #### Admin UI Tabs 1. `Overview`: slug, name, description, order. 2. `Media`: heroImage. 3. `SEO`: seo. --- ### 2. FAQ Slug: `faqs` - `group` — **shared** — select, required. Suggested values: `setup | streaming | connectivity | hardware | warranty | other`. Editable at admin level. - `question` — **localized** — text, required. - `answer` — **localized** — rich text, required. - `relatedProducts` — **shared** — relationship to `products`, multi-select, optional. - `relatedDownloads` — **shared** — relationship to `downloads`, multi-select, optional. - `order` — **shared** — number, default 0. - `status` — **shared** — select (see status workflow). #### Listing - Grouped by `group`, ordered by `order` ascending. - Support page shows curated groups; dedicated FAQ page shows all published entries. #### Validation - `question` and `answer` required for any locale being published. --- ### 3. Downloads Slug: `downloads` - `title` — **localized** — text, required. - `type` — **shared** — select, required. Values: `firmware | manual | software | other`. - `product` — **shared** — relationship to `products`, required. - `version` — **shared** — text, optional. - `releaseDate` — **shared** — date, optional. - `file` — **shared** — upload, required. - `checksum` — **shared** — text, optional. - `changelog` — **localized** — rich text, optional. - `notes` — **localized** — rich text, optional. - `visibility` — group. - `showInProductPage` — **shared** — checkbox, default true. - `showInDownloadsCenter` — **shared** — checkbox, default true. - `status` — **shared** — select (see status workflow). #### Rules - Firmware display on product pages also requires the product's `firmware.showOnProductPage` toggle to be on. - Firmware display in the Downloads center also requires the product's `firmware.showInDownloadsCenter` toggle to be on. - `showInProductPage` and `showInDownloadsCenter` are download-level overrides: they can hide a specific file even when the product-level toggle is on. #### Validation - `title` required per published locale. - `file` required. - `product` required. - `type` required. #### Admin UI Tabs 1. `Overview`: title, type, product, version, releaseDate, status. 2. `File`: file, checksum. 3. `Content`: changelog, notes. 4. `Visibility`: visibility group. --- ### 4. Product Videos Slug: `productVideos` - `title` — **shared** — text, required. - `product` — **shared** — relationship to `products`, required. - `provider` — **shared** — select, required. Values: `youtube | vimeo | bilibili | other`. - `videoUrl` — **shared** — text, required. - `embedUrl` — **shared** — text, optional. - `thumbnail` — **shared** — relationship to `media`, optional. - `description` — **localized** — rich text, optional. - `publishedAt` — **shared** — date, optional. - `sortOrder` — **shared** — number, default 0. - `legacySourceUrl` — **shared** — text, optional. - `status` — **shared** — select (see status workflow). #### Rules - Product detail pages render all published videos related to the current product, ordered by `sortOrder`, then newest first. - Seed import may create draft records first; editors can refine descriptions later. --- ### 5. Product Reviews Slug: `productReviews` - `title` — **shared** — text, required. - `product` — **shared** — relationship to `products`, required. - `sourceName` — **shared** — text, optional. - `reviewUrl` — **shared** — text, required. - `thumbnail` — **shared** — relationship to `media`, optional. - `excerpt` — **localized** — text, optional. - `quote` — **localized** — text, optional. - `publishedAt` — **shared** — date, optional. - `sortOrder` — **shared** — number, default 0. - `legacySourceUrl` — **shared** — text, optional. - `status` — **shared** — select (see status workflow). #### Rules - Product detail pages render all published reviews related to the current product, ordered by `sortOrder`, then newest first. - Reviews may be imported from the legacy site with minimal metadata and enriched later in CMS. --- ### 6. Dealers Slug: `dealers` - `name` — **shared** — text, required. - `region` — **shared** — select, required. Values mirror the Dealers page region filter. - `country` — **shared** — text, required. - `city` — **shared** — text, optional. - `address` — **localized** — textarea, optional. - `email` — **shared** — email, optional. - `phone` — **shared** — text, optional. - `website` — **shared** — URL, optional. - `logo` — **shared** — upload, optional. - `isAuthorized` — **shared** — checkbox, default true. - `order` — **shared** — number, default 0. - `status` — **shared** — select (see status workflow). #### Listing - Grouped by region, then country, then name. - Only `isAuthorized = true` are shown in public lists. --- ### 7. News Articles Slug: `news` - `slug` — **localized** — text, required, unique per locale. - `category` — **shared** — select, required. Values: `announcement | press | review | event`. - `title` — **localized** — text, required. - `excerpt` — **localized** — textarea, optional. - `coverImage` — **shared** — upload, required. - `body` — **localized** — rich text, required. - `author` — **shared** — text, optional. - `source` — **shared** — text, optional (for press / review entries). - `sourceUrl` — **shared** — URL, optional. - `publishedAt` — **shared** — date, required on publish. - `relatedProducts` — **shared** — relationship to `products`, multi-select, optional. - `status` — **shared** — select (see status workflow). - `seo` — group. #### Listing Rules - News list: all published, newest first. - Homepage news highlights: manually selected via Homepage global. - Articles may exist in a single locale; the UI surfaces only locales with published content. --- ### 8. Media Slug: `media` Generic upload collection for images used across the site. - `file` — upload, required. - `alt` — **localized** — text, required. - `caption` — **localized** — text, optional. - `credit` — **shared** — text, optional. - `tags` — **shared** — array of text. #### Usage Every image field in other collections references `media`. Editors upload once, reuse everywhere. --- ### 9. Videos Slug: `videos` Dedicated upload collection for video assets (hero videos, product showcase). - `file` — upload, required. - `poster` — relationship to `media`, required. - `durationSeconds` — **shared** — number, optional. - `alt` — **localized** — text, required. - `tags` — **shared** — array of text. --- ### 10. Files Slug: `files` Upload collection for firmware, manuals, software binaries, and other downloadable files. Used as the `file` reference on the `downloads` collection. - `file` — upload, required. - `sizeBytes` — **shared** — number (auto). - `mimeType` — **shared** — text (auto). --- ### 11. Redirects Slug: `redirects` Source of truth for 301 redirects from the legacy site, including the entries documented in `REDIRECTS.md`. - `from` — **shared** — text, required. Matches a path or pattern on the legacy URL space. - `to` — **shared** — text, required. Destination path on the new site, locale-prefixed. - `statusCode` — **shared** — select, required. Values: `301 | 302 | 307 | 308`. Default `301`. - `matchType` — **shared** — select, required. Values: `exact | prefix | regex`. Default `exact`. - `locale` — **shared** — select, optional. Values: `any | en | zh`. Controls whether the redirect applies only to a locale-scoped target. - `notes` — **shared** — text, optional. - `enabled` — **shared** — checkbox, default true. #### Rules - Redirects are resolved at the edge in priority order: `exact` > `prefix` > `regex`. - External links (Amazon, JD, Tmall, forum.zidoo, shop.zidoo, blog.eversolo) are not managed here. - The APP header link points directly to `https://www.eversolo.com/controlapp/index.html` and is not stored as a redirect. --- ## Globals ### 1. Homepage Slug: `homepage` - `hero` — group. - `mode` — **shared** — select, required. Values: `image | video`. Default `image`. - `image` — **shared** — relationship to `media`, required when `mode = image`. - `video` — **shared** — relationship to `videos`, required when `mode = video`. - `headline` — **localized** — text, required. - `supportingLine` — **localized** — text, optional. - `primaryCta` — group. - `label` — **localized** — text. - `href` — **shared** — text (internal path or URL). - `secondaryCta` — group. - `label` — **localized** — text. - `href` — **shared** — text. - `featuredProducts` — **shared** — relationship to `products`, required, exactly 4. - `statement` — group. - `title` — **localized** — text. - `body` — **localized** — rich text. - `image` — **shared** — relationship to `media`. - `cta` — optional group of `{ label, href }`. - `supportShortcuts` — array, 3 to 4 items. - `label` — **localized** — text, required. - `description` — **localized** — text. - `href` — **shared** — text, required. - `recognition` — group. - `awards` — **shared** — array of `{ logo: media, label (localized), year, url }`. - `reviews` — **shared** — array of `{ sourceLogo: media, sourceName (localized), quote (localized), url }`. - `newsHighlights` — **shared** — relationship to `news`, up to 3, optional. - `seo` — group. #### Validation - `featuredProducts` length must equal 4. - `recognition.awards` should contain at least 1 item if the module is shown; otherwise leave empty. --- ### 2. Support Page Slug: `supportPage` - `intro` — group. - `title` — **localized** — text. - `body` — **localized** — rich text. - `primaryEntries` — array, required, exactly 3. - `key` — **shared** — select, required. Values: `faq | warranty | contact`. - `label` — **localized** — text, required. - `description` — **localized** — text. - `href` — **shared** — text, required. - `secondaryEntries` — array, 0 to 3 items. - `key` — **shared** — select. Values: `downloads | manuals | firmware`. - `label` — **localized** — text, required. - `description` — **localized** — text. - `href` — **shared** — text, required. - `seo` — group. --- ### 3. Warranty Page Slug: `warrantyPage` - `intro` — group. - `title` — **localized** — text. - `body` — **localized** — rich text. - `policyBody` — **localized** — rich text, required. - `regionalNotes` — array. - `region` — **shared** — select. - `body` — **localized** — rich text. - `registrationCta` — optional group of `{ label, href }`. - `seo` — group. --- ### 4. Contact Page Slug: `contactPage` - `intro` — group. - `title` — **localized** — text. - `body` — **localized** — rich text. - `regions` — array. - `region` — **shared** — select. - `email` — **shared** — email. - `phone` — **shared** — text. - `address` — **localized** — textarea. - `hours` — **localized** — text. - `formEnabled` — **shared** — checkbox, default true. - `formSuccessMessage` — **localized** — rich text. - `seo` — group. --- ### 5. About Page Slug: `aboutPage` - `intro` — group. - `title` — **localized** — text. - `body` — **localized** — rich text. - `brandStory` — **localized** — rich text. - `philosophy` — **localized** — rich text. - `milestones` — array. - `year` — **shared** — number. - `title` — **localized** — text. - `description` — **localized** — rich text. - `image` — **shared** — relationship to `media`, optional. - `seo` — group. --- ### 6. Global Settings Slug: `globalSettings` - `site` — group. - `name` — **localized** — text. - `logoLight` — **shared** — relationship to `media`. - `logoDark` — **shared** — relationship to `media`. - `favicon` — **shared** — relationship to `media`. - `navigation` — group. - `primary` — array, required. Each item: - `label` — **localized** — text. - `href` — **shared** — text. - `external` — **shared** — checkbox, default false. - `openInNewTab` — **shared** — checkbox, default false. - `iconKey` — **shared** — text, optional (for future icon support). - `app` — group (singleton inside navigation). - `label` — **localized** — text, default `APP`. - `href` — **shared** — text, required, default `https://www.eversolo.com/controlapp/index.html`. - `openInNewTab` — **shared** — checkbox, default true. - `showOnDesktop` — **shared** — checkbox, default true. - `showOnMobile` — **shared** — checkbox, default true. - `footer` — group. - `columns` — array. - `title` — **localized** — text. - `items` — array of `{ label (localized), href, external, openInNewTab }`. - `legalLinks` — array of `{ label (localized), href }`. - `copyright` — **localized** — text. - `social` — array of `{ platform, url, iconKey }`. - `languageSwitcher` — group. - `labels` — per-locale `{ en: "English", zh: "简体中文" }`. - `analytics` — group. - `provider` — select: `plausible | ga4 | none`. - `domain` — text. - `measurementId` — text. - `seoDefaults` — group. - `titleSuffix` — **localized** — text. - `description` — **localized** — textarea. - `ogImage` — **shared** — relationship to `media`. - `cookieBanner` — group. - `enabled` — **shared** — checkbox, default true. - `body` — **localized** — rich text. - `acceptLabel` — **localized** — text. - `declineLabel` — **localized** — text. #### Notes - The header APP entry is driven by `navigation.app` so editors can update the label, URL, and visibility without a code change. - `openInNewTab` defaults to true for external items. --- ## Hooks (cross-collection) - `afterChange` on any publishable document: - Revalidate affected public routes. - Revalidate Homepage if the document is referenced in Homepage fields. - `beforeValidate` on any slug field: - Normalize (lowercase, trim, dashes, ASCII fallback). - `beforeChange` on `status`: - Set `publishedAt` on first transition to `published`. ## Admin UX Principles - Use tabs per collection to separate structural, content, media, and SEO fields. - Globals use grouped sections rather than tabs to stay editor-friendly. - Every relationship field is labeled with its expected content type and any filters applied. - Locale selector is persistent across the admin UI. ## Open Items - Whether Redirects are managed in CMS or in a versioned file; current plan: CMS-managed for editor control. - Whether News articles should support per-locale `status` separately. - Whether the Dealers region list should be a CMS-managed enum or hardcoded. - Whether the Homepage `recognition` module should allow a layout choice (logo wall vs quote strip) per locale.