448 lines
17 KiB
Markdown
448 lines
17 KiB
Markdown
|
|
# 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.
|