chore: baseline import

This commit is contained in:
Codex
2026-04-23 01:08:18 +08:00
commit c8ddfdf660
260 changed files with 117643 additions and 0 deletions
+741
View File
@@ -0,0 +1,741 @@
html[data-theme] {
--theme-bg: #f4efe7;
--theme-input-bg: #fffdf9;
--theme-text: #171412;
--theme-elevation-0: #fffdf9;
--theme-elevation-50: #faf6ef;
--theme-elevation-100: #f3ede2;
--theme-elevation-150: #ece2d3;
--theme-elevation-200: #dfd2be;
--theme-elevation-250: #d4c1a8;
--theme-elevation-300: #c9b194;
--theme-elevation-400: #a28362;
--theme-elevation-500: #7f6349;
--theme-elevation-600: #654d38;
--theme-elevation-700: #463529;
--theme-elevation-800: #241c16;
--theme-elevation-900: #17110d;
--theme-elevation-950: #0e0a08;
--theme-border-color: rgba(78, 57, 39, 0.14);
--theme-success-500: #1a7f58;
--theme-warning-500: #b46f1a;
--theme-error-500: #b83b31;
--font-body: "Instrument Sans", "Manrope", "PingFang SC", sans-serif;
--gutter-h: 18px;
--style-radius-s: 6px;
--style-radius-m: 10px;
--style-radius-l: 14px;
}
html[data-theme] body {
background:
radial-gradient(circle at top, rgba(196, 162, 122, 0.18), transparent 26%),
linear-gradient(180deg, #f8f4ed 0%, #f3ede4 48%, #efe8dc 100%);
color: var(--theme-text);
}
html[data-theme] body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.72), transparent 44%),
linear-gradient(180deg, transparent, rgba(74, 46, 24, 0.03));
}
html[data-theme] .template-minimal__wrap,
html[data-theme] .template-default__wrap {
position: relative;
border-radius: 16px;
border: 1px solid rgba(84, 60, 34, 0.08);
background: rgba(255, 252, 247, 0.92);
box-shadow: 0 16px 36px rgba(52, 34, 18, 0.06);
backdrop-filter: blur(18px);
}
html[data-theme] .template-minimal {
min-height: 100vh;
padding: 28px 16px;
}
html[data-theme] .template-minimal__wrap {
max-width: 500px;
padding: 28px 24px 22px;
}
html[data-theme] .login__brand {
margin-bottom: 30px;
}
html[data-theme] .login__brand::after {
content: "Eversolo Content Studio";
display: block;
margin-top: 12px;
font-size: 12px;
letter-spacing: 0.28em;
text-transform: uppercase;
color: rgba(93, 70, 48, 0.72);
}
html[data-theme] .app-header {
border-bottom: 1px solid rgba(84, 60, 34, 0.08);
background: rgba(255, 251, 245, 0.76);
backdrop-filter: blur(16px);
}
html[data-theme] .nav {
background: linear-gradient(180deg, rgba(255, 251, 245, 0.88), rgba(244, 236, 224, 0.8));
border-right: 1px solid rgba(84, 60, 34, 0.08);
}
html[data-theme] .nav::before {
content: "Eversolo";
display: block;
margin: 12px 12px 8px;
padding: 10px 12px;
border-radius: 10px;
background: linear-gradient(135deg, #2f241b, #54402f);
color: #fff9f1;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.24em;
text-transform: uppercase;
}
html[data-theme] .nav a,
html[data-theme] .nav button {
border-radius: 8px;
}
html[data-theme] .nav a:hover,
html[data-theme] .nav button:hover {
background: rgba(84, 60, 34, 0.08);
}
html[data-theme] .gutter--left,
html[data-theme] .gutter--right,
html[data-theme] .collection-list,
html[data-theme] .list-controls,
html[data-theme] .render-fields,
html[data-theme] .doc-controls {
position: relative;
z-index: 1;
}
html[data-theme] .list-header,
html[data-theme] .doc-controls,
html[data-theme] .render-fields > *,
html[data-theme] .field-type,
html[data-theme] .banner,
html[data-theme] .card,
html[data-theme] .popup__content,
html[data-theme] .table,
html[data-theme] .collection-edit,
html[data-theme] .document-fields,
html[data-theme] .tabs-field__content-wrap {
border-color: rgba(84, 60, 34, 0.08);
}
html[data-theme] .render-fields > .field-type,
html[data-theme] .tabs-field__content-wrap,
html[data-theme] .table table,
html[data-theme] .popup__content,
html[data-theme] .banner,
html[data-theme] .card {
border-radius: 10px;
}
html[data-theme] .render-fields > .field-type,
html[data-theme] .tabs-field__content-wrap {
background: rgba(255, 253, 249, 0.9);
box-shadow: inset 0 0 0 1px rgba(84, 60, 34, 0.04);
padding: 12px 14px;
}
html[data-theme] .document-fields,
html[data-theme] .collection-edit {
gap: 12px;
}
html[data-theme] .render-fields > .field-type {
margin-bottom: 10px;
}
html[data-theme] .tabs-field__tabs {
gap: 6px;
}
html[data-theme] .tabs-field__tab-button {
border-radius: 999px;
background: rgba(84, 60, 34, 0.06);
padding: 7px 12px;
}
html[data-theme] .tabs-field__tab-button--active {
background: #241c16;
color: #fff8f0;
}
html[data-theme] .doc-controls {
position: sticky;
top: 20px;
z-index: 6;
border-radius: 12px;
padding: 12px 14px;
background: rgba(255, 252, 247, 0.88);
box-shadow: 0 10px 24px rgba(52, 34, 18, 0.05);
backdrop-filter: blur(16px);
}
html[data-theme] input,
html[data-theme] textarea,
html[data-theme] select,
html[data-theme] .rs__control,
html[data-theme] .code-editor,
html[data-theme] .monaco-editor,
html[data-theme] .lexical-editor,
html[data-theme] [contenteditable="true"] {
border-radius: 8px !important;
}
html[data-theme] .code-editor,
html[data-theme] .monaco-editor {
min-height: 420px;
overflow: hidden;
}
html[data-theme] .code-editor .overflow-guard,
html[data-theme] .monaco-editor .overflow-guard {
border-radius: 14px;
}
html[data-theme] input,
html[data-theme] textarea,
html[data-theme] select,
html[data-theme] .rs__control,
html[data-theme] .code-editor,
html[data-theme] .lexical-editor {
background: rgba(255, 253, 249, 0.96);
}
html[data-theme] input,
html[data-theme] textarea,
html[data-theme] select,
html[data-theme] .rs__control {
min-height: 40px;
}
html[data-theme] textarea {
min-height: 100px;
}
html[data-theme] .field-type,
html[data-theme] .field-type .field-type,
html[data-theme] .tabs-field__content-wrap > .render-fields > .field-type {
max-width: 980px;
}
html[data-theme] .field-type:has(input[type='checkbox']),
html[data-theme] .field-type:has(select),
html[data-theme] .field-type:has(input[type='date']),
html[data-theme] .field-type:has(input[type='datetime-local']),
html[data-theme] .field-type:has(input[type='number']),
html[data-theme] .field-type:has(input[type='email']),
html[data-theme] .field-type:has(input[type='text']) {
max-width: 760px;
}
html[data-theme] .row .field-type {
max-width: none;
}
html[data-theme] .rich-text-lexical,
html[data-theme] .rich-text-lexical .editor-container,
html[data-theme] .rich-text-lexical .editor-scroller,
html[data-theme] .rich-text-lexical .editor,
html[data-theme] .lexical-editor {
overflow: visible !important;
box-shadow: none;
}
html[data-theme] .lexical-editor [contenteditable='true'] {
min-height: 240px;
padding: 16px 18px;
line-height: 1.75;
}
html[data-theme] .fixed-toolbar,
html[data-theme] .inline-toolbar-popup {
border-radius: 10px;
padding: 6px 8px;
background: rgba(255, 252, 247, 0.96);
box-shadow: 0 10px 24px rgba(52, 34, 18, 0.08);
}
html[data-theme] .fixed-toolbar__group,
html[data-theme] .inline-toolbar-popup__group {
gap: 4px;
}
html[data-theme] .fixed-toolbar,
html[data-theme] .sticky-toolbar {
border-radius: 10px;
background: rgba(255, 252, 247, 0.96);
box-shadow: 0 10px 24px rgba(52, 34, 18, 0.08);
}
html[data-theme] .array-field,
html[data-theme] .blocks-field,
html[data-theme] .collapsible-field,
html[data-theme] .relationship-field,
html[data-theme] .upload-field {
max-width: 1200px;
}
html[data-theme] .field-label {
margin-bottom: 6px;
}
html[data-theme] .field-description {
margin-top: 6px;
font-size: 12px;
line-height: 1.45;
}
html[data-theme] .array-field__header,
html[data-theme] .blocks-field__header,
html[data-theme] .collapsible__header,
html[data-theme] .relationship-field__drawer-toggler,
html[data-theme] .upload-field__controls {
gap: 8px;
}
html[data-theme] .collection-edit__title,
html[data-theme] .list-header,
html[data-theme] .list-controls__wrap {
margin-bottom: 14px;
}
html[data-theme] input:focus,
html[data-theme] textarea:focus,
html[data-theme] select:focus,
html[data-theme] .rs__control--is-focused,
html[data-theme] [contenteditable="true"]:focus {
border-color: rgba(181, 123, 54, 0.55) !important;
box-shadow: 0 0 0 4px rgba(196, 145, 85, 0.12) !important;
}
html[data-theme] .btn--style-primary {
--bg-color: #241c16;
--hover-bg: #403024;
--color: #fff9f1;
}
html[data-theme] .btn--style-primary,
html[data-theme] .btn--style-primary *,
html[data-theme] .btn--icon-style-without-border.btn--style-primary,
html[data-theme] .btn--icon-style-without-border.btn--style-primary *,
html[data-theme] .btn--withoutPopup.btn--style-primary,
html[data-theme] .btn--withoutPopup.btn--style-primary * {
color: #fff8f0 !important;
fill: currentColor;
}
html[data-theme] .btn--icon-style-without-border.btn--style-primary,
html[data-theme] .btn--withoutPopup.btn--style-primary {
background: #241c16 !important;
}
html[data-theme] .btn--icon-style-without-border.btn--style-primary:hover,
html[data-theme] .btn--withoutPopup.btn--style-primary:hover {
background: #403024 !important;
}
html[data-theme] .nullify-locale-field {
display: none !important;
}
html[data-theme] .btn--style-secondary,
html[data-theme] .btn--style-pill {
--bg-color: rgba(84, 60, 34, 0.06);
--color: #2d2118;
--hover-bg: rgba(84, 60, 34, 0.12);
}
html[data-theme] .list-header,
html[data-theme] .doc-controls {
margin-bottom: 20px;
}
html[data-theme] .table table {
overflow: hidden;
background: rgba(255, 253, 249, 0.9);
}
html[data-theme] .table,
html[data-theme] .table *,
html[data-theme] .collection-list,
html[data-theme] .collection-list *,
html[data-theme] .popup__content,
html[data-theme] .popup__content *,
html[data-theme] [class*="drawer"],
html[data-theme] [class*="drawer"] *,
html[data-theme] [class*="modal"],
html[data-theme] [class*="modal"] * {
color: var(--theme-text);
}
html[data-theme] .popup__content,
html[data-theme] [class*="drawer"],
html[data-theme] [class*="modal"] {
background: rgba(255, 252, 247, 0.98);
}
html[data-theme] .table thead th,
html[data-theme] .table tbody td,
html[data-theme] .collection-list__header,
html[data-theme] .list-controls__wrap,
html[data-theme] .collection-list__wrap {
color: var(--theme-text);
}
html[data-theme] .popup-button-list__button,
html[data-theme] .popup-button-list__button button,
html[data-theme] .btn--style-secondary,
html[data-theme] .btn--style-pill {
color: #2d2118;
}
html[data-theme] .react-datepicker-popper {
z-index: 140;
}
html[data-theme] .react-datepicker,
html[data-theme] .react-datepicker__month-container,
html[data-theme] .react-datepicker__header,
html[data-theme] .react-datepicker__time-container,
html[data-theme] .react-datepicker__month {
background: rgba(255, 253, 249, 0.98);
color: var(--theme-text);
border-color: rgba(84, 60, 34, 0.12);
}
html[data-theme] .react-datepicker__current-month,
html[data-theme] .react-datepicker-time__header,
html[data-theme] .react-datepicker-year-header,
html[data-theme] .react-datepicker__day-name,
html[data-theme] .react-datepicker__day,
html[data-theme] .react-datepicker__time-name,
html[data-theme] .react-datepicker__month-text,
html[data-theme] .react-datepicker__quarter-text,
html[data-theme] .react-datepicker__year-text {
color: var(--theme-text);
}
html[data-theme] .react-datepicker__day:hover,
html[data-theme] .react-datepicker__month-text:hover,
html[data-theme] .react-datepicker__quarter-text:hover,
html[data-theme] .react-datepicker__year-text:hover,
html[data-theme] .react-datepicker__day--keyboard-selected,
html[data-theme] .react-datepicker__month-text--keyboard-selected,
html[data-theme] .react-datepicker__quarter-text--keyboard-selected,
html[data-theme] .react-datepicker__year-text--keyboard-selected {
background: rgba(84, 60, 34, 0.1);
color: var(--theme-text);
}
html[data-theme] .react-datepicker__day--selected,
html[data-theme] .react-datepicker__day--in-selecting-range,
html[data-theme] .react-datepicker__day--in-range,
html[data-theme] .react-datepicker__month-text--selected,
html[data-theme] .react-datepicker__quarter-text--selected,
html[data-theme] .react-datepicker__year-text--selected {
background: #241c16;
color: #fff8f0;
}
html[data-theme] .studio-home {
display: grid;
gap: 24px;
padding: 28px;
}
html[data-theme] .studio-home__hero,
html[data-theme] .studio-home__panel,
html[data-theme] .studio-home__stat-card {
border: 1px solid rgba(84, 60, 34, 0.08);
background: rgba(255, 252, 247, 0.9);
box-shadow: 0 22px 60px rgba(52, 34, 18, 0.07);
}
html[data-theme] .studio-home__hero {
display: grid;
gap: 24px;
align-items: end;
border-radius: 28px;
padding: 28px;
background:
radial-gradient(circle at top right, rgba(212, 171, 122, 0.22), transparent 34%),
linear-gradient(135deg, rgba(255, 253, 249, 0.98), rgba(247, 240, 230, 0.94));
}
html[data-theme] .studio-home__eyebrow,
html[data-theme] .studio-home__stat-eyebrow {
margin: 0 0 10px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.24em;
text-transform: uppercase;
color: rgba(93, 70, 48, 0.72);
}
html[data-theme] .studio-home__title {
max-width: 14ch;
margin: 0;
font-size: clamp(32px, 4vw, 52px);
line-height: 0.98;
letter-spacing: -0.04em;
}
html[data-theme] .studio-home__lede,
html[data-theme] .studio-home__panel-head p,
html[data-theme] .studio-home__link-card span,
html[data-theme] .studio-home__checklist li {
color: rgba(52, 35, 22, 0.8);
}
html[data-theme] .studio-home__lede {
max-width: 60ch;
margin: 16px 0 0;
font-size: 15px;
line-height: 1.8;
}
html[data-theme] .studio-home__actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
html[data-theme] .studio-home__primary,
html[data-theme] .studio-home__secondary,
html[data-theme] .studio-home__link-card,
html[data-theme] .studio-home__stat-card {
text-decoration: none;
}
html[data-theme] .studio-home__primary,
html[data-theme] .studio-home__secondary {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 44px;
border-radius: 999px;
padding: 0 18px;
font-weight: 600;
}
html[data-theme] .studio-home__primary {
background: #241c16;
color: #fff8f0;
}
html[data-theme] .studio-home__secondary {
background: rgba(84, 60, 34, 0.07);
color: #2d2118;
}
html[data-theme] .studio-home__stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 16px;
}
html[data-theme] .studio-home__stat-card {
display: grid;
gap: 8px;
border-radius: 22px;
padding: 20px;
color: inherit;
}
html[data-theme] .studio-home__stat-card:hover,
html[data-theme] .studio-home__link-card:hover {
transform: translateY(-1px);
box-shadow: 0 26px 64px rgba(52, 34, 18, 0.09);
}
html[data-theme] .studio-home__stat-value {
font-size: 38px;
line-height: 1;
letter-spacing: -0.04em;
}
html[data-theme] .studio-home__stat-title {
font-size: 14px;
font-weight: 600;
color: rgba(52, 35, 22, 0.82);
}
html[data-theme] .studio-home__grid {
display: grid;
gap: 20px;
grid-template-columns: repeat(2, minmax(0, 1fr));
}
html[data-theme] .studio-home__panel {
border-radius: 24px;
padding: 22px;
}
html[data-theme] .studio-home__panel-head h2 {
margin: 0;
font-size: 22px;
letter-spacing: -0.03em;
}
html[data-theme] .studio-home__panel-head p {
margin: 8px 0 0;
font-size: 14px;
line-height: 1.7;
}
html[data-theme] .studio-home__actions {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
html[data-theme] .admin-cache-header-slot {
position: fixed;
top: 8px;
right: 260px;
z-index: 80;
}
html[data-theme] .admin-cache-header-slot form {
margin: 0;
}
html[data-theme] .admin-cache-header-slot__button {
border: 1px solid rgba(84, 60, 34, 0.1);
border-radius: 999px;
background: rgba(255, 251, 245, 0.9);
box-shadow: 0 12px 26px rgba(52, 34, 18, 0.08);
color: #2d2118;
cursor: pointer;
padding: 10px 14px;
font: inherit;
font-size: 12px;
font-weight: 600;
letter-spacing: 0.02em;
backdrop-filter: blur(16px);
}
html[data-theme] .admin-cache-header-slot__button:hover {
background: rgba(255, 247, 236, 0.98);
}
html[data-theme] .studio-home__actions form {
margin: 0;
}
html[data-theme] .studio-home__secondary--warm {
border: 1px solid rgba(112, 74, 34, 0.14);
cursor: pointer;
}
html[data-theme] .studio-home__notice {
margin: 18px 0 0;
border: 1px solid rgba(26, 127, 88, 0.18);
border-radius: 18px;
background: rgba(250, 255, 252, 0.85);
color: #215c45;
padding: 14px 18px;
box-shadow: 0 12px 28px rgba(26, 127, 88, 0.05);
}
@media (max-width: 1100px) {
html[data-theme] .admin-cache-header-slot {
right: 132px;
}
}
@media (max-width: 900px) {
html[data-theme] .admin-cache-header-slot {
top: auto;
right: 12px;
bottom: 14px;
}
}
html[data-theme] .studio-home__link-list {
display: grid;
gap: 14px;
margin-top: 18px;
}
html[data-theme] .studio-home__link-card {
display: grid;
gap: 6px;
border-radius: 18px;
border: 1px solid rgba(84, 60, 34, 0.08);
background: rgba(255, 255, 255, 0.72);
padding: 16px 18px;
color: inherit;
}
html[data-theme] .studio-home__link-card strong {
font-size: 15px;
}
html[data-theme] .studio-home__link-card span {
font-size: 13px;
line-height: 1.7;
}
html[data-theme] .studio-home__checklist {
display: grid;
gap: 14px;
margin: 18px 0 0;
padding: 0;
list-style: none;
}
html[data-theme] .studio-home__checklist li {
position: relative;
border-radius: 18px;
background: rgba(255, 255, 255, 0.72);
padding: 14px 16px 14px 42px;
line-height: 1.8;
}
html[data-theme] .studio-home__checklist li::before {
content: "";
position: absolute;
left: 16px;
top: 20px;
width: 10px;
height: 10px;
border-radius: 999px;
background: linear-gradient(135deg, #b78148, #5d432d);
}
@media (max-width: 900px) {
html[data-theme] .studio-home__grid {
grid-template-columns: 1fr;
}
}
html[data-theme] .table thead th {
background: rgba(241, 233, 220, 0.9);
}
html[data-theme] .pill {
border-radius: 999px;
}
html[data-theme] .banner {
background: rgba(241, 233, 220, 0.78);
}
@@ -0,0 +1,40 @@
import configPromise from '@payload-config'
import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
// @ts-expect-error generated by Payload admin import map generator
import { importMap } from '../importMap.js'
import { AdminHome } from '../_components/admin-home'
type AdminPageProps = {
params: Promise<{ segments?: string[] }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export async function generateMetadata({ params, searchParams }: AdminPageProps) {
return generatePageMetadata({
config: configPromise,
params: params as Promise<{ [key: string]: string | string[] }>,
searchParams: searchParams as Promise<{ [key: string]: string | string[] }>,
})
}
export default async function PayloadAdminPage({ params, searchParams }: AdminPageProps) {
const normalizedParams = params.then(async (value) => {
const segments = Array.isArray(value.segments) ? value.segments.filter(Boolean) : []
return { segments }
})
const { segments } = await normalizedParams
const resolvedSearchParams = await searchParams
if (segments.length === 0) {
return <AdminHome cacheCleared={resolvedSearchParams.cache === 'cleared'} />
}
return RootPage({
config: configPromise,
importMap,
params: Promise.resolve({ segments }),
searchParams: Promise.resolve(resolvedSearchParams as { [key: string]: string | string[] }),
})
}
@@ -0,0 +1,43 @@
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
const CACHE_PATHS = [
'/en',
'/zh',
'/en/about',
'/zh/about',
'/en/dealers',
'/zh/dealers',
'/en/downloads',
'/zh/downloads',
'/en/news',
'/zh/news',
'/en/products',
'/zh/products',
'/en/support',
'/zh/support',
'/en/support/contact',
'/zh/support/contact',
'/en/support/faq',
'/zh/support/faq',
'/en/support/warranty',
'/zh/support/warranty',
] as const
function normalizeReturnPath(value: FormDataEntryValue | null) {
if (typeof value !== 'string') return '/admin'
if (!value.startsWith('/admin')) return '/admin'
return value
}
export async function clearFrontendCache(formData: FormData) {
const returnTo = normalizeReturnPath(formData.get('returnTo'))
for (const path of CACHE_PATHS) {
revalidatePath(path)
}
redirect(`${returnTo}${returnTo.includes('?') ? '&' : '?'}cache=cleared`)
}
@@ -0,0 +1,26 @@
'use client'
import { usePathname, useSearchParams } from 'next/navigation'
import { clearFrontendCache } from './admin-cache-actions'
export function AdminCacheButton() {
const pathname = usePathname()
const searchParams = useSearchParams()
const cacheCleared = searchParams.get('cache') === 'cleared'
const returnTo = pathname || '/admin'
return (
<div className="admin-cache-header-slot" aria-live="polite">
<form action={clearFrontendCache}>
<input type="hidden" name="returnTo" value={returnTo} />
<button
className="admin-cache-header-slot__button"
type="submit"
title={cacheCleared ? 'Frontend cache cleared.' : 'Clear frontend cache'}
>
{cacheCleared ? 'Cache cleared' : 'Clear cache'}
</button>
</form>
</div>
)
}
@@ -0,0 +1,170 @@
import Link from 'next/link'
import { getPayload } from 'payload'
import configPromise from '@payload-config'
import { clearFrontendCache } from './admin-cache-actions'
const QUICK_LINKS = [
{
description: 'Manage hero modules, featured products, support panels, and homepage news surfaces.',
href: '/admin/globals/homepage',
title: 'Homepage',
},
{
description: 'Open core product records, templates, editor-driven pages, downloads, reviews, and videos.',
href: '/admin/collections/products',
title: 'Products',
},
{
description: 'Review firmware, manuals, online-upgrade entries, and release-note content.',
href: '/admin/collections/downloads',
title: 'Downloads',
},
{
description: 'Keep support landing copy, FAQ content, contact instructions, and warranty policy aligned.',
href: '/admin/globals/supportPage',
title: 'Support',
},
{
description: 'Track incoming after-sales, distributor, and OEM requests in one operations lane.',
href: '/admin/collections/afterServiceRequests',
title: 'Operations',
},
{
description: 'Publish news, maintain redirects, and keep site-wide branding settings consistent.',
href: '/admin/collections/news',
title: 'Publishing',
},
] as const
const WORKSTREAMS = [
'Template-backed products: keep local template assignments stable and manage hero/card media from the product record.',
'Editor-driven products: use the imported legacy HTML as the rebuild reference, then move final content into overview and structured fields.',
'Downloads: maintain release notes, package links, online-upgrade guidance, and product visibility together.',
'Social proof: attach videos and reviews directly to each product so the frontend tabs stay complete.',
] as const
function formatCount(value: number) {
return new Intl.NumberFormat('en-US').format(value)
}
async function getOverviewCounts() {
const payload = await getPayload({ config: configPromise })
const [
products,
downloads,
productVideos,
productReviews,
news,
faqs,
dealers,
afterServiceRequests,
distributorRequests,
oemRequests,
] = await Promise.all([
payload.count({ collection: 'products' }),
payload.count({ collection: 'downloads' }),
payload.count({ collection: 'productVideos' }),
payload.count({ collection: 'productReviews' }),
payload.count({ collection: 'news' }),
payload.count({ collection: 'faqs' }),
payload.count({ collection: 'dealers' }),
payload.count({ collection: 'afterServiceRequests' }),
payload.count({ collection: 'distributorRequests' }),
payload.count({ collection: 'oemRequests' }),
])
return [
{ eyebrow: 'Catalog', href: '/admin/collections/products', title: 'Products', value: products.totalDocs },
{ eyebrow: 'Catalog', href: '/admin/collections/downloads', title: 'Downloads', value: downloads.totalDocs },
{ eyebrow: 'Social Proof', href: '/admin/collections/productVideos', title: 'Product Videos', value: productVideos.totalDocs },
{ eyebrow: 'Social Proof', href: '/admin/collections/productReviews', title: 'Product Reviews', value: productReviews.totalDocs },
{ eyebrow: 'Publishing', href: '/admin/collections/news', title: 'News', value: news.totalDocs },
{ eyebrow: 'Support', href: '/admin/collections/faqs', title: 'FAQs', value: faqs.totalDocs },
{ eyebrow: 'Sales', href: '/admin/collections/dealers', title: 'Dealers', value: dealers.totalDocs },
{
eyebrow: 'Operations',
href: '/admin/collections/afterServiceRequests',
title: 'Requests',
value: afterServiceRequests.totalDocs + distributorRequests.totalDocs + oemRequests.totalDocs,
},
]
}
export async function AdminHome({ cacheCleared = false }: { cacheCleared?: boolean }) {
const stats = await getOverviewCounts()
return (
<div className="studio-home">
<section className="studio-home__hero">
<div>
<p className="studio-home__eyebrow">Eversolo Content Studio</p>
<h1 className="studio-home__title">A cleaner control room for products, publishing, and support.</h1>
<p className="studio-home__lede">
This workspace keeps template-backed products, CMS-built pages, firmware updates, reviews,
videos, and support operations in one place.
</p>
</div>
<div className="studio-home__actions">
<Link className="studio-home__primary" href="/admin/collections/products">
Open products
</Link>
<Link className="studio-home__secondary" href="/admin/globals/homepage">
Edit homepage
</Link>
<form action={clearFrontendCache}>
<input type="hidden" name="returnTo" value="/admin" />
<button className="studio-home__secondary studio-home__secondary--warm" type="submit">
Clear frontend cache
</button>
</form>
</div>
</section>
{cacheCleared ? (
<section className="studio-home__notice" aria-live="polite">
Frontend cache cleared. Refresh the storefront to confirm the latest content, ordering, and homepage feeds.
</section>
) : null}
<section className="studio-home__stats">
{stats.map((stat) => (
<Link key={stat.title} href={stat.href} className="studio-home__stat-card">
<span className="studio-home__stat-eyebrow">{stat.eyebrow}</span>
<strong className="studio-home__stat-value">{formatCount(stat.value)}</strong>
<span className="studio-home__stat-title">{stat.title}</span>
</Link>
))}
</section>
<section className="studio-home__grid">
<div className="studio-home__panel">
<div className="studio-home__panel-head">
<h2>Priority lanes</h2>
<p>Jump into the surfaces that drive the storefront most often.</p>
</div>
<div className="studio-home__link-list">
{QUICK_LINKS.map((item) => (
<Link key={item.href} className="studio-home__link-card" href={item.href}>
<strong>{item.title}</strong>
<span>{item.description}</span>
</Link>
))}
</div>
</div>
<div className="studio-home__panel">
<div className="studio-home__panel-head">
<h2>Editorial guidance</h2>
<p>Keep the admin team working the same way across templates, downloads, and support.</p>
</div>
<ul className="studio-home__checklist">
{WORKSTREAMS.map((item) => (
<li key={item}>{item}</li>
))}
</ul>
</div>
</section>
</div>
)
}
+54
View File
@@ -0,0 +1,54 @@
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { FixedToolbarFeatureClient as FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
/** @type import('payload').ImportMap */
export const importMap = {
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/client#FixedToolbarFeatureClient": FixedToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
}
+38
View File
@@ -0,0 +1,38 @@
import configPromise from '@payload-config'
import '@payloadcms/next/css'
import './admin-theme.css'
import { RootLayout, handleServerFunctions, metadata } from '@payloadcms/next/layouts'
import type { ServerFunctionClient } from 'payload'
import { AdminCacheButton } from './admin/_components/admin-cache-button'
// @ts-expect-error generated by Payload admin import map generator
import { importMap } from './admin/importMap.js'
export { metadata }
const serverFunction: ServerFunctionClient = async (args) => {
'use server'
return handleServerFunctions({
...args,
config: configPromise,
importMap,
})
}
export default function PayloadRootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<>
{RootLayout({
children,
config: configPromise,
importMap,
serverFunction,
})}
<AdminCacheButton />
</>
)
}