commit faa74086fe4eacece59576fb382cb9a9c9053390 Author: “dongming” <“lidongming@aituringflow.com”> Date: Wed Jan 21 16:05:30 2026 +0800 first commit diff --git a/.env b/.env new file mode 100644 index 0000000..5ab2839 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +VITE_TENANT_SLUG=zitadel-example +VITE_TENANT_API_KEY=tenant_4_PBA8ymVYxHXe_0-Xe7f4akUZZ7nuIygHZupKppem +VITE_API_URL=http://localhost:3000/api diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml new file mode 100644 index 0000000..2c2ffa8 --- /dev/null +++ b/.gitea/workflows/deploy.yml @@ -0,0 +1,134 @@ +name: Deploy to Cloudflare Pages + +on: + push: + tags: + - 'deploy-*' # 只在推送 deploy-* 标签时触发 + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Parse tag to env + run: | + # 获取 tag 名称(Gitea Actions 使用 GITHUB_REF_NAME) + TAG_NAME="${GITHUB_REF_NAME}" + echo "TAG_NAME=$TAG_NAME" + + # Tag 格式: deploy-{project_name}-{deploymentId_no_dashes} + # 例如: deploy-b7ea026a-cf09-4e31-9f29-b55d7c652b71-123e4567e89b12d3a456426614174000 + + # 去掉 "deploy-" 前缀 + PREFIX="deploy-" + REST="${TAG_NAME#$PREFIX}" + + # deploymentId(无破折号)固定是最后32个字符 + DEPLOYMENT_ID="${REST: -32}" + + # project_name 是剩余部分(去掉最后的 "-" 和 deploymentId) + PROJECT_NAME="${REST%-${DEPLOYMENT_ID}}" + + echo "PROJECT_NAME=$PROJECT_NAME" >> "$GITHUB_ENV" + echo "DEPLOYMENT_ID=$DEPLOYMENT_ID" >> "$GITHUB_ENV" + #echo "DOMAIN=${PROJECT_NAME}-preview.turingflowai.com" >> "$GITHUB_ENV" + + # 调试输出 + echo "Parsed PROJECT_NAME: $PROJECT_NAME" + echo "Parsed DEPLOYMENT_ID: $DEPLOYMENT_ID" + + - name: Check toolchain (debug only, 可选) + run: | + node -v || echo "node not found" + npm -v || echo "npm not found" + curl --version || echo "curl not found" + + # 已经在 node:20-bookworm-slim 容器内,无需再 setup-node + # - name: Setup Node + # uses: actions/setup-node@v4 + # with: + # node-version: '20' + + - name: Use CN npm registry + run: | + npm config set registry http://repo.myhuaweicloud.com/repository/npm/ + + - name: Install dependencies + run: | + npm ci --no-audit --no-fund + + - name: Build + run: npm run build + + - name: Deploy to Cloudflare Pages + env: + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} + PROJECT_NAME: ${{ env.PROJECT_NAME }} + DOMAIN: ${{ env.DOMAIN }} + run: | + set -euo pipefail + echo "[deploy] project: $PROJECT_NAME" + echo "[deploy] domain: $DOMAIN" + + # 部署到 Cloudflare Pages (假定构建产物在 dist/) + npx wrangler pages deploy dist \ + --project-name "$PROJECT_NAME" \ + --branch main + + # 绑定自定义域名:-preview.turingflowai.com + #curl --fail -X POST \ + # "https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/pages/projects/${PROJECT_NAME}/domains" \ + # -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ + # -H "Content-Type: application/json" \ + # -d '{"name":"'"${DOMAIN}"'"}' \ + # || true + + - name: Notify Deploy Service (success) + if: success() + env: + DEPLOY_SERVICE_CALLBACK_URL: ${{ secrets.DEPLOY_SERVICE_CALLBACK_URL }} + DEPLOY_SERVICE_TOKEN: ${{ secrets.DEPLOY_SERVICE_TOKEN }} + DEPLOYMENT_ID: ${{ env.DEPLOYMENT_ID }} + run: | + set -euo pipefail + + # 获取当前 commit SHA (Gitea Actions 使用 GITHUB_SHA) + COMMIT_SHA="${GITHUB_SHA}" + + curl -X POST "$DEPLOY_SERVICE_CALLBACK_URL" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $DEPLOY_SERVICE_TOKEN" \ + -d '{ + "deploymentId": "'"${DEPLOYMENT_ID}"'", + "status": "deployed", + "commitSha": "'"${COMMIT_SHA}"'", + "cfDeploymentId": "", + "errorMessage": null + }' + + - name: Notify Deploy Service (failure) + if: failure() + env: + DEPLOY_SERVICE_CALLBACK_URL: ${{ secrets.DEPLOY_SERVICE_CALLBACK_URL }} + DEPLOY_SERVICE_TOKEN: ${{ secrets.DEPLOY_SERVICE_TOKEN }} + DEPLOYMENT_ID: ${{ env.DEPLOYMENT_ID }} + run: | + set -euo pipefail + + # 获取当前 commit SHA + COMMIT_SHA="${GITHUB_SHA}" + + curl -X POST "$DEPLOY_SERVICE_CALLBACK_URL" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $DEPLOY_SERVICE_TOKEN" \ + -d '{ + "deploymentId": "'"${DEPLOYMENT_ID}"'", + "status": "failed", + "commitSha": "'"${COMMIT_SHA}"'", + "cfDeploymentId": "", + "errorMessage": "see Gitea Actions logs" + }' diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e6fbd1b --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Playwright +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ + +.claude +docs +.pnpm-store +pnpm-lock.yaml +package-lock.json \ No newline at end of file diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..82ffb13 --- /dev/null +++ b/AGENT.md @@ -0,0 +1,116 @@ +# Client SDK Usage Guide + +This project uses an auto-generated SDK powered by `@hey-api/openapi-ts` to interact with the PayloadCMS multi-tenant backend. + +## SDK Structure + +- **Core Methods**: `src/clientsdk/sdk.gen.ts` (Contains classes like `Posts`, `Categories`, `Pages`, `Media`) +- **Type Definitions**: `src/clientsdk/types.gen.ts` (Contains interfaces like `Post`, `Category`, `Media`) +- **Query Serializer**: `src/clientsdk/querySerializer.ts` (Handles nested object serialization for PayloadCMS queries) +- **Client Factory**: `src/clientsdk/client/index.ts` (Use `createClient` to initialize a client instance) + +## 1. Initialization + +You must initialize the client with the correct `baseUrl`, `querySerializer`, and tenant headers. + +```typescript +import { createClient } from '../clientsdk/client'; +import { customQuerySerializer } from '../clientsdk/querySerializer'; +import { TENANT_SLUG, TENANT_API_KEY, API_URL } from '../config'; + +const client = createClient({ + baseUrl: API_URL, + querySerializer: customQuerySerializer, // CRITICAL: Required for nested where queries + headers: { + 'X-Tenant-Slug': TENANT_SLUG, + 'X-API-Key': TENANT_API_KEY, + }, +}); +``` + +## 2. Common Operations + +### Fetching a List (with Sorting & Limits) +```typescript +import { Posts } from '../clientsdk/sdk.gen'; + +const response = await Posts.listPosts({ + client, + query: { + limit: 10, + sort: '-createdAt', // Prefix with '-' for descending + }, +}); + +// Access the data +const posts = response.data?.docs || []; +``` + +### Fetching a Single Document by Slug (Filtering) +PayloadCMS uses a specific `where` query syntax. The `customQuerySerializer` handles the translation to `where[slug][equals]=my-slug`. + +```typescript +const response = await Posts.listPosts({ + client, + query: { + where: { + slug: { + equals: 'my-article-slug', + }, + }, + limit: 1, + }, +}); + +const post = response.data?.docs?.[0]; +``` + +### Filtering by Category Slug +```typescript +const response = await Posts.listPosts({ + client, + query: { + where: { + 'categories.slug': { + equals: 'news', + }, + }, + }, +}); +``` + +## 3. Data Patterns + +### Relationships +- **Categories**: In this project, categories are a **many-to-many** relationship. Always treat `post.categories` as an array. + - Correct: `post.categories?.[0]?.title` + - Incorrect: `post.category.title` +- **Media**: Images (like `heroImage`) are objects containing `url`, `alt`, and `sizes`. + - Example: `{post.heroImage?.alt}` + +### Rich Text (Lexical) +PayloadCMS provides Lexical rich text. We typically use `post.content_html` which is pre-rendered to HTML on the server. +- **React**: `
` +- **Vue**: `
` +- **Astro**: `
` + +## 4. TypeScript Usage + +Always use the generated types for better DX and safety. + +```typescript +import type { Post, Category, Media } from '../clientsdk/types.gen'; + +function formatPost(post: Post) { + return { + title: post.title, + date: new Date(post.createdAt).toLocaleDateString() + }; +} +``` + +## 5. Troubleshooting + +- **CORS Issues**: Ensure `API_URL` in `config.ts` matches the backend port (default 3000). +- **Empty Results**: Check if the `X-Tenant-Slug` header matches the slug assigned to your content in the CMS admin panel. +- **Nested Query Error**: If you see "Deeply-nested arrays/objects aren’t supported", verify that you are passing the `customQuerySerializer` to the `createClient` options. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d2e7761 --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) (or [oxc](https://oxc.rs) when used in [rolldown-vite](https://vite.dev/guide/rolldown)) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/hero-building.jpg b/hero-building.jpg new file mode 100644 index 0000000..3c03c59 Binary files /dev/null and b/hero-building.jpg differ diff --git a/index.html b/index.html new file mode 100644 index 0000000..c96a31f --- /dev/null +++ b/index.html @@ -0,0 +1,16 @@ + + + + + + + + + + 示例集团 - 稳健前行 · 携手共赢 + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..0341efc --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "react-template", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "axios": "^1.13.2", + "framer-motion": "^12.23.26", + "lucide-react": "^0.562.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "react-router-dom": "^7.11.0" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@tailwindcss/postcss": "^4.1.18", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.1.18", + "@types/node": "^24.10.1", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^5.1.1", + "@vitejs/plugin-react-swc": "^4.2.2", + "autoprefixer": "^10.4.23", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.4.24", + "globals": "^16.5.0", + "postcss": "^8.5.6", + "tailwindcss": "^4.1.18", + "typescript": "~5.9.3", + "typescript-eslint": "^8.46.4", + "vite": "^7.2.4" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..a7f73a2 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..a9ae56e --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/about-office.jpg b/public/images/about-office.jpg new file mode 100644 index 0000000..fc89792 Binary files /dev/null and b/public/images/about-office.jpg differ diff --git a/public/images/hero-bg.svg b/public/images/hero-bg.svg new file mode 100644 index 0000000..a9314fd --- /dev/null +++ b/public/images/hero-bg.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/hero-building.jpg b/public/images/hero-building.jpg new file mode 100644 index 0000000..7ea4f9b Binary files /dev/null and b/public/images/hero-building.jpg differ diff --git a/public/images/logo.svg b/public/images/logo.svg new file mode 100644 index 0000000..8983641 --- /dev/null +++ b/public/images/logo.svg @@ -0,0 +1,5 @@ + + + 示例集团 + CHENGYU GROUP + diff --git a/public/images/news-award.jpg b/public/images/news-award.jpg new file mode 100644 index 0000000..914c031 Binary files /dev/null and b/public/images/news-award.jpg differ diff --git a/public/images/news-company.jpg b/public/images/news-company.jpg new file mode 100644 index 0000000..7f75c8e Binary files /dev/null and b/public/images/news-company.jpg differ diff --git a/public/images/news-tech.jpg b/public/images/news-tech.jpg new file mode 100644 index 0000000..e29bc4a Binary files /dev/null and b/public/images/news-tech.jpg differ diff --git a/public/images/placeholder-honor.svg b/public/images/placeholder-honor.svg new file mode 100644 index 0000000..d0d8179 --- /dev/null +++ b/public/images/placeholder-honor.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/images/placeholder-news.svg b/public/images/placeholder-news.svg new file mode 100644 index 0000000..afcc1e4 --- /dev/null +++ b/public/images/placeholder-news.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/images/placeholder-services.svg b/public/images/placeholder-services.svg new file mode 100644 index 0000000..cd1a0b9 --- /dev/null +++ b/public/images/placeholder-services.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/placeholder-team.svg b/public/images/placeholder-team.svg new file mode 100644 index 0000000..c4de190 --- /dev/null +++ b/public/images/placeholder-team.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..391b6a0 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,80 @@ +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' +import { motion, AnimatePresence } from 'framer-motion' +import { Home } from './pages/Home' +import { About } from './pages/About' +import { Services } from './pages/Services' +import { News } from './pages/News' +import { Contact } from './pages/Contact' + +// 页面切换动画配置 +const pageVariants = { + initial: { + opacity: 0, + y: 20, + }, + animate: { + opacity: 1, + y: 0, + }, + exit: { + opacity: 0, + y: -20, + }, +} + +const pageTransition = { + duration: 0.4, + ease: 'easeInOut', +} + +// 页面包装组件 - 添加动画效果 +const PageWrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + +) + +// 404 页面组件 +const NotFound = () => ( + +
+
+

404

+

页面不存在

+

抱歉,您访问的页面不存在或已被移除

+ + 返回首页 + +
+
+
+) + +function App() { + return ( + + + + } /> + } /> + } /> + } /> + } /> + } /> + + + + ) +} + +export default App diff --git a/src/clientsdk/client.gen.ts b/src/clientsdk/client.gen.ts new file mode 100644 index 0000000..6d73486 --- /dev/null +++ b/src/clientsdk/client.gen.ts @@ -0,0 +1,16 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { type ClientOptions, type Config, createClient, createConfig } from './client'; +import type { ClientOptions as ClientOptions2 } from './types.gen'; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = (override?: Config) => Config & T>; + +export const client = createClient(createConfig({ baseUrl: 'http://localhost:3000' })); diff --git a/src/clientsdk/client/client.gen.ts b/src/clientsdk/client/client.gen.ts new file mode 100644 index 0000000..c2a5190 --- /dev/null +++ b/src/clientsdk/client/client.gen.ts @@ -0,0 +1,301 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { createSseClient } from '../core/serverSentEvents.gen'; +import type { HttpMethod } from '../core/types.gen'; +import { getValidRequestBody } from '../core/utils.gen'; +import type { + Client, + Config, + RequestOptions, + ResolvedRequestOptions, +} from './types.gen'; +import { + buildUrl, + createConfig, + createInterceptors, + getParseAs, + mergeConfigs, + mergeHeaders, + setAuthParams, +} from './utils.gen'; + +type ReqInit = Omit & { + body?: any; + headers: ReturnType; +}; + +export const createClient = (config: Config = {}): Client => { + let _config = mergeConfigs(createConfig(), config); + + const getConfig = (): Config => ({ ..._config }); + + const setConfig = (config: Config): Config => { + _config = mergeConfigs(_config, config); + return getConfig(); + }; + + const interceptors = createInterceptors< + Request, + Response, + unknown, + ResolvedRequestOptions + >(); + + const beforeRequest = async (options: RequestOptions) => { + const opts = { + ..._config, + ...options, + fetch: options.fetch ?? _config.fetch ?? globalThis.fetch, + headers: mergeHeaders(_config.headers, options.headers), + serializedBody: undefined, + }; + + if (opts.security) { + await setAuthParams({ + ...opts, + security: opts.security, + }); + } + + if (opts.requestValidator) { + await opts.requestValidator(opts); + } + + if (opts.body !== undefined && opts.bodySerializer) { + opts.serializedBody = opts.bodySerializer(opts.body); + } + + // remove Content-Type header if body is empty to avoid sending invalid requests + if (opts.body === undefined || opts.serializedBody === '') { + opts.headers.delete('Content-Type'); + } + + const url = buildUrl(opts); + + return { opts, url }; + }; + + const request: Client['request'] = async (options) => { + // @ts-expect-error + const { opts, url } = await beforeRequest(options); + const requestInit: ReqInit = { + redirect: 'follow', + ...opts, + body: getValidRequestBody(opts), + }; + + let request = new Request(url, requestInit); + + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = opts.fetch!; + let response: Response; + + try { + response = await _fetch(request); + } catch (error) { + // Handle fetch exceptions (AbortError, network errors, etc.) + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn( + error, + undefined as any, + request, + opts, + )) as unknown; + } + } + + finalError = finalError || ({} as unknown); + + if (opts.throwOnError) { + throw finalError; + } + + // Return error response + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + request, + response: undefined as any, + }; + } + + for (const fn of interceptors.response.fns) { + if (fn) { + response = await fn(response, request, opts); + } + } + + const result = { + request, + response, + }; + + if (response.ok) { + const parseAs = + (opts.parseAs === 'auto' + ? getParseAs(response.headers.get('Content-Type')) + : opts.parseAs) ?? 'json'; + + if ( + response.status === 204 || + response.headers.get('Content-Length') === '0' + ) { + let emptyData: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'text': + emptyData = await response[parseAs](); + break; + case 'formData': + emptyData = new FormData(); + break; + case 'stream': + emptyData = response.body; + break; + case 'json': + default: + emptyData = {}; + break; + } + return opts.responseStyle === 'data' + ? emptyData + : { + data: emptyData, + ...result, + }; + } + + let data: any; + switch (parseAs) { + case 'arrayBuffer': + case 'blob': + case 'formData': + case 'json': + case 'text': + data = await response[parseAs](); + break; + case 'stream': + return opts.responseStyle === 'data' + ? response.body + : { + data: response.body, + ...result, + }; + } + + if (parseAs === 'json') { + if (opts.responseValidator) { + await opts.responseValidator(data); + } + + if (opts.responseTransformer) { + data = await opts.responseTransformer(data); + } + } + + return opts.responseStyle === 'data' + ? data + : { + data, + ...result, + }; + } + + const textError = await response.text(); + let jsonError: unknown; + + try { + jsonError = JSON.parse(textError); + } catch { + // noop + } + + const error = jsonError ?? textError; + let finalError = error; + + for (const fn of interceptors.error.fns) { + if (fn) { + finalError = (await fn(error, response, request, opts)) as string; + } + } + + finalError = finalError || ({} as string); + + if (opts.throwOnError) { + throw finalError; + } + + // TODO: we probably want to return error and improve types + return opts.responseStyle === 'data' + ? undefined + : { + error: finalError, + ...result, + }; + }; + + const makeMethodFn = + (method: Uppercase) => (options: RequestOptions) => + request({ ...options, method }); + + const makeSseFn = + (method: Uppercase) => async (options: RequestOptions) => { + const { opts, url } = await beforeRequest(options); + return createSseClient({ + ...opts, + body: opts.body as BodyInit | null | undefined, + headers: opts.headers as unknown as Record, + method, + onRequest: async (url, init) => { + let request = new Request(url, init); + for (const fn of interceptors.request.fns) { + if (fn) { + request = await fn(request, opts); + } + } + return request; + }, + url, + }); + }; + + return { + buildUrl, + connect: makeMethodFn('CONNECT'), + delete: makeMethodFn('DELETE'), + get: makeMethodFn('GET'), + getConfig, + head: makeMethodFn('HEAD'), + interceptors, + options: makeMethodFn('OPTIONS'), + patch: makeMethodFn('PATCH'), + post: makeMethodFn('POST'), + put: makeMethodFn('PUT'), + request, + setConfig, + sse: { + connect: makeSseFn('CONNECT'), + delete: makeSseFn('DELETE'), + get: makeSseFn('GET'), + head: makeSseFn('HEAD'), + options: makeSseFn('OPTIONS'), + patch: makeSseFn('PATCH'), + post: makeSseFn('POST'), + put: makeSseFn('PUT'), + trace: makeSseFn('TRACE'), + }, + trace: makeMethodFn('TRACE'), + } as Client; +}; diff --git a/src/clientsdk/client/index.ts b/src/clientsdk/client/index.ts new file mode 100644 index 0000000..b295ede --- /dev/null +++ b/src/clientsdk/client/index.ts @@ -0,0 +1,25 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type { Auth } from '../core/auth.gen'; +export type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +export { + formDataBodySerializer, + jsonBodySerializer, + urlSearchParamsBodySerializer, +} from '../core/bodySerializer.gen'; +export { buildClientParams } from '../core/params.gen'; +export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen'; +export { createClient } from './client.gen'; +export type { + Client, + ClientOptions, + Config, + CreateClientConfig, + Options, + RequestOptions, + RequestResult, + ResolvedRequestOptions, + ResponseStyle, + TDataShape, +} from './types.gen'; +export { createConfig, mergeHeaders } from './utils.gen'; diff --git a/src/clientsdk/client/types.gen.ts b/src/clientsdk/client/types.gen.ts new file mode 100644 index 0000000..b4a499c --- /dev/null +++ b/src/clientsdk/client/types.gen.ts @@ -0,0 +1,241 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth } from '../core/auth.gen'; +import type { + ServerSentEventsOptions, + ServerSentEventsResult, +} from '../core/serverSentEvents.gen'; +import type { + Client as CoreClient, + Config as CoreConfig, +} from '../core/types.gen'; +import type { Middleware } from './utils.gen'; + +export type ResponseStyle = 'data' | 'fields'; + +export interface Config + extends Omit, + CoreConfig { + /** + * Base URL for all requests made by this client. + */ + baseUrl?: T['baseUrl']; + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Please don't use the Fetch client for Next.js applications. The `next` + * options won't have any effect. + * + * Install {@link https://www.npmjs.com/package/@hey-api/client-next `@hey-api/client-next`} instead. + */ + next?: never; + /** + * Return the response data parsed in a specified format. By default, `auto` + * will infer the appropriate method from the `Content-Type` response header. + * You can override this behavior with any of the {@link Body} methods. + * Select `stream` if you don't want to parse response data at all. + * + * @default 'auto' + */ + parseAs?: + | 'arrayBuffer' + | 'auto' + | 'blob' + | 'formData' + | 'json' + | 'stream' + | 'text'; + /** + * Should we return only data or multiple fields (data, error, response, etc.)? + * + * @default 'fields' + */ + responseStyle?: ResponseStyle; + /** + * Throw an error instead of returning it in the response? + * + * @default false + */ + throwOnError?: T['throwOnError']; +} + +export interface RequestOptions< + TData = unknown, + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends Config<{ + responseStyle: TResponseStyle; + throwOnError: ThrowOnError; + }>, + Pick< + ServerSentEventsOptions, + | 'onSseError' + | 'onSseEvent' + | 'sseDefaultRetryDelay' + | 'sseMaxRetryAttempts' + | 'sseMaxRetryDelay' + > { + /** + * Any body that you want to add to your request. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#body} + */ + body?: unknown; + path?: Record; + query?: Record; + /** + * Security mechanism(s) to use for the request. + */ + security?: ReadonlyArray; + url: Url; +} + +export interface ResolvedRequestOptions< + TResponseStyle extends ResponseStyle = 'fields', + ThrowOnError extends boolean = boolean, + Url extends string = string, +> extends RequestOptions { + serializedBody?: string; +} + +export type RequestResult< + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = boolean, + TResponseStyle extends ResponseStyle = 'fields', +> = ThrowOnError extends true + ? Promise< + TResponseStyle extends 'data' + ? TData extends Record + ? TData[keyof TData] + : TData + : { + data: TData extends Record + ? TData[keyof TData] + : TData; + request: Request; + response: Response; + } + > + : Promise< + TResponseStyle extends 'data' + ? + | (TData extends Record + ? TData[keyof TData] + : TData) + | undefined + : ( + | { + data: TData extends Record + ? TData[keyof TData] + : TData; + error: undefined; + } + | { + data: undefined; + error: TError extends Record + ? TError[keyof TError] + : TError; + } + ) & { + request: Request; + response: Response; + } + >; + +export interface ClientOptions { + baseUrl?: string; + responseStyle?: ResponseStyle; + throwOnError?: boolean; +} + +type MethodFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => RequestResult; + +type SseFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'>, +) => Promise>; + +type RequestFn = < + TData = unknown, + TError = unknown, + ThrowOnError extends boolean = false, + TResponseStyle extends ResponseStyle = 'fields', +>( + options: Omit, 'method'> & + Pick< + Required>, + 'method' + >, +) => RequestResult; + +type BuildUrlFn = < + TData extends { + body?: unknown; + path?: Record; + query?: Record; + url: string; + }, +>( + options: TData & Options, +) => string; + +export type Client = CoreClient< + RequestFn, + Config, + MethodFn, + BuildUrlFn, + SseFn +> & { + interceptors: Middleware; +}; + +/** + * The `createClientConfig()` function will be called on client initialization + * and the returned object will become the client's initial configuration. + * + * You may want to initialize your client this way instead of calling + * `setConfig()`. This is useful for example if you're using Next.js + * to ensure your client always has the correct values. + */ +export type CreateClientConfig = ( + override?: Config, +) => Config & T>; + +export interface TDataShape { + body?: unknown; + headers?: unknown; + path?: unknown; + query?: unknown; + url: string; +} + +type OmitKeys = Pick>; + +export type Options< + TData extends TDataShape = TDataShape, + ThrowOnError extends boolean = boolean, + TResponse = unknown, + TResponseStyle extends ResponseStyle = 'fields', +> = OmitKeys< + RequestOptions, + 'body' | 'path' | 'query' | 'url' +> & + ([TData] extends [never] ? unknown : Omit); diff --git a/src/clientsdk/client/utils.gen.ts b/src/clientsdk/client/utils.gen.ts new file mode 100644 index 0000000..4c48a9e --- /dev/null +++ b/src/clientsdk/client/utils.gen.ts @@ -0,0 +1,332 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import { getAuthToken } from '../core/auth.gen'; +import type { QuerySerializerOptions } from '../core/bodySerializer.gen'; +import { jsonBodySerializer } from '../core/bodySerializer.gen'; +import { + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from '../core/pathSerializer.gen'; +import { getUrl } from '../core/utils.gen'; +import type { Client, ClientOptions, Config, RequestOptions } from './types.gen'; + +export const createQuerySerializer = ({ + parameters = {}, + ...args +}: QuerySerializerOptions = {}) => { + const querySerializer = (queryParams: T) => { + const search: string[] = []; + if (queryParams && typeof queryParams === 'object') { + for (const name in queryParams) { + const value = queryParams[name]; + + if (value === undefined || value === null) { + continue; + } + + const options = parameters[name] || args; + + if (Array.isArray(value)) { + const serializedArray = serializeArrayParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'form', + value, + ...options.array, + }); + if (serializedArray) search.push(serializedArray); + } else if (typeof value === 'object') { + const serializedObject = serializeObjectParam({ + allowReserved: options.allowReserved, + explode: true, + name, + style: 'deepObject', + value: value as Record, + ...options.object, + }); + if (serializedObject) search.push(serializedObject); + } else { + const serializedPrimitive = serializePrimitiveParam({ + allowReserved: options.allowReserved, + name, + value: value as string, + }); + if (serializedPrimitive) search.push(serializedPrimitive); + } + } + } + return search.join('&'); + }; + return querySerializer; +}; + +/** + * Infers parseAs value from provided Content-Type header. + */ +export const getParseAs = ( + contentType: string | null, +): Exclude => { + if (!contentType) { + // If no Content-Type header is provided, the best we can do is return the raw response body, + // which is effectively the same as the 'stream' option. + return 'stream'; + } + + const cleanContent = contentType.split(';')[0]?.trim(); + + if (!cleanContent) { + return; + } + + if ( + cleanContent.startsWith('application/json') || + cleanContent.endsWith('+json') + ) { + return 'json'; + } + + if (cleanContent === 'multipart/form-data') { + return 'formData'; + } + + if ( + ['application/', 'audio/', 'image/', 'video/'].some((type) => + cleanContent.startsWith(type), + ) + ) { + return 'blob'; + } + + if (cleanContent.startsWith('text/')) { + return 'text'; + } + + return; +}; + +const checkForExistence = ( + options: Pick & { + headers: Headers; + }, + name?: string, +): boolean => { + if (!name) { + return false; + } + if ( + options.headers.has(name) || + options.query?.[name] || + options.headers.get('Cookie')?.includes(`${name}=`) + ) { + return true; + } + return false; +}; + +export const setAuthParams = async ({ + security, + ...options +}: Pick, 'security'> & + Pick & { + headers: Headers; + }) => { + for (const auth of security) { + if (checkForExistence(options, auth.name)) { + continue; + } + + const token = await getAuthToken(auth, options.auth); + + if (!token) { + continue; + } + + const name = auth.name ?? 'Authorization'; + + switch (auth.in) { + case 'query': + if (!options.query) { + options.query = {}; + } + options.query[name] = token; + break; + case 'cookie': + options.headers.append('Cookie', `${name}=${token}`); + break; + case 'header': + default: + options.headers.set(name, token); + break; + } + } +}; + +export const buildUrl: Client['buildUrl'] = (options) => + getUrl({ + baseUrl: options.baseUrl as string, + path: options.path, + query: options.query, + querySerializer: + typeof options.querySerializer === 'function' + ? options.querySerializer + : createQuerySerializer(options.querySerializer), + url: options.url, + }); + +export const mergeConfigs = (a: Config, b: Config): Config => { + const config = { ...a, ...b }; + if (config.baseUrl?.endsWith('/')) { + config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1); + } + config.headers = mergeHeaders(a.headers, b.headers); + return config; +}; + +const headersEntries = (headers: Headers): Array<[string, string]> => { + const entries: Array<[string, string]> = []; + headers.forEach((value, key) => { + entries.push([key, value]); + }); + return entries; +}; + +export const mergeHeaders = ( + ...headers: Array['headers'] | undefined> +): Headers => { + const mergedHeaders = new Headers(); + for (const header of headers) { + if (!header) { + continue; + } + + const iterator = + header instanceof Headers + ? headersEntries(header) + : Object.entries(header); + + for (const [key, value] of iterator) { + if (value === null) { + mergedHeaders.delete(key); + } else if (Array.isArray(value)) { + for (const v of value) { + mergedHeaders.append(key, v as string); + } + } else if (value !== undefined) { + // assume object headers are meant to be JSON stringified, i.e. their + // content value in OpenAPI specification is 'application/json' + mergedHeaders.set( + key, + typeof value === 'object' ? JSON.stringify(value) : (value as string), + ); + } + } + } + return mergedHeaders; +}; + +type ErrInterceptor = ( + error: Err, + response: Res, + request: Req, + options: Options, +) => Err | Promise; + +type ReqInterceptor = ( + request: Req, + options: Options, +) => Req | Promise; + +type ResInterceptor = ( + response: Res, + request: Req, + options: Options, +) => Res | Promise; + +class Interceptors { + fns: Array = []; + + clear(): void { + this.fns = []; + } + + eject(id: number | Interceptor): void { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = null; + } + } + + exists(id: number | Interceptor): boolean { + const index = this.getInterceptorIndex(id); + return Boolean(this.fns[index]); + } + + getInterceptorIndex(id: number | Interceptor): number { + if (typeof id === 'number') { + return this.fns[id] ? id : -1; + } + return this.fns.indexOf(id); + } + + update( + id: number | Interceptor, + fn: Interceptor, + ): number | Interceptor | false { + const index = this.getInterceptorIndex(id); + if (this.fns[index]) { + this.fns[index] = fn; + return id; + } + return false; + } + + use(fn: Interceptor): number { + this.fns.push(fn); + return this.fns.length - 1; + } +} + +export interface Middleware { + error: Interceptors>; + request: Interceptors>; + response: Interceptors>; +} + +export const createInterceptors = (): Middleware< + Req, + Res, + Err, + Options +> => ({ + error: new Interceptors>(), + request: new Interceptors>(), + response: new Interceptors>(), +}); + +const defaultQuerySerializer = createQuerySerializer({ + allowReserved: false, + array: { + explode: true, + style: 'form', + }, + object: { + explode: true, + style: 'deepObject', + }, +}); + +const defaultHeaders = { + 'Content-Type': 'application/json', +}; + +export const createConfig = ( + override: Config & T> = {}, +): Config & T> => ({ + ...jsonBodySerializer, + headers: defaultHeaders, + parseAs: 'auto', + querySerializer: defaultQuerySerializer, + ...override, +}); diff --git a/src/clientsdk/core/auth.gen.ts b/src/clientsdk/core/auth.gen.ts new file mode 100644 index 0000000..f8a7326 --- /dev/null +++ b/src/clientsdk/core/auth.gen.ts @@ -0,0 +1,42 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type AuthToken = string | undefined; + +export interface Auth { + /** + * Which part of the request do we use to send the auth? + * + * @default 'header' + */ + in?: 'header' | 'query' | 'cookie'; + /** + * Header or query parameter name. + * + * @default 'Authorization' + */ + name?: string; + scheme?: 'basic' | 'bearer'; + type: 'apiKey' | 'http'; +} + +export const getAuthToken = async ( + auth: Auth, + callback: ((auth: Auth) => Promise | AuthToken) | AuthToken, +): Promise => { + const token = + typeof callback === 'function' ? await callback(auth) : callback; + + if (!token) { + return; + } + + if (auth.scheme === 'bearer') { + return `Bearer ${token}`; + } + + if (auth.scheme === 'basic') { + return `Basic ${btoa(token)}`; + } + + return token; +}; diff --git a/src/clientsdk/core/bodySerializer.gen.ts b/src/clientsdk/core/bodySerializer.gen.ts new file mode 100644 index 0000000..552b50f --- /dev/null +++ b/src/clientsdk/core/bodySerializer.gen.ts @@ -0,0 +1,100 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { + ArrayStyle, + ObjectStyle, + SerializerOptions, +} from './pathSerializer.gen'; + +export type QuerySerializer = (query: Record) => string; + +export type BodySerializer = (body: any) => any; + +type QuerySerializerOptionsObject = { + allowReserved?: boolean; + array?: Partial>; + object?: Partial>; +}; + +export type QuerySerializerOptions = QuerySerializerOptionsObject & { + /** + * Per-parameter serialization overrides. When provided, these settings + * override the global array/object settings for specific parameter names. + */ + parameters?: Record; +}; + +const serializeFormDataPair = ( + data: FormData, + key: string, + value: unknown, +): void => { + if (typeof value === 'string' || value instanceof Blob) { + data.append(key, value); + } else if (value instanceof Date) { + data.append(key, value.toISOString()); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +const serializeUrlSearchParamsPair = ( + data: URLSearchParams, + key: string, + value: unknown, +): void => { + if (typeof value === 'string') { + data.append(key, value); + } else { + data.append(key, JSON.stringify(value)); + } +}; + +export const formDataBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): FormData => { + const data = new FormData(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeFormDataPair(data, key, v)); + } else { + serializeFormDataPair(data, key, value); + } + }); + + return data; + }, +}; + +export const jsonBodySerializer = { + bodySerializer: (body: T): string => + JSON.stringify(body, (_key, value) => + typeof value === 'bigint' ? value.toString() : value, + ), +}; + +export const urlSearchParamsBodySerializer = { + bodySerializer: | Array>>( + body: T, + ): string => { + const data = new URLSearchParams(); + + Object.entries(body).forEach(([key, value]) => { + if (value === undefined || value === null) { + return; + } + if (Array.isArray(value)) { + value.forEach((v) => serializeUrlSearchParamsPair(data, key, v)); + } else { + serializeUrlSearchParamsPair(data, key, value); + } + }); + + return data.toString(); + }, +}; diff --git a/src/clientsdk/core/params.gen.ts b/src/clientsdk/core/params.gen.ts new file mode 100644 index 0000000..602715c --- /dev/null +++ b/src/clientsdk/core/params.gen.ts @@ -0,0 +1,176 @@ +// This file is auto-generated by @hey-api/openapi-ts + +type Slot = 'body' | 'headers' | 'path' | 'query'; + +export type Field = + | { + in: Exclude; + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If omitted, we use the same value as `key`. + */ + map?: string; + } + | { + in: Extract; + /** + * Key isn't required for bodies. + */ + key?: string; + map?: string; + } + | { + /** + * Field name. This is the name we want the user to see and use. + */ + key: string; + /** + * Field mapped name. This is the name we want to use in the request. + * If `in` is omitted, `map` aliases `key` to the transport layer. + */ + map: Slot; + }; + +export interface Fields { + allowExtra?: Partial>; + args?: ReadonlyArray; +} + +export type FieldsConfig = ReadonlyArray; + +const extraPrefixesMap: Record = { + $body_: 'body', + $headers_: 'headers', + $path_: 'path', + $query_: 'query', +}; +const extraPrefixes = Object.entries(extraPrefixesMap); + +type KeyMap = Map< + string, + | { + in: Slot; + map?: string; + } + | { + in?: never; + map: Slot; + } +>; + +const buildKeyMap = (fields: FieldsConfig, map?: KeyMap): KeyMap => { + if (!map) { + map = new Map(); + } + + for (const config of fields) { + if ('in' in config) { + if (config.key) { + map.set(config.key, { + in: config.in, + map: config.map, + }); + } + } else if ('key' in config) { + map.set(config.key, { + map: config.map, + }); + } else if (config.args) { + buildKeyMap(config.args, map); + } + } + + return map; +}; + +interface Params { + body: unknown; + headers: Record; + path: Record; + query: Record; +} + +const stripEmptySlots = (params: Params) => { + for (const [slot, value] of Object.entries(params)) { + if (value && typeof value === 'object' && !Object.keys(value).length) { + delete params[slot as Slot]; + } + } +}; + +export const buildClientParams = ( + args: ReadonlyArray, + fields: FieldsConfig, +) => { + const params: Params = { + body: {}, + headers: {}, + path: {}, + query: {}, + }; + + const map = buildKeyMap(fields); + + let config: FieldsConfig[number] | undefined; + + for (const [index, arg] of args.entries()) { + if (fields[index]) { + config = fields[index]; + } + + if (!config) { + continue; + } + + if ('in' in config) { + if (config.key) { + const field = map.get(config.key)!; + const name = field.map || config.key; + if (field.in) { + (params[field.in] as Record)[name] = arg; + } + } else { + params.body = arg; + } + } else { + for (const [key, value] of Object.entries(arg ?? {})) { + const field = map.get(key); + + if (field) { + if (field.in) { + const name = field.map || key; + (params[field.in] as Record)[name] = value; + } else { + params[field.map] = value; + } + } else { + const extra = extraPrefixes.find(([prefix]) => + key.startsWith(prefix), + ); + + if (extra) { + const [prefix, slot] = extra; + (params[slot] as Record)[ + key.slice(prefix.length) + ] = value; + } else if ('allowExtra' in config && config.allowExtra) { + for (const [slot, allowed] of Object.entries(config.allowExtra)) { + if (allowed) { + (params[slot as Slot] as Record)[key] = value; + break; + } + } + } + } + } + } + } + + stripEmptySlots(params); + + return params; +}; diff --git a/src/clientsdk/core/pathSerializer.gen.ts b/src/clientsdk/core/pathSerializer.gen.ts new file mode 100644 index 0000000..8d99931 --- /dev/null +++ b/src/clientsdk/core/pathSerializer.gen.ts @@ -0,0 +1,181 @@ +// This file is auto-generated by @hey-api/openapi-ts + +interface SerializeOptions + extends SerializePrimitiveOptions, + SerializerOptions {} + +interface SerializePrimitiveOptions { + allowReserved?: boolean; + name: string; +} + +export interface SerializerOptions { + /** + * @default true + */ + explode: boolean; + style: T; +} + +export type ArrayStyle = 'form' | 'spaceDelimited' | 'pipeDelimited'; +export type ArraySeparatorStyle = ArrayStyle | MatrixStyle; +type MatrixStyle = 'label' | 'matrix' | 'simple'; +export type ObjectStyle = 'form' | 'deepObject'; +type ObjectSeparatorStyle = ObjectStyle | MatrixStyle; + +interface SerializePrimitiveParam extends SerializePrimitiveOptions { + value: string; +} + +export const separatorArrayExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => { + switch (style) { + case 'form': + return ','; + case 'pipeDelimited': + return '|'; + case 'spaceDelimited': + return '%20'; + default: + return ','; + } +}; + +export const separatorObjectExplode = (style: ObjectSeparatorStyle) => { + switch (style) { + case 'label': + return '.'; + case 'matrix': + return ';'; + case 'simple': + return ','; + default: + return '&'; + } +}; + +export const serializeArrayParam = ({ + allowReserved, + explode, + name, + style, + value, +}: SerializeOptions & { + value: unknown[]; +}) => { + if (!explode) { + const joinedValues = ( + allowReserved ? value : value.map((v) => encodeURIComponent(v as string)) + ).join(separatorArrayNoExplode(style)); + switch (style) { + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + case 'simple': + return joinedValues; + default: + return `${name}=${joinedValues}`; + } + } + + const separator = separatorArrayExplode(style); + const joinedValues = value + .map((v) => { + if (style === 'label' || style === 'simple') { + return allowReserved ? v : encodeURIComponent(v as string); + } + + return serializePrimitiveParam({ + allowReserved, + name, + value: v as string, + }); + }) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; + +export const serializePrimitiveParam = ({ + allowReserved, + name, + value, +}: SerializePrimitiveParam) => { + if (value === undefined || value === null) { + return ''; + } + + if (typeof value === 'object') { + throw new Error( + 'Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.', + ); + } + + return `${name}=${allowReserved ? value : encodeURIComponent(value)}`; +}; + +export const serializeObjectParam = ({ + allowReserved, + explode, + name, + style, + value, + valueOnly, +}: SerializeOptions & { + value: Record | Date; + valueOnly?: boolean; +}) => { + if (value instanceof Date) { + return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`; + } + + if (style !== 'deepObject' && !explode) { + let values: string[] = []; + Object.entries(value).forEach(([key, v]) => { + values = [ + ...values, + key, + allowReserved ? (v as string) : encodeURIComponent(v as string), + ]; + }); + const joinedValues = values.join(','); + switch (style) { + case 'form': + return `${name}=${joinedValues}`; + case 'label': + return `.${joinedValues}`; + case 'matrix': + return `;${name}=${joinedValues}`; + default: + return joinedValues; + } + } + + const separator = separatorObjectExplode(style); + const joinedValues = Object.entries(value) + .map(([key, v]) => + serializePrimitiveParam({ + allowReserved, + name: style === 'deepObject' ? `${name}[${key}]` : key, + value: v as string, + }), + ) + .join(separator); + return style === 'label' || style === 'matrix' + ? separator + joinedValues + : joinedValues; +}; diff --git a/src/clientsdk/core/queryKeySerializer.gen.ts b/src/clientsdk/core/queryKeySerializer.gen.ts new file mode 100644 index 0000000..d3bb683 --- /dev/null +++ b/src/clientsdk/core/queryKeySerializer.gen.ts @@ -0,0 +1,136 @@ +// This file is auto-generated by @hey-api/openapi-ts + +/** + * JSON-friendly union that mirrors what Pinia Colada can hash. + */ +export type JsonValue = + | null + | string + | number + | boolean + | JsonValue[] + | { [key: string]: JsonValue }; + +/** + * Replacer that converts non-JSON values (bigint, Date, etc.) to safe substitutes. + */ +export const queryKeyJsonReplacer = (_key: string, value: unknown) => { + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + if (typeof value === 'bigint') { + return value.toString(); + } + if (value instanceof Date) { + return value.toISOString(); + } + return value; +}; + +/** + * Safely stringifies a value and parses it back into a JsonValue. + */ +export const stringifyToJsonValue = (input: unknown): JsonValue | undefined => { + try { + const json = JSON.stringify(input, queryKeyJsonReplacer); + if (json === undefined) { + return undefined; + } + return JSON.parse(json) as JsonValue; + } catch { + return undefined; + } +}; + +/** + * Detects plain objects (including objects with a null prototype). + */ +const isPlainObject = (value: unknown): value is Record => { + if (value === null || typeof value !== 'object') { + return false; + } + const prototype = Object.getPrototypeOf(value as object); + return prototype === Object.prototype || prototype === null; +}; + +/** + * Turns URLSearchParams into a sorted JSON object for deterministic keys. + */ +const serializeSearchParams = (params: URLSearchParams): JsonValue => { + const entries = Array.from(params.entries()).sort(([a], [b]) => + a.localeCompare(b), + ); + const result: Record = {}; + + for (const [key, value] of entries) { + const existing = result[key]; + if (existing === undefined) { + result[key] = value; + continue; + } + + if (Array.isArray(existing)) { + (existing as string[]).push(value); + } else { + result[key] = [existing, value]; + } + } + + return result; +}; + +/** + * Normalizes any accepted value into a JSON-friendly shape for query keys. + */ +export const serializeQueryKeyValue = ( + value: unknown, +): JsonValue | undefined => { + if (value === null) { + return null; + } + + if ( + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + return value; + } + + if ( + value === undefined || + typeof value === 'function' || + typeof value === 'symbol' + ) { + return undefined; + } + + if (typeof value === 'bigint') { + return value.toString(); + } + + if (value instanceof Date) { + return value.toISOString(); + } + + if (Array.isArray(value)) { + return stringifyToJsonValue(value); + } + + if ( + typeof URLSearchParams !== 'undefined' && + value instanceof URLSearchParams + ) { + return serializeSearchParams(value); + } + + if (isPlainObject(value)) { + return stringifyToJsonValue(value); + } + + return undefined; +}; diff --git a/src/clientsdk/core/serverSentEvents.gen.ts b/src/clientsdk/core/serverSentEvents.gen.ts new file mode 100644 index 0000000..343d25a --- /dev/null +++ b/src/clientsdk/core/serverSentEvents.gen.ts @@ -0,0 +1,266 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Config } from './types.gen'; + +export type ServerSentEventsOptions = Omit< + RequestInit, + 'method' +> & + Pick & { + /** + * Fetch API implementation. You can use this option to provide a custom + * fetch instance. + * + * @default globalThis.fetch + */ + fetch?: typeof fetch; + /** + * Implementing clients can call request interceptors inside this hook. + */ + onRequest?: (url: string, init: RequestInit) => Promise; + /** + * Callback invoked when a network or parsing error occurs during streaming. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param error The error that occurred. + */ + onSseError?: (error: unknown) => void; + /** + * Callback invoked when an event is streamed from the server. + * + * This option applies only if the endpoint returns a stream of events. + * + * @param event Event streamed from the server. + * @returns Nothing (void). + */ + onSseEvent?: (event: StreamEvent) => void; + serializedBody?: RequestInit['body']; + /** + * Default retry delay in milliseconds. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 3000 + */ + sseDefaultRetryDelay?: number; + /** + * Maximum number of retry attempts before giving up. + */ + sseMaxRetryAttempts?: number; + /** + * Maximum retry delay in milliseconds. + * + * Applies only when exponential backoff is used. + * + * This option applies only if the endpoint returns a stream of events. + * + * @default 30000 + */ + sseMaxRetryDelay?: number; + /** + * Optional sleep function for retry backoff. + * + * Defaults to using `setTimeout`. + */ + sseSleepFn?: (ms: number) => Promise; + url: string; + }; + +export interface StreamEvent { + data: TData; + event?: string; + id?: string; + retry?: number; +} + +export type ServerSentEventsResult< + TData = unknown, + TReturn = void, + TNext = unknown, +> = { + stream: AsyncGenerator< + TData extends Record ? TData[keyof TData] : TData, + TReturn, + TNext + >; +}; + +export const createSseClient = ({ + onRequest, + onSseError, + onSseEvent, + responseTransformer, + responseValidator, + sseDefaultRetryDelay, + sseMaxRetryAttempts, + sseMaxRetryDelay, + sseSleepFn, + url, + ...options +}: ServerSentEventsOptions): ServerSentEventsResult => { + let lastEventId: string | undefined; + + const sleep = + sseSleepFn ?? + ((ms: number) => new Promise((resolve) => setTimeout(resolve, ms))); + + const createStream = async function* () { + let retryDelay: number = sseDefaultRetryDelay ?? 3000; + let attempt = 0; + const signal = options.signal ?? new AbortController().signal; + + while (true) { + if (signal.aborted) break; + + attempt++; + + const headers = + options.headers instanceof Headers + ? options.headers + : new Headers(options.headers as Record | undefined); + + if (lastEventId !== undefined) { + headers.set('Last-Event-ID', lastEventId); + } + + try { + const requestInit: RequestInit = { + redirect: 'follow', + ...options, + body: options.serializedBody, + headers, + signal, + }; + let request = new Request(url, requestInit); + if (onRequest) { + request = await onRequest(url, requestInit); + } + // fetch must be assigned here, otherwise it would throw the error: + // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation + const _fetch = options.fetch ?? globalThis.fetch; + const response = await _fetch(request); + + if (!response.ok) + throw new Error( + `SSE failed: ${response.status} ${response.statusText}`, + ); + + if (!response.body) throw new Error('No body in SSE response'); + + const reader = response.body + .pipeThrough(new TextDecoderStream()) + .getReader(); + + let buffer = ''; + + const abortHandler = () => { + try { + reader.cancel(); + } catch { + // noop + } + }; + + signal.addEventListener('abort', abortHandler); + + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buffer += value; + // Normalize line endings: CRLF -> LF, then CR -> LF + buffer = buffer.replace(/\r\n/g, '\n').replace(/\r/g, '\n'); + + const chunks = buffer.split('\n\n'); + buffer = chunks.pop() ?? ''; + + for (const chunk of chunks) { + const lines = chunk.split('\n'); + const dataLines: Array = []; + let eventName: string | undefined; + + for (const line of lines) { + if (line.startsWith('data:')) { + dataLines.push(line.replace(/^data:\s*/, '')); + } else if (line.startsWith('event:')) { + eventName = line.replace(/^event:\s*/, ''); + } else if (line.startsWith('id:')) { + lastEventId = line.replace(/^id:\s*/, ''); + } else if (line.startsWith('retry:')) { + const parsed = Number.parseInt( + line.replace(/^retry:\s*/, ''), + 10, + ); + if (!Number.isNaN(parsed)) { + retryDelay = parsed; + } + } + } + + let data: unknown; + let parsedJson = false; + + if (dataLines.length) { + const rawData = dataLines.join('\n'); + try { + data = JSON.parse(rawData); + parsedJson = true; + } catch { + data = rawData; + } + } + + if (parsedJson) { + if (responseValidator) { + await responseValidator(data); + } + + if (responseTransformer) { + data = await responseTransformer(data); + } + } + + onSseEvent?.({ + data, + event: eventName, + id: lastEventId, + retry: retryDelay, + }); + + if (dataLines.length) { + yield data as any; + } + } + } + } finally { + signal.removeEventListener('abort', abortHandler); + reader.releaseLock(); + } + + break; // exit loop on normal completion + } catch (error) { + // connection failed or aborted; retry after delay + onSseError?.(error); + + if ( + sseMaxRetryAttempts !== undefined && + attempt >= sseMaxRetryAttempts + ) { + break; // stop after firing error + } + + // exponential backoff: double retry each attempt, cap at 30s + const backoff = Math.min( + retryDelay * 2 ** (attempt - 1), + sseMaxRetryDelay ?? 30000, + ); + await sleep(backoff); + } + } + }; + + const stream = createStream(); + + return { stream }; +}; diff --git a/src/clientsdk/core/types.gen.ts b/src/clientsdk/core/types.gen.ts new file mode 100644 index 0000000..643c070 --- /dev/null +++ b/src/clientsdk/core/types.gen.ts @@ -0,0 +1,118 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Auth, AuthToken } from './auth.gen'; +import type { + BodySerializer, + QuerySerializer, + QuerySerializerOptions, +} from './bodySerializer.gen'; + +export type HttpMethod = + | 'connect' + | 'delete' + | 'get' + | 'head' + | 'options' + | 'patch' + | 'post' + | 'put' + | 'trace'; + +export type Client< + RequestFn = never, + Config = unknown, + MethodFn = never, + BuildUrlFn = never, + SseFn = never, +> = { + /** + * Returns the final request URL. + */ + buildUrl: BuildUrlFn; + getConfig: () => Config; + request: RequestFn; + setConfig: (config: Config) => Config; +} & { + [K in HttpMethod]: MethodFn; +} & ([SseFn] extends [never] + ? { sse?: never } + : { sse: { [K in HttpMethod]: SseFn } }); + +export interface Config { + /** + * Auth token or a function returning auth token. The resolved value will be + * added to the request payload as defined by its `security` array. + */ + auth?: ((auth: Auth) => Promise | AuthToken) | AuthToken; + /** + * A function for serializing request body parameter. By default, + * {@link JSON.stringify()} will be used. + */ + bodySerializer?: BodySerializer | null; + /** + * An object containing any HTTP headers that you want to pre-populate your + * `Headers` object with. + * + * {@link https://developer.mozilla.org/docs/Web/API/Headers/Headers#init See more} + */ + headers?: + | RequestInit['headers'] + | Record< + string, + | string + | number + | boolean + | (string | number | boolean)[] + | null + | undefined + | unknown + >; + /** + * The request method. + * + * {@link https://developer.mozilla.org/docs/Web/API/fetch#method See more} + */ + method?: Uppercase; + /** + * A function for serializing request query parameters. By default, arrays + * will be exploded in form style, objects will be exploded in deepObject + * style, and reserved characters are percent-encoded. + * + * This method will have no effect if the native `paramsSerializer()` Axios + * API function is used. + * + * {@link https://swagger.io/docs/specification/serialization/#query View examples} + */ + querySerializer?: QuerySerializer | QuerySerializerOptions; + /** + * A function validating request data. This is useful if you want to ensure + * the request conforms to the desired shape, so it can be safely sent to + * the server. + */ + requestValidator?: (data: unknown) => Promise; + /** + * A function transforming response data before it's returned. This is useful + * for post-processing data, e.g. converting ISO strings into Date objects. + */ + responseTransformer?: (data: unknown) => Promise; + /** + * A function validating response data. This is useful if you want to ensure + * the response conforms to the desired shape, so it can be safely passed to + * the transformers and returned to the user. + */ + responseValidator?: (data: unknown) => Promise; +} + +type IsExactlyNeverOrNeverUndefined = [T] extends [never] + ? true + : [T] extends [never | undefined] + ? [undefined] extends [T] + ? false + : true + : false; + +export type OmitNever> = { + [K in keyof T as IsExactlyNeverOrNeverUndefined extends true + ? never + : K]: T[K]; +}; diff --git a/src/clientsdk/core/utils.gen.ts b/src/clientsdk/core/utils.gen.ts new file mode 100644 index 0000000..0b5389d --- /dev/null +++ b/src/clientsdk/core/utils.gen.ts @@ -0,0 +1,143 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { BodySerializer, QuerySerializer } from './bodySerializer.gen'; +import { + type ArraySeparatorStyle, + serializeArrayParam, + serializeObjectParam, + serializePrimitiveParam, +} from './pathSerializer.gen'; + +export interface PathSerializer { + path: Record; + url: string; +} + +export const PATH_PARAM_RE = /\{[^{}]+\}/g; + +export const defaultPathSerializer = ({ path, url: _url }: PathSerializer) => { + let url = _url; + const matches = _url.match(PATH_PARAM_RE); + if (matches) { + for (const match of matches) { + let explode = false; + let name = match.substring(1, match.length - 1); + let style: ArraySeparatorStyle = 'simple'; + + if (name.endsWith('*')) { + explode = true; + name = name.substring(0, name.length - 1); + } + + if (name.startsWith('.')) { + name = name.substring(1); + style = 'label'; + } else if (name.startsWith(';')) { + name = name.substring(1); + style = 'matrix'; + } + + const value = path[name]; + + if (value === undefined || value === null) { + continue; + } + + if (Array.isArray(value)) { + url = url.replace( + match, + serializeArrayParam({ explode, name, style, value }), + ); + continue; + } + + if (typeof value === 'object') { + url = url.replace( + match, + serializeObjectParam({ + explode, + name, + style, + value: value as Record, + valueOnly: true, + }), + ); + continue; + } + + if (style === 'matrix') { + url = url.replace( + match, + `;${serializePrimitiveParam({ + name, + value: value as string, + })}`, + ); + continue; + } + + const replaceValue = encodeURIComponent( + style === 'label' ? `.${value as string}` : (value as string), + ); + url = url.replace(match, replaceValue); + } + } + return url; +}; + +export const getUrl = ({ + baseUrl, + path, + query, + querySerializer, + url: _url, +}: { + baseUrl?: string; + path?: Record; + query?: Record; + querySerializer: QuerySerializer; + url: string; +}) => { + const pathUrl = _url.startsWith('/') ? _url : `/${_url}`; + let url = (baseUrl ?? '') + pathUrl; + if (path) { + url = defaultPathSerializer({ path, url }); + } + let search = query ? querySerializer(query) : ''; + if (search.startsWith('?')) { + search = search.substring(1); + } + if (search) { + url += `?${search}`; + } + return url; +}; + +export function getValidRequestBody(options: { + body?: unknown; + bodySerializer?: BodySerializer | null; + serializedBody?: unknown; +}) { + const hasBody = options.body !== undefined; + const isSerializedBody = hasBody && options.bodySerializer; + + if (isSerializedBody) { + if ('serializedBody' in options) { + const hasSerializedBody = + options.serializedBody !== undefined && options.serializedBody !== ''; + + return hasSerializedBody ? options.serializedBody : null; + } + + // not all clients implement a serializedBody property (i.e. client-axios) + return options.body !== '' ? options.body : null; + } + + // plain/text body + if (hasBody) { + return options.body; + } + + // no body was provided + return undefined; +} diff --git a/src/clientsdk/index.ts b/src/clientsdk/index.ts new file mode 100644 index 0000000..e7c8690 --- /dev/null +++ b/src/clientsdk/index.ts @@ -0,0 +1,4 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export { Categories, Folders, Forms, FormSubmissions, type Options, Pages, PayloadJobs, PayloadKvs, PayloadLockedDocuments, PayloadMigrations, PayloadPreferences, Posts, Redirects, SearchResults, Tenants, Users } from './sdk.gen'; +export { type Category, type CategoryPatchRequestBody, type CategoryQueryOperations, type CategoryQueryOperationsAnd, type CategoryQueryOperationsOr, type CategoryRequestBody, type ClientOptions, type CreateCategoryData, type CreateCategoryResponse, type CreateCategoryResponses, type CreateFolderData, type CreateFolderResponse, type CreateFolderResponses, type CreateFormData, type CreateFormResponse, type CreateFormResponses, type CreateFormSubmissionData, type CreateFormSubmissionResponse, type CreateFormSubmissionResponses, type CreateMediaData, type CreateMediaResponse, type CreateMediaResponses, type CreatePageData, type CreatePageResponse, type CreatePageResponses, type CreatePayloadJobData, type CreatePayloadJobResponse, type CreatePayloadJobResponses, type CreatePayloadKvData, type CreatePayloadKvResponse, type CreatePayloadKvResponses, type CreatePayloadLockedDocumentData, type CreatePayloadLockedDocumentResponse, type CreatePayloadLockedDocumentResponses, type CreatePayloadMigrationData, type CreatePayloadMigrationResponse, type CreatePayloadMigrationResponses, type CreatePayloadPreferenceData, type CreatePayloadPreferenceResponse, type CreatePayloadPreferenceResponses, type CreatePostData, type CreatePostResponse, type CreatePostResponses, type CreateRedirectData, type CreateRedirectResponse, type CreateRedirectResponses, type CreateSearchResultData, type CreateSearchResultResponse, type CreateSearchResultResponses, type CreateTenantData, type CreateTenantResponse, type CreateTenantResponses, type CreateUserData, type CreateUserResponse, type CreateUserResponses, type DeleteCategoryData, type DeleteCategoryErrors, type DeleteCategoryResponse, type DeleteCategoryResponses, type DeleteFolderData, type DeleteFolderErrors, type DeleteFolderResponse, type DeleteFolderResponses, type DeleteFormData, type DeleteFormErrors, type DeleteFormResponse, type DeleteFormResponses, type DeleteFormSubmissionData, type DeleteFormSubmissionErrors, type DeleteFormSubmissionResponse, type DeleteFormSubmissionResponses, type DeleteMediaData, type DeleteMediaErrors, type DeleteMediaResponse, type DeleteMediaResponses, type DeletePageData, type DeletePageErrors, type DeletePageResponse, type DeletePageResponses, type DeletePayloadJobData, type DeletePayloadJobErrors, type DeletePayloadJobResponse, type DeletePayloadJobResponses, type DeletePayloadKvData, type DeletePayloadKvErrors, type DeletePayloadKvResponse, type DeletePayloadKvResponses, type DeletePayloadLockedDocumentData, type DeletePayloadLockedDocumentErrors, type DeletePayloadLockedDocumentResponse, type DeletePayloadLockedDocumentResponses, type DeletePayloadMigrationData, type DeletePayloadMigrationErrors, type DeletePayloadMigrationResponse, type DeletePayloadMigrationResponses, type DeletePayloadPreferenceData, type DeletePayloadPreferenceErrors, type DeletePayloadPreferenceResponse, type DeletePayloadPreferenceResponses, type DeletePostData, type DeletePostErrors, type DeletePostResponse, type DeletePostResponses, type DeleteRedirectData, type DeleteRedirectErrors, type DeleteRedirectResponse, type DeleteRedirectResponses, type DeleteSearchResultData, type DeleteSearchResultErrors, type DeleteSearchResultResponse, type DeleteSearchResultResponses, type DeleteTenantData, type DeleteTenantErrors, type DeleteTenantResponse, type DeleteTenantResponses, type DeleteUserData, type DeleteUserErrors, type DeleteUserResponse, type DeleteUserResponses, type FindCategoryByIdData, type FindCategoryByIdErrors, type FindCategoryByIdResponse, type FindCategoryByIdResponses, type FindFolderByIdData, type FindFolderByIdErrors, type FindFolderByIdResponse, type FindFolderByIdResponses, type FindFormByIdData, type FindFormByIdErrors, type FindFormByIdResponse, type FindFormByIdResponses, type FindFormSubmissionByIdData, type FindFormSubmissionByIdErrors, type FindFormSubmissionByIdResponse, type FindFormSubmissionByIdResponses, type FindMediaByIdData, type FindMediaByIdErrors, type FindMediaByIdResponse, type FindMediaByIdResponses, type FindPageByIdData, type FindPageByIdErrors, type FindPageByIdResponse, type FindPageByIdResponses, type FindPayloadJobByIdData, type FindPayloadJobByIdErrors, type FindPayloadJobByIdResponse, type FindPayloadJobByIdResponses, type FindPayloadKvByIdData, type FindPayloadKvByIdErrors, type FindPayloadKvByIdResponse, type FindPayloadKvByIdResponses, type FindPayloadLockedDocumentByIdData, type FindPayloadLockedDocumentByIdErrors, type FindPayloadLockedDocumentByIdResponse, type FindPayloadLockedDocumentByIdResponses, type FindPayloadMigrationByIdData, type FindPayloadMigrationByIdErrors, type FindPayloadMigrationByIdResponse, type FindPayloadMigrationByIdResponses, type FindPayloadPreferenceByIdData, type FindPayloadPreferenceByIdErrors, type FindPayloadPreferenceByIdResponse, type FindPayloadPreferenceByIdResponses, type FindPostByIdData, type FindPostByIdErrors, type FindPostByIdResponse, type FindPostByIdResponses, type FindRedirectByIdData, type FindRedirectByIdErrors, type FindRedirectByIdResponse, type FindRedirectByIdResponses, type FindSearchResultByIdData, type FindSearchResultByIdErrors, type FindSearchResultByIdResponse, type FindSearchResultByIdResponses, type FindTenantByIdData, type FindTenantByIdErrors, type FindTenantByIdResponse, type FindTenantByIdResponses, type FindUserByIdData, type FindUserByIdErrors, type FindUserByIdResponse, type FindUserByIdResponses, type Folder, type FolderPatchRequestBody, type FolderQueryOperations, type FolderQueryOperationsAnd, type FolderQueryOperationsOr, type FolderRequestBody, Footer, type FooterRead, type FooterRequestBody, type FooterWrite, type Form, type FormPatchRequestBody, type FormQueryOperations, type FormQueryOperationsAnd, type FormQueryOperationsOr, type FormRequestBody, type FormSubmission, type FormSubmissionPatchRequestBody, type FormSubmissionQueryOperations, type FormSubmissionQueryOperationsAnd, type FormSubmissionQueryOperationsOr, type FormSubmissionRequestBody, type GetApiGlobalsFooterData, type GetApiGlobalsFooterResponse, type GetApiGlobalsFooterResponses, type GetApiGlobalsHeaderData, type GetApiGlobalsHeaderResponse, type GetApiGlobalsHeaderResponses, Header, type HeaderRead, type HeaderRequestBody, type HeaderWrite, type ListCategoriesData, type ListCategoriesResponse, type ListCategoriesResponses, type ListFoldersData, type ListFoldersResponse, type ListFoldersResponses, type ListFormsData, type ListFormsResponse, type ListFormsResponses, type ListFormSubmissionsData, type ListFormSubmissionsResponse, type ListFormSubmissionsResponses, type ListMediaData, type ListMediaResponse, type ListMediaResponses, type ListPagesData, type ListPagesResponse, type ListPagesResponses, type ListPayloadJobsData, type ListPayloadJobsResponse, type ListPayloadJobsResponses, type ListPayloadKvsData, type ListPayloadKvsResponse, type ListPayloadKvsResponses, type ListPayloadLockedDocumentsData, type ListPayloadLockedDocumentsResponse, type ListPayloadLockedDocumentsResponses, type ListPayloadMigrationsData, type ListPayloadMigrationsResponse, type ListPayloadMigrationsResponses, type ListPayloadPreferencesData, type ListPayloadPreferencesResponse, type ListPayloadPreferencesResponses, type ListPostsData, type ListPostsResponse, type ListPostsResponses, type ListRedirectsData, type ListRedirectsResponse, type ListRedirectsResponses, type ListSearchResultsData, type ListSearchResultsResponse, type ListSearchResultsResponses, type ListTenantsData, type ListTenantsResponse, type ListTenantsResponses, type ListUsersData, type ListUsersResponse, type ListUsersResponses, Media, type MediaPatchRequestBody, type MediaQueryOperations, type MediaQueryOperationsAnd, type MediaQueryOperationsOr, type MediaRequestBody, type Page, type PagePatchRequestBody, type PageQueryOperations, type PageQueryOperationsAnd, type PageQueryOperationsOr, type PageRequestBody, type PayloadJob, type PayloadJobPatchRequestBody, type PayloadJobQueryOperations, type PayloadJobQueryOperationsAnd, type PayloadJobQueryOperationsOr, type PayloadJobRequestBody, type PayloadKv, type PayloadKvPatchRequestBody, type PayloadKvQueryOperations, type PayloadKvQueryOperationsAnd, type PayloadKvQueryOperationsOr, type PayloadKvRequestBody, type PayloadLockedDocument, type PayloadLockedDocumentPatchRequestBody, type PayloadLockedDocumentQueryOperations, type PayloadLockedDocumentQueryOperationsAnd, type PayloadLockedDocumentQueryOperationsOr, type PayloadLockedDocumentRequestBody, type PayloadMigration, type PayloadMigrationPatchRequestBody, type PayloadMigrationQueryOperations, type PayloadMigrationQueryOperationsAnd, type PayloadMigrationQueryOperationsOr, type PayloadMigrationRequestBody, type PayloadPreference, type PayloadPreferencePatchRequestBody, type PayloadPreferenceQueryOperations, type PayloadPreferenceQueryOperationsAnd, type PayloadPreferenceQueryOperationsOr, type PayloadPreferenceRequestBody, type Post, type PostApiGlobalsFooterData, type PostApiGlobalsFooterResponse, type PostApiGlobalsFooterResponses, type PostApiGlobalsHeaderData, type PostApiGlobalsHeaderResponse, type PostApiGlobalsHeaderResponses, type PostPatchRequestBody, type PostQueryOperations, type PostQueryOperationsAnd, type PostQueryOperationsOr, type PostRequestBody, type Redirect, type RedirectPatchRequestBody, type RedirectQueryOperations, type RedirectQueryOperationsAnd, type RedirectQueryOperationsOr, type RedirectRequestBody, type SearchResult, type SearchResultPatchRequestBody, type SearchResultQueryOperations, type SearchResultQueryOperationsAnd, type SearchResultQueryOperationsOr, type SearchResultRequestBody, type SupportedTimezones, type Tenant, type TenantPatchRequestBody, type TenantQueryOperations, type TenantQueryOperationsAnd, type TenantQueryOperationsOr, type TenantRequestBody, type UpdateCategoryData, type UpdateCategoryErrors, type UpdateCategoryResponse, type UpdateCategoryResponses, type UpdateFolderData, type UpdateFolderErrors, type UpdateFolderResponse, type UpdateFolderResponses, type UpdateFormData, type UpdateFormErrors, type UpdateFormResponse, type UpdateFormResponses, type UpdateFormSubmissionData, type UpdateFormSubmissionErrors, type UpdateFormSubmissionResponse, type UpdateFormSubmissionResponses, type UpdateMediaData, type UpdateMediaErrors, type UpdateMediaResponse, type UpdateMediaResponses, type UpdatePageData, type UpdatePageErrors, type UpdatePageResponse, type UpdatePageResponses, type UpdatePayloadJobData, type UpdatePayloadJobErrors, type UpdatePayloadJobResponse, type UpdatePayloadJobResponses, type UpdatePayloadKvData, type UpdatePayloadKvErrors, type UpdatePayloadKvResponse, type UpdatePayloadKvResponses, type UpdatePayloadLockedDocumentData, type UpdatePayloadLockedDocumentErrors, type UpdatePayloadLockedDocumentResponse, type UpdatePayloadLockedDocumentResponses, type UpdatePayloadMigrationData, type UpdatePayloadMigrationErrors, type UpdatePayloadMigrationResponse, type UpdatePayloadMigrationResponses, type UpdatePayloadPreferenceData, type UpdatePayloadPreferenceErrors, type UpdatePayloadPreferenceResponse, type UpdatePayloadPreferenceResponses, type UpdatePostData, type UpdatePostErrors, type UpdatePostResponse, type UpdatePostResponses, type UpdateRedirectData, type UpdateRedirectErrors, type UpdateRedirectResponse, type UpdateRedirectResponses, type UpdateSearchResultData, type UpdateSearchResultErrors, type UpdateSearchResultResponse, type UpdateSearchResultResponses, type UpdateTenantData, type UpdateTenantErrors, type UpdateTenantResponse, type UpdateTenantResponses, type UpdateUserData, type UpdateUserErrors, type UpdateUserResponse, type UpdateUserResponses, type User, type UserPatchRequestBody, type UserQueryOperations, type UserQueryOperationsAnd, type UserQueryOperationsOr, type UserRequestBody } from './types.gen'; diff --git a/src/clientsdk/querySerializer.ts b/src/clientsdk/querySerializer.ts new file mode 100644 index 0000000..b938868 --- /dev/null +++ b/src/clientsdk/querySerializer.ts @@ -0,0 +1,28 @@ +export const customQuerySerializer = (queryParams: any) => { + const search: string[] = []; + + const serialize = (name: string, value: any) => { + if (value === undefined || value === null) return; + + if (Array.isArray(value)) { + value.forEach((v, i) => { + serialize(`${name}[${i}]`, v); + }); + } else if (typeof value === 'object' && value !== null && !(value instanceof Date)) { + Object.entries(value).forEach(([key, v]) => { + serialize(`${name}[${key}]`, v); + }); + } else { + const val = value instanceof Date ? value.toISOString() : String(value); + search.push(`${encodeURIComponent(name)}=${encodeURIComponent(val)}`); + } + }; + + if (queryParams && typeof queryParams === 'object') { + for (const key in queryParams) { + serialize(key, queryParams[key]); + } + } + + return search.join('&'); +}; diff --git a/src/clientsdk/sdk.gen.ts b/src/clientsdk/sdk.gen.ts new file mode 100644 index 0000000..d3d199f --- /dev/null +++ b/src/clientsdk/sdk.gen.ts @@ -0,0 +1,1098 @@ +// This file is auto-generated by @hey-api/openapi-ts + +import type { Client, Options as Options2, TDataShape } from './client'; +import { client } from './client.gen'; +import type { CreateCategoryData, CreateCategoryResponses, CreateFolderData, CreateFolderResponses, CreateFormData, CreateFormResponses, CreateFormSubmissionData, CreateFormSubmissionResponses, CreateMediaData, CreateMediaResponses, CreatePageData, CreatePageResponses, CreatePayloadJobData, CreatePayloadJobResponses, CreatePayloadKvData, CreatePayloadKvResponses, CreatePayloadLockedDocumentData, CreatePayloadLockedDocumentResponses, CreatePayloadMigrationData, CreatePayloadMigrationResponses, CreatePayloadPreferenceData, CreatePayloadPreferenceResponses, CreatePostData, CreatePostResponses, CreateRedirectData, CreateRedirectResponses, CreateSearchResultData, CreateSearchResultResponses, CreateTenantData, CreateTenantResponses, CreateUserData, CreateUserResponses, DeleteCategoryData, DeleteCategoryErrors, DeleteCategoryResponses, DeleteFolderData, DeleteFolderErrors, DeleteFolderResponses, DeleteFormData, DeleteFormErrors, DeleteFormResponses, DeleteFormSubmissionData, DeleteFormSubmissionErrors, DeleteFormSubmissionResponses, DeleteMediaData, DeleteMediaErrors, DeleteMediaResponses, DeletePageData, DeletePageErrors, DeletePageResponses, DeletePayloadJobData, DeletePayloadJobErrors, DeletePayloadJobResponses, DeletePayloadKvData, DeletePayloadKvErrors, DeletePayloadKvResponses, DeletePayloadLockedDocumentData, DeletePayloadLockedDocumentErrors, DeletePayloadLockedDocumentResponses, DeletePayloadMigrationData, DeletePayloadMigrationErrors, DeletePayloadMigrationResponses, DeletePayloadPreferenceData, DeletePayloadPreferenceErrors, DeletePayloadPreferenceResponses, DeletePostData, DeletePostErrors, DeletePostResponses, DeleteRedirectData, DeleteRedirectErrors, DeleteRedirectResponses, DeleteSearchResultData, DeleteSearchResultErrors, DeleteSearchResultResponses, DeleteTenantData, DeleteTenantErrors, DeleteTenantResponses, DeleteUserData, DeleteUserErrors, DeleteUserResponses, FindCategoryByIdData, FindCategoryByIdErrors, FindCategoryByIdResponses, FindFolderByIdData, FindFolderByIdErrors, FindFolderByIdResponses, FindFormByIdData, FindFormByIdErrors, FindFormByIdResponses, FindFormSubmissionByIdData, FindFormSubmissionByIdErrors, FindFormSubmissionByIdResponses, FindMediaByIdData, FindMediaByIdErrors, FindMediaByIdResponses, FindPageByIdData, FindPageByIdErrors, FindPageByIdResponses, FindPayloadJobByIdData, FindPayloadJobByIdErrors, FindPayloadJobByIdResponses, FindPayloadKvByIdData, FindPayloadKvByIdErrors, FindPayloadKvByIdResponses, FindPayloadLockedDocumentByIdData, FindPayloadLockedDocumentByIdErrors, FindPayloadLockedDocumentByIdResponses, FindPayloadMigrationByIdData, FindPayloadMigrationByIdErrors, FindPayloadMigrationByIdResponses, FindPayloadPreferenceByIdData, FindPayloadPreferenceByIdErrors, FindPayloadPreferenceByIdResponses, FindPostByIdData, FindPostByIdErrors, FindPostByIdResponses, FindRedirectByIdData, FindRedirectByIdErrors, FindRedirectByIdResponses, FindSearchResultByIdData, FindSearchResultByIdErrors, FindSearchResultByIdResponses, FindTenantByIdData, FindTenantByIdErrors, FindTenantByIdResponses, FindUserByIdData, FindUserByIdErrors, FindUserByIdResponses, GetApiGlobalsFooterData, GetApiGlobalsFooterResponses, GetApiGlobalsHeaderData, GetApiGlobalsHeaderResponses, ListCategoriesData, ListCategoriesResponses, ListFoldersData, ListFoldersResponses, ListFormsData, ListFormsResponses, ListFormSubmissionsData, ListFormSubmissionsResponses, ListMediaData, ListMediaResponses, ListPagesData, ListPagesResponses, ListPayloadJobsData, ListPayloadJobsResponses, ListPayloadKvsData, ListPayloadKvsResponses, ListPayloadLockedDocumentsData, ListPayloadLockedDocumentsResponses, ListPayloadMigrationsData, ListPayloadMigrationsResponses, ListPayloadPreferencesData, ListPayloadPreferencesResponses, ListPostsData, ListPostsResponses, ListRedirectsData, ListRedirectsResponses, ListSearchResultsData, ListSearchResultsResponses, ListTenantsData, ListTenantsResponses, ListUsersData, ListUsersResponses, PostApiGlobalsFooterData, PostApiGlobalsFooterResponses, PostApiGlobalsHeaderData, PostApiGlobalsHeaderResponses, UpdateCategoryData, UpdateCategoryErrors, UpdateCategoryResponses, UpdateFolderData, UpdateFolderErrors, UpdateFolderResponses, UpdateFormData, UpdateFormErrors, UpdateFormResponses, UpdateFormSubmissionData, UpdateFormSubmissionErrors, UpdateFormSubmissionResponses, UpdateMediaData, UpdateMediaErrors, UpdateMediaResponses, UpdatePageData, UpdatePageErrors, UpdatePageResponses, UpdatePayloadJobData, UpdatePayloadJobErrors, UpdatePayloadJobResponses, UpdatePayloadKvData, UpdatePayloadKvErrors, UpdatePayloadKvResponses, UpdatePayloadLockedDocumentData, UpdatePayloadLockedDocumentErrors, UpdatePayloadLockedDocumentResponses, UpdatePayloadMigrationData, UpdatePayloadMigrationErrors, UpdatePayloadMigrationResponses, UpdatePayloadPreferenceData, UpdatePayloadPreferenceErrors, UpdatePayloadPreferenceResponses, UpdatePostData, UpdatePostErrors, UpdatePostResponses, UpdateRedirectData, UpdateRedirectErrors, UpdateRedirectResponses, UpdateSearchResultData, UpdateSearchResultErrors, UpdateSearchResultResponses, UpdateTenantData, UpdateTenantErrors, UpdateTenantResponses, UpdateUserData, UpdateUserErrors, UpdateUserResponses } from './types.gen'; + +export type Options = Options2 & { + /** + * You can provide a client instance returned by `createClient()` instead of + * individual options. This might be also useful if you want to implement a + * custom client. + */ + client?: Client; + /** + * You can pass arbitrary values through the `meta` object. This can be + * used to access values that aren't defined as part of the SDK function. + */ + meta?: Record; +}; + +export class Pages { + /** + * Retrieve a list of Pages + */ + public static listPages(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/pages', + ...options + }); + } + + /** + * Create a new Page + */ + public static createPage(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/pages', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Page + */ + public static deletePage(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/pages/{id}', + ...options + }); + } + + /** + * Find a Page by ID + */ + public static findPageById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/pages/{id}', + ...options + }); + } + + /** + * Update a Page + */ + public static updatePage(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/pages/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Posts { + /** + * Retrieve a list of Posts + */ + public static listPosts(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/posts', + ...options + }); + } + + /** + * Create a new Post + */ + public static createPost(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/posts', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Post + */ + public static deletePost(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/posts/{id}', + ...options + }); + } + + /** + * Find a Post by ID + */ + public static findPostById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/posts/{id}', + ...options + }); + } + + /** + * Update a Post + */ + public static updatePost(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/posts/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Media { + /** + * Retrieve a list of Media + */ + public static listMedia(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/media', + ...options + }); + } + + /** + * Create a new Media + */ + public static createMedia(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/media', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Media + */ + public static deleteMedia(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/media/{id}', + ...options + }); + } + + /** + * Find a Media by ID + */ + public static findMediaById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/media/{id}', + ...options + }); + } + + /** + * Update a Media + */ + public static updateMedia(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/media/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Categories { + /** + * Retrieve a list of Categories + */ + public static listCategories(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/categories', + ...options + }); + } + + /** + * Create a new Category + */ + public static createCategory(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/categories', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Category + */ + public static deleteCategory(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/categories/{id}', + ...options + }); + } + + /** + * Find a Category by ID + */ + public static findCategoryById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/categories/{id}', + ...options + }); + } + + /** + * Update a Category + */ + public static updateCategory(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/categories/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Users { + /** + * Retrieve a list of Users + */ + public static listUsers(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/users', + ...options + }); + } + + /** + * Create a new User + */ + public static createUser(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/users', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a User + */ + public static deleteUser(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/users/{id}', + ...options + }); + } + + /** + * Find a User by ID + */ + public static findUserById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/users/{id}', + ...options + }); + } + + /** + * Update a User + */ + public static updateUser(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/users/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Tenants { + /** + * Retrieve a list of Tenants + */ + public static listTenants(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/tenants', + ...options + }); + } + + /** + * Create a new Tenant + */ + public static createTenant(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/tenants', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Tenant + */ + public static deleteTenant(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/tenants/{id}', + ...options + }); + } + + /** + * Find a Tenant by ID + */ + public static findTenantById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/tenants/{id}', + ...options + }); + } + + /** + * Update a Tenant + */ + public static updateTenant(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/tenants/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Redirects { + /** + * Retrieve a list of Redirects + */ + public static listRedirects(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/redirects', + ...options + }); + } + + /** + * Create a new Redirect + */ + public static createRedirect(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/redirects', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Redirect + */ + public static deleteRedirect(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/redirects/{id}', + ...options + }); + } + + /** + * Find a Redirect by ID + */ + public static findRedirectById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/redirects/{id}', + ...options + }); + } + + /** + * Update a Redirect + */ + public static updateRedirect(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/redirects/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Forms { + /** + * Retrieve a list of Forms + */ + public static listForms(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/forms', + ...options + }); + } + + /** + * Create a new Form + */ + public static createForm(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/forms', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Form + */ + public static deleteForm(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/forms/{id}', + ...options + }); + } + + /** + * Find a Form by ID + */ + public static findFormById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/forms/{id}', + ...options + }); + } + + /** + * Update a Form + */ + public static updateForm(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/forms/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class FormSubmissions { + /** + * Retrieve a list of Form Submissions + */ + public static listFormSubmissions(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/form-submissions', + ...options + }); + } + + /** + * Create a new Form Submission + */ + public static createFormSubmission(options?: Options) { + return (options?.client ?? client).post({ + url: '/api/form-submissions', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Form Submission + */ + public static deleteFormSubmission(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/form-submissions/{id}', + ...options + }); + } + + /** + * Find a Form Submission by ID + */ + public static findFormSubmissionById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/form-submissions/{id}', + ...options + }); + } + + /** + * Update a Form Submission + */ + public static updateFormSubmission(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/form-submissions/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class SearchResults { + /** + * Retrieve a list of Search Results + */ + public static listSearchResults(options?: Options) { + return (options?.client ?? client).get({ url: '/api/search', ...options }); + } + + /** + * Create a new Search Result + */ + public static createSearchResult(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/search', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Search Result + */ + public static deleteSearchResult(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/search/{id}', + ...options + }); + } + + /** + * Find a Search Result by ID + */ + public static findSearchResultById(options: Options) { + return (options.client ?? client).get({ url: '/api/search/{id}', ...options }); + } + + /** + * Update a Search Result + */ + public static updateSearchResult(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/search/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class PayloadKvs { + /** + * Retrieve a list of Payload Kvs + */ + public static listPayloadKvs(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-kv', + ...options + }); + } + + /** + * Create a new Payload Kv + */ + public static createPayloadKv(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-kv', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Payload Kv + */ + public static deletePayloadKv(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-kv/{id}', + ...options + }); + } + + /** + * Find a Payload Kv by ID + */ + public static findPayloadKvById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-kv/{id}', + ...options + }); + } + + /** + * Update a Payload Kv + */ + public static updatePayloadKv(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-kv/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class PayloadJobs { + /** + * Retrieve a list of Payload Jobs + */ + public static listPayloadJobs(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-jobs', + ...options + }); + } + + /** + * Create a new Payload Job + */ + public static createPayloadJob(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-jobs', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Payload Job + */ + public static deletePayloadJob(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-jobs/{id}', + ...options + }); + } + + /** + * Find a Payload Job by ID + */ + public static findPayloadJobById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-jobs/{id}', + ...options + }); + } + + /** + * Update a Payload Job + */ + public static updatePayloadJob(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-jobs/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Folders { + /** + * Retrieve a list of Folders + */ + public static listFolders(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-folders', + ...options + }); + } + + /** + * Create a new Folder + */ + public static createFolder(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-folders', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Folder + */ + public static deleteFolder(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-folders/{id}', + ...options + }); + } + + /** + * Find a Folder by ID + */ + public static findFolderById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-folders/{id}', + ...options + }); + } + + /** + * Update a Folder + */ + public static updateFolder(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-folders/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class PayloadLockedDocuments { + /** + * Retrieve a list of Payload Locked Documents + */ + public static listPayloadLockedDocuments(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-locked-documents', + ...options + }); + } + + /** + * Create a new Payload Locked Document + */ + public static createPayloadLockedDocument(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-locked-documents', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Payload Locked Document + */ + public static deletePayloadLockedDocument(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-locked-documents/{id}', + ...options + }); + } + + /** + * Find a Payload Locked Document by ID + */ + public static findPayloadLockedDocumentById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-locked-documents/{id}', + ...options + }); + } + + /** + * Update a Payload Locked Document + */ + public static updatePayloadLockedDocument(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-locked-documents/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class PayloadPreferences { + /** + * Retrieve a list of Payload Preferences + */ + public static listPayloadPreferences(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-preferences', + ...options + }); + } + + /** + * Create a new Payload Preference + */ + public static createPayloadPreference(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-preferences', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Payload Preference + */ + public static deletePayloadPreference(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-preferences/{id}', + ...options + }); + } + + /** + * Find a Payload Preference by ID + */ + public static findPayloadPreferenceById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-preferences/{id}', + ...options + }); + } + + /** + * Update a Payload Preference + */ + public static updatePayloadPreference(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-preferences/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class PayloadMigrations { + /** + * Retrieve a list of Payload Migrations + */ + public static listPayloadMigrations(options?: Options) { + return (options?.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-migrations', + ...options + }); + } + + /** + * Create a new Payload Migration + */ + public static createPayloadMigration(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-migrations', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } + + /** + * Delete a Payload Migration + */ + public static deletePayloadMigration(options: Options) { + return (options.client ?? client).delete({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-migrations/{id}', + ...options + }); + } + + /** + * Find a Payload Migration by ID + */ + public static findPayloadMigrationById(options: Options) { + return (options.client ?? client).get({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-migrations/{id}', + ...options + }); + } + + /** + * Update a Payload Migration + */ + public static updatePayloadMigration(options: Options) { + return (options.client ?? client).patch({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/payload-migrations/{id}', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options.headers + } + }); + } +} + +export class Header { + /** + * Get the Header + */ + public static getApiGlobalsHeader(options?: Options) { + return (options?.client ?? client).get({ url: '/api/globals/header', ...options }); + } + + /** + * Update the Header + */ + public static postApiGlobalsHeader(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/globals/header', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } +} + +export class Footer { + /** + * Get the Footer + */ + public static getApiGlobalsFooter(options?: Options) { + return (options?.client ?? client).get({ url: '/api/globals/footer', ...options }); + } + + /** + * Update the Footer + */ + public static postApiGlobalsFooter(options?: Options) { + return (options?.client ?? client).post({ + security: [{ scheme: 'bearer', type: 'http' }], + url: '/api/globals/footer', + ...options, + headers: { + 'Content-Type': 'application/json', + ...options?.headers + } + }); + } +} diff --git a/src/clientsdk/types.gen.ts b/src/clientsdk/types.gen.ts new file mode 100644 index 0000000..b87491f --- /dev/null +++ b/src/clientsdk/types.gen.ts @@ -0,0 +1,6179 @@ +// This file is auto-generated by @hey-api/openapi-ts + +export type ClientOptions = { + baseUrl: 'http://localhost:3000' | (string & {}); +}; + +export type SupportedTimezones = string; + +/** + * Page + */ +export type Page = { + id: string; + tenant?: string | null | Tenant; + title: string; + hero: { + type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + media?: string | null | Media; + }; + layout: Array<{ + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'cta'; + } | { + columns?: Array<{ + size?: 'oneThird' | 'half' | 'twoThirds' | 'full'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + enableLink?: boolean | null; + link?: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'content'; + } | { + media: string | Media; + id?: string | null; + blockName?: string | null; + blockType: 'mediaBlock'; + } | { + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + populateBy?: 'collection' | 'selection'; + relationTo?: 'posts'; + categories?: Array | null; + limit?: number | null; + selectedDocs?: Array<{ + relationTo: 'posts'; + value: string | Post; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'archive'; + } | { + form: string | Form; + enableIntro?: boolean | null; + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'formBlock'; + }>; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + updatedAt: string; + createdAt: string; + _status?: 'draft' | 'published'; +}; + +/** + * Post + */ +export type Post = { + id: string; + tenant?: string | null | Tenant; + title: string; + heroImage?: string | null | Media; + content: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + }; + content_html?: string | null; + relatedPosts?: Array | null; + categories?: Array | null; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + authors?: Array | null; + populatedAuthors?: Array<{ + id?: string | null; + name?: string | null; + }> | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + updatedAt: string; + createdAt: string; + _status?: 'draft' | 'published'; +}; + +/** + * Media + */ +export type Media = { + id: string; + tenant?: string | null | Tenant; + alt?: string | null; + caption?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + folder?: string | null | Folder; + updatedAt: string; + createdAt: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; + sizes?: { + thumbnail?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + square?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + small?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + medium?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + large?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + xlarge?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + og?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + }; +}; + +/** + * Category + */ +export type Category = { + id: string; + tenant?: string | null | Tenant; + title: string; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + parent?: string | null | Category; + breadcrumbs?: Array<{ + doc?: string | null | Category; + url?: string | null; + label?: string | null; + id?: string | null; + }> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * User + */ +export type User = { + id: string; + email: string; + emailVerified?: string | null; + name?: string | null; + image?: string | null; + displayName?: string | null; + tenants?: Array<{ + tenant: string | Tenant; + id?: string | null; + }> | null; + accounts?: Array<{ + provider: string; + providerAccountId: string; + type: 'oidc' | 'oauth' | 'email' | 'webauthn'; + id?: string | null; + }> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Tenant + * + * 租户基本信息管理 + */ +export type Tenant = { + id: string; + /** + * 租户显示名称 + */ + name?: string | null; + /** + * 租户标识符(用于 API 认证时的 X-Tenant-Slug 请求头) + */ + slug: string; + apiKeyEncrypted?: string | null; + /** + * 这是该租户的主 API Key。 + */ + apiKeyDisplay?: string | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Redirect + */ +export type Redirect = { + id: string; + tenant?: string | null | Tenant; + /** + * You will need to rebuild the website when changing this field. + */ + from: string; + to?: { + type?: 'reference' | 'custom'; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + }; + updatedAt: string; + createdAt: string; +}; + +/** + * Form + */ +export type Form = { + id: string; + tenant?: string | null | Tenant; + title: string; + fields?: Array<{ + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + defaultValue?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'checkbox'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'country'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'email'; + } | { + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'message'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'number'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + placeholder?: string | null; + options?: Array<{ + label: string; + value: string; + id?: string | null; + }> | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'select'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'state'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'text'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'textarea'; + }> | null; + submitButtonLabel?: string | null; + /** + * Choose whether to display an on-page message or redirect to a different page after they submit the form. + */ + confirmationType?: 'message' | 'redirect'; + confirmationMessage?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + redirect?: { + url: string; + }; + /** + * Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email. + */ + emails?: Array<{ + emailTo?: string | null; + cc?: string | null; + bcc?: string | null; + replyTo?: string | null; + emailFrom?: string | null; + subject: string; + /** + * Enter the message that should be sent in this email. + */ + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + }> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Form Submission + */ +export type FormSubmission = { + id: string; + form: string | Form; + submissionData?: Array<{ + field: string; + value: string; + id?: string | null; + }> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Search Result + * + * This is a collection of automatically created search results. These results are used by the global site search and will be updated automatically as documents in the CMS are created or updated. + */ +export type SearchResult = { + id: string; + title?: string | null; + priority?: number | null; + doc: { + relationTo: 'posts'; + value: string | Post; + }; + slug?: string | null; + meta?: { + title?: string | null; + description?: string | null; + image?: string | null | Media; + }; + categories?: Array<{ + relationTo?: string | null; + categoryID?: string | null; + title?: string | null; + id?: string | null; + }> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Payload Kv + */ +export type PayloadKv = { + id: string; + key: string; + data: { + [key: string]: unknown; + } | Array | string | number | boolean | null; +}; + +/** + * Payload Job + */ +export type PayloadJob = { + id: string; + /** + * Input data provided to the job + */ + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + taskStatus?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + completedAt?: string | null; + totalTried?: number | null; + /** + * If hasError is true this job will not be retried + */ + hasError?: boolean | null; + /** + * If hasError is true, this is the error that caused it + */ + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + /** + * Task execution log + */ + log?: Array<{ + executedAt: string; + completedAt: string; + taskSlug: 'inline' | 'schedulePublish'; + taskID: string; + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + output?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + state: 'failed' | 'succeeded'; + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + id?: string | null; + }> | null; + taskSlug?: 'inline' | 'schedulePublish'; + queue?: string | null; + waitUntil?: string | null; + processing?: boolean | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Folder + */ +export type Folder = { + id: string; + name: string; + folder?: string | null | Folder; + documentsAndFolders?: { + docs?: Array<{ + relationTo?: 'payload-folders'; + value: string | Folder; + } | { + relationTo?: 'media'; + value: string | Media; + }>; + hasNextPage?: boolean; + totalDocs?: number; + }; + folderType?: Array<'media'> | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Payload Locked Document + */ +export type PayloadLockedDocument = { + id: string; + document?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + } | { + relationTo: 'media'; + value: string | Media; + } | { + relationTo: 'categories'; + value: string | Category; + } | { + relationTo: 'users'; + value: string | User; + } | { + relationTo: 'tenants'; + value: string | Tenant; + } | { + relationTo: 'redirects'; + value: string | Redirect; + } | { + relationTo: 'forms'; + value: string | Form; + } | { + relationTo: 'form-submissions'; + value: string | FormSubmission; + } | { + relationTo: 'search'; + value: string | SearchResult; + } | { + relationTo: 'payload-folders'; + value: string | Folder; + }; + globalSlug?: string | null; + user: { + relationTo: 'users'; + value: string | User; + }; + updatedAt: string; + createdAt: string; +}; + +/** + * Payload Preference + */ +export type PayloadPreference = { + id: string; + user: { + relationTo: 'users'; + value: string | User; + }; + key?: string | null; + value?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Payload Migration + */ +export type PayloadMigration = { + id: string; + name?: string | null; + batch?: number | null; + updatedAt: string; + createdAt: string; +}; + +/** + * Page query operations + */ +export type PageQueryOperations = { + title?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + publishedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + _status?: { + equals?: 'draft' | 'published'; + not_equals?: 'draft' | 'published'; + in?: string; + not_in?: string; + }; +}; + +/** + * Page query conjunction + */ +export type PageQueryOperationsAnd = { + and: Array; +}; + +/** + * Page query disjunction + */ +export type PageQueryOperationsOr = { + or: Array; +}; + +/** + * Post query operations + */ +export type PostQueryOperations = { + title?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + publishedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + _status?: { + equals?: 'draft' | 'published'; + not_equals?: 'draft' | 'published'; + in?: string; + not_in?: string; + }; +}; + +/** + * Post query conjunction + */ +export type PostQueryOperationsAnd = { + and: Array; +}; + +/** + * Post query disjunction + */ +export type PostQueryOperationsOr = { + or: Array; +}; + +/** + * Media query operations + */ +export type MediaQueryOperations = { + alt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + url?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + thumbnailURL?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + filename?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + mimeType?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + filesize?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + width?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + height?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + focalX?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + focalY?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; +}; + +/** + * Media query conjunction + */ +export type MediaQueryOperationsAnd = { + and: Array; +}; + +/** + * Media query disjunction + */ +export type MediaQueryOperationsOr = { + or: Array; +}; + +/** + * Category query operations + */ +export type CategoryQueryOperations = { + title?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Category query conjunction + */ +export type CategoryQueryOperationsAnd = { + and: Array; +}; + +/** + * Category query disjunction + */ +export type CategoryQueryOperationsOr = { + or: Array; +}; + +/** + * User query operations + */ +export type UserQueryOperations = { + id?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * User query conjunction + */ +export type UserQueryOperationsAnd = { + and: Array; +}; + +/** + * User query disjunction + */ +export type UserQueryOperationsOr = { + or: Array; +}; + +/** + * Tenant query operations + */ +export type TenantQueryOperations = { + name?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + slug?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Tenant query conjunction + */ +export type TenantQueryOperationsAnd = { + and: Array; +}; + +/** + * Tenant query disjunction + */ +export type TenantQueryOperationsOr = { + or: Array; +}; + +/** + * Redirect query operations + */ +export type RedirectQueryOperations = { + from?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Redirect query conjunction + */ +export type RedirectQueryOperationsAnd = { + and: Array; +}; + +/** + * Redirect query disjunction + */ +export type RedirectQueryOperationsOr = { + or: Array; +}; + +/** + * Form query operations + */ +export type FormQueryOperations = { + title?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + submitButtonLabel?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + confirmationType?: { + equals?: 'message' | 'redirect'; + not_equals?: 'message' | 'redirect'; + in?: string; + not_in?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Form query conjunction + */ +export type FormQueryOperationsAnd = { + and: Array; +}; + +/** + * Form query disjunction + */ +export type FormQueryOperationsOr = { + or: Array; +}; + +/** + * Form Submission query operations + */ +export type FormSubmissionQueryOperations = { + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Form Submission query conjunction + */ +export type FormSubmissionQueryOperationsAnd = { + and: Array; +}; + +/** + * Form Submission query disjunction + */ +export type FormSubmissionQueryOperationsOr = { + or: Array; +}; + +/** + * Search Result query operations + */ +export type SearchResultQueryOperations = { + title?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + priority?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + slug?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Search Result query conjunction + */ +export type SearchResultQueryOperationsAnd = { + and: Array; +}; + +/** + * Search Result query disjunction + */ +export type SearchResultQueryOperationsOr = { + or: Array; +}; + +/** + * Payload Kv query operations + */ +export type PayloadKvQueryOperations = { + key?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; +}; + +/** + * Payload Kv query conjunction + */ +export type PayloadKvQueryOperationsAnd = { + and: Array; +}; + +/** + * Payload Kv query disjunction + */ +export type PayloadKvQueryOperationsOr = { + or: Array; +}; + +/** + * Payload Job query operations + */ +export type PayloadJobQueryOperations = { + taskSlug?: { + equals?: 'inline' | 'schedulePublish'; + not_equals?: 'inline' | 'schedulePublish'; + in?: string; + not_in?: string; + }; + queue?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + waitUntil?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + processing?: { + equals?: boolean; + not_equals?: boolean; + in?: string; + not_in?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Payload Job query conjunction + */ +export type PayloadJobQueryOperationsAnd = { + and: Array; +}; + +/** + * Payload Job query disjunction + */ +export type PayloadJobQueryOperationsOr = { + or: Array; +}; + +/** + * Folder query operations + */ +export type FolderQueryOperations = { + name?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + folderType?: { + equals?: 'media'; + not_equals?: 'media'; + in?: string; + not_in?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Folder query conjunction + */ +export type FolderQueryOperationsAnd = { + and: Array; +}; + +/** + * Folder query disjunction + */ +export type FolderQueryOperationsOr = { + or: Array; +}; + +/** + * Payload Locked Document query operations + */ +export type PayloadLockedDocumentQueryOperations = { + globalSlug?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Payload Locked Document query conjunction + */ +export type PayloadLockedDocumentQueryOperationsAnd = { + and: Array; +}; + +/** + * Payload Locked Document query disjunction + */ +export type PayloadLockedDocumentQueryOperationsOr = { + or: Array; +}; + +/** + * Payload Preference query operations + */ +export type PayloadPreferenceQueryOperations = { + key?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Payload Preference query conjunction + */ +export type PayloadPreferenceQueryOperationsAnd = { + and: Array; +}; + +/** + * Payload Preference query disjunction + */ +export type PayloadPreferenceQueryOperationsOr = { + or: Array; +}; + +/** + * Payload Migration query operations + */ +export type PayloadMigrationQueryOperations = { + name?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + like?: string; + contains?: string; + }; + batch?: { + equals?: number; + not_equals?: number; + in?: string; + not_in?: string; + greater_than?: number; + greater_than_equal?: number; + less_than?: number; + less_than_equal?: number; + }; + updatedAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; + createdAt?: { + equals?: string; + not_equals?: string; + in?: string; + not_in?: string; + greater_than?: string; + greater_than_equal?: string; + less_than?: string; + less_than_equal?: string; + }; +}; + +/** + * Payload Migration query conjunction + */ +export type PayloadMigrationQueryOperationsAnd = { + and: Array; +}; + +/** + * Payload Migration query disjunction + */ +export type PayloadMigrationQueryOperationsOr = { + or: Array; +}; + +/** + * Header + */ +export type Header = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +}; + +/** + * Header (if present) + */ +export type HeaderRead = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +} | { + [key: string]: unknown; +}; + +/** + * Header (writable fields) + */ +export type HeaderWrite = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +}; + +/** + * Footer + */ +export type Footer = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +}; + +/** + * Footer (if present) + */ +export type FooterRead = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +} | { + [key: string]: unknown; +}; + +/** + * Footer (writable fields) + */ +export type FooterWrite = { + id: string; + navItems?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + }; + id?: string | null; + }> | null; + updatedAt?: string | null; + createdAt?: string | null; +}; + +/** + * Page + * + * Page + */ +export type PageRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title: string; + hero: { + type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + media?: string | null | Media; + }; + layout: Array<{ + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'cta'; + } | { + columns?: Array<{ + size?: 'oneThird' | 'half' | 'twoThirds' | 'full'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + enableLink?: boolean | null; + link?: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'content'; + } | { + media: string | Media; + id?: string | null; + blockName?: string | null; + blockType: 'mediaBlock'; + } | { + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + populateBy?: 'collection' | 'selection'; + relationTo?: 'posts'; + categories?: Array | null; + limit?: number | null; + selectedDocs?: Array<{ + relationTo: 'posts'; + value: string | Post; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'archive'; + } | { + form: string | Form; + enableIntro?: boolean | null; + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'formBlock'; + }>; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + _status?: 'draft' | 'published'; +}; + +/** + * Page + * + * Page + */ +export type PagePatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title?: string; + hero?: { + type: 'none' | 'highImpact' | 'mediumImpact' | 'lowImpact'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + media?: string | null | Media; + }; + layout?: Array<{ + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + links?: Array<{ + link: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'cta'; + } | { + columns?: Array<{ + size?: 'oneThird' | 'half' | 'twoThirds' | 'full'; + richText?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + enableLink?: boolean | null; + link?: { + type?: 'reference' | 'custom'; + newTab?: boolean | null; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + label: string; + /** + * Choose how the link should be rendered. + */ + appearance?: 'default' | 'outline'; + }; + id?: string | null; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'content'; + } | { + media: string | Media; + id?: string | null; + blockName?: string | null; + blockType: 'mediaBlock'; + } | { + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + populateBy?: 'collection' | 'selection'; + relationTo?: 'posts'; + categories?: Array | null; + limit?: number | null; + selectedDocs?: Array<{ + relationTo: 'posts'; + value: string | Post; + }> | null; + id?: string | null; + blockName?: string | null; + blockType: 'archive'; + } | { + form: string | Form; + enableIntro?: boolean | null; + introContent?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'formBlock'; + }>; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug?: string; + _status?: 'draft' | 'published'; +}; + +/** + * Post + * + * Post + */ +export type PostRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title: string; + heroImage?: string | null | Media; + content: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + }; + content_html?: string | null; + relatedPosts?: Array | null; + categories?: Array | null; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + /** + * ID of the users + */ + authors?: string; + populatedAuthors?: Array<{ + id?: string | null; + name?: string | null; + }> | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + _status?: 'draft' | 'published'; +}; + +/** + * Post + * + * Post + */ +export type PostPatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title?: string; + heroImage?: string | null | Media; + content?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + }; + content_html?: string | null; + relatedPosts?: Array | null; + categories?: Array | null; + meta?: { + title?: string | null; + /** + * Maximum upload file size: 12MB. Recommended file size for images is <500KB. + */ + image?: string | null | Media; + description?: string | null; + }; + publishedAt?: string | null; + /** + * ID of the users + */ + authors?: string; + populatedAuthors?: Array<{ + id?: string | null; + name?: string | null; + }> | null; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug?: string; + _status?: 'draft' | 'published'; +}; + +/** + * Media + * + * Media + */ +export type MediaRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + alt?: string | null; + caption?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + /** + * ID of the payload-folders + */ + folder?: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; + sizes?: { + thumbnail?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + square?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + small?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + medium?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + large?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + xlarge?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + og?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + }; +}; + +/** + * Media + * + * Media + */ +export type MediaPatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + alt?: string | null; + caption?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + /** + * ID of the payload-folders + */ + folder?: string; + url?: string | null; + thumbnailURL?: string | null; + filename?: string | null; + mimeType?: string | null; + filesize?: number | null; + width?: number | null; + height?: number | null; + focalX?: number | null; + focalY?: number | null; + sizes?: { + thumbnail?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + square?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + small?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + medium?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + large?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + xlarge?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + og?: { + url?: string | null; + width?: number | null; + height?: number | null; + mimeType?: string | null; + filesize?: number | null; + filename?: string | null; + }; + }; +}; + +/** + * Category + * + * Category + */ +export type CategoryRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title: string; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug: string; + /** + * ID of the categories + */ + parent?: string; + breadcrumbs?: Array<{ + doc?: string | null | Category; + url?: string | null; + label?: string | null; + id?: string | null; + }> | null; +}; + +/** + * Category + * + * Category + */ +export type CategoryPatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title?: string; + /** + * When enabled, the slug will auto-generate from the title field on save and autosave. + */ + generateSlug?: boolean | null; + slug?: string; + /** + * ID of the categories + */ + parent?: string; + breadcrumbs?: Array<{ + doc?: string | null | Category; + url?: string | null; + label?: string | null; + id?: string | null; + }> | null; +}; + +/** + * User + * + * User + */ +export type UserRequestBody = { + email: string; + emailVerified?: string | null; + name?: string | null; + image?: string | null; + displayName?: string | null; + tenants?: Array<{ + tenant: string | Tenant; + id?: string | null; + }> | null; + accounts?: Array<{ + provider: string; + providerAccountId: string; + type: 'oidc' | 'oauth' | 'email' | 'webauthn'; + id?: string | null; + }> | null; +}; + +/** + * User + * + * User + */ +export type UserPatchRequestBody = { + email?: string; + emailVerified?: string | null; + name?: string | null; + image?: string | null; + displayName?: string | null; + tenants?: Array<{ + tenant: string | Tenant; + id?: string | null; + }> | null; + accounts?: Array<{ + provider: string; + providerAccountId: string; + type: 'oidc' | 'oauth' | 'email' | 'webauthn'; + id?: string | null; + }> | null; +}; + +/** + * Tenant + * + * 租户基本信息管理 + */ +export type TenantRequestBody = { + /** + * 租户显示名称 + */ + name?: string | null; + /** + * 租户标识符(用于 API 认证时的 X-Tenant-Slug 请求头) + */ + slug: string; + apiKeyEncrypted?: string | null; + /** + * 这是该租户的主 API Key。 + */ + apiKeyDisplay?: string | null; +}; + +/** + * Tenant + * + * 租户基本信息管理 + */ +export type TenantPatchRequestBody = { + /** + * 租户显示名称 + */ + name?: string | null; + /** + * 租户标识符(用于 API 认证时的 X-Tenant-Slug 请求头) + */ + slug?: string; + apiKeyEncrypted?: string | null; + /** + * 这是该租户的主 API Key。 + */ + apiKeyDisplay?: string | null; +}; + +/** + * Redirect + * + * Redirect + */ +export type RedirectRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + /** + * You will need to rebuild the website when changing this field. + */ + from: string; + to?: { + type?: 'reference' | 'custom'; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + }; +}; + +/** + * Redirect + * + * Redirect + */ +export type RedirectPatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + /** + * You will need to rebuild the website when changing this field. + */ + from?: string; + to?: { + type?: 'reference' | 'custom'; + reference?: { + relationTo: 'pages'; + value: string | Page; + } | null | { + relationTo: 'posts'; + value: string | Post; + }; + url?: string | null; + }; +}; + +/** + * Form + * + * Form + */ +export type FormRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title: string; + fields?: Array<{ + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + defaultValue?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'checkbox'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'country'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'email'; + } | { + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'message'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'number'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + placeholder?: string | null; + options?: Array<{ + label: string; + value: string; + id?: string | null; + }> | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'select'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'state'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'text'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'textarea'; + }> | null; + submitButtonLabel?: string | null; + /** + * Choose whether to display an on-page message or redirect to a different page after they submit the form. + */ + confirmationType?: 'message' | 'redirect'; + confirmationMessage?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + redirect?: { + url: string; + }; + /** + * Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email. + */ + emails?: Array<{ + emailTo?: string | null; + cc?: string | null; + bcc?: string | null; + replyTo?: string | null; + emailFrom?: string | null; + subject: string; + /** + * Enter the message that should be sent in this email. + */ + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + }> | null; +}; + +/** + * Form + * + * Form + */ +export type FormPatchRequestBody = { + /** + * ID of the tenants + */ + tenant?: string; + title?: string; + fields?: Array<{ + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + defaultValue?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'checkbox'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'country'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'email'; + } | { + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + blockName?: string | null; + blockType: 'message'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'number'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + placeholder?: string | null; + options?: Array<{ + label: string; + value: string; + id?: string | null; + }> | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'select'; + } | { + name: string; + label?: string | null; + width?: number | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'state'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'text'; + } | { + name: string; + label?: string | null; + width?: number | null; + defaultValue?: string | null; + required?: boolean | null; + id?: string | null; + blockName?: string | null; + blockType: 'textarea'; + }> | null; + submitButtonLabel?: string | null; + /** + * Choose whether to display an on-page message or redirect to a different page after they submit the form. + */ + confirmationType?: 'message' | 'redirect'; + confirmationMessage?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + redirect?: { + url: string; + }; + /** + * Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email. + */ + emails?: Array<{ + emailTo?: string | null; + cc?: string | null; + bcc?: string | null; + replyTo?: string | null; + emailFrom?: string | null; + subject: string; + /** + * Enter the message that should be sent in this email. + */ + message?: { + root: { + type: string; + children: Array<{ + type: string; + version: number; + [key: string]: unknown | string | number; + }>; + direction: 'ltr' | 'rtl' | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + } | null; + id?: string | null; + }> | null; +}; + +/** + * FormSubmission + * + * Form Submission + */ +export type FormSubmissionRequestBody = { + /** + * ID of the forms + */ + form: string; + submissionData?: Array<{ + field: string; + value: string; + id?: string | null; + }> | null; +}; + +/** + * FormSubmission + * + * Form Submission + */ +export type FormSubmissionPatchRequestBody = { + /** + * ID of the forms + */ + form?: string; + submissionData?: Array<{ + field: string; + value: string; + id?: string | null; + }> | null; +}; + +/** + * Search + * + * This is a collection of automatically created search results. These results are used by the global site search and will be updated automatically as documents in the CMS are created or updated. + */ +export type SearchResultRequestBody = { + title?: string | null; + priority?: number | null; + /** + * ID of the posts + */ + doc: string; + slug?: string | null; + meta?: { + title?: string | null; + description?: string | null; + image?: string | null | Media; + }; + categories?: Array<{ + relationTo?: string | null; + categoryID?: string | null; + title?: string | null; + id?: string | null; + }> | null; +}; + +/** + * Search + * + * This is a collection of automatically created search results. These results are used by the global site search and will be updated automatically as documents in the CMS are created or updated. + */ +export type SearchResultPatchRequestBody = { + title?: string | null; + priority?: number | null; + /** + * ID of the posts + */ + doc?: string; + slug?: string | null; + meta?: { + title?: string | null; + description?: string | null; + image?: string | null | Media; + }; + categories?: Array<{ + relationTo?: string | null; + categoryID?: string | null; + title?: string | null; + id?: string | null; + }> | null; +}; + +/** + * PayloadKv + * + * Payload Kv + */ +export type PayloadKvRequestBody = { + key: string; + data: { + [key: string]: unknown; + } | Array | string | number | boolean | null; +}; + +/** + * PayloadKv + * + * Payload Kv + */ +export type PayloadKvPatchRequestBody = { + key?: string; + data?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; +}; + +/** + * PayloadJob + * + * Payload Job + */ +export type PayloadJobRequestBody = { + /** + * Input data provided to the job + */ + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + taskStatus?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + completedAt?: string | null; + totalTried?: number | null; + /** + * If hasError is true this job will not be retried + */ + hasError?: boolean | null; + /** + * If hasError is true, this is the error that caused it + */ + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + /** + * Task execution log + */ + log?: Array<{ + executedAt: string; + completedAt: string; + taskSlug: 'inline' | 'schedulePublish'; + taskID: string; + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + output?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + state: 'failed' | 'succeeded'; + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + id?: string | null; + }> | null; + taskSlug?: 'inline' | 'schedulePublish'; + queue?: string | null; + waitUntil?: string | null; + processing?: boolean | null; +}; + +/** + * PayloadJob + * + * Payload Job + */ +export type PayloadJobPatchRequestBody = { + /** + * Input data provided to the job + */ + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + taskStatus?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + completedAt?: string | null; + totalTried?: number | null; + /** + * If hasError is true this job will not be retried + */ + hasError?: boolean | null; + /** + * If hasError is true, this is the error that caused it + */ + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + /** + * Task execution log + */ + log?: Array<{ + executedAt: string; + completedAt: string; + taskSlug: 'inline' | 'schedulePublish'; + taskID: string; + input?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + output?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + state: 'failed' | 'succeeded'; + error?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; + id?: string | null; + }> | null; + taskSlug?: 'inline' | 'schedulePublish'; + queue?: string | null; + waitUntil?: string | null; + processing?: boolean | null; +}; + +/** + * FolderInterface + * + * Folder + */ +export type FolderRequestBody = { + name: string; + /** + * ID of the payload-folders + */ + folder?: string; + documentsAndFolders?: { + docs?: Array<{ + relationTo?: 'payload-folders'; + value: string | Folder; + } | { + relationTo?: 'media'; + value: string | Media; + }>; + hasNextPage?: boolean; + totalDocs?: number; + }; + folderType?: Array<'media'> | null; +}; + +/** + * FolderInterface + * + * Folder + */ +export type FolderPatchRequestBody = { + name?: string; + /** + * ID of the payload-folders + */ + folder?: string; + documentsAndFolders?: { + docs?: Array<{ + relationTo?: 'payload-folders'; + value: string | Folder; + } | { + relationTo?: 'media'; + value: string | Media; + }>; + hasNextPage?: boolean; + totalDocs?: number; + }; + folderType?: Array<'media'> | null; +}; + +/** + * PayloadLockedDocument + * + * Payload Locked Document + */ +export type PayloadLockedDocumentRequestBody = { + /** + * ID of the pages/posts/media/categories/users/tenants/redirects/forms/form-submissions/search/payload-folders + */ + document?: string; + globalSlug?: string | null; + /** + * ID of the users + */ + user: string; +}; + +/** + * PayloadLockedDocument + * + * Payload Locked Document + */ +export type PayloadLockedDocumentPatchRequestBody = { + /** + * ID of the pages/posts/media/categories/users/tenants/redirects/forms/form-submissions/search/payload-folders + */ + document?: string; + globalSlug?: string | null; + /** + * ID of the users + */ + user?: string; +}; + +/** + * PayloadPreference + * + * Payload Preference + */ +export type PayloadPreferenceRequestBody = { + /** + * ID of the users + */ + user: string; + key?: string | null; + value?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; +}; + +/** + * PayloadPreference + * + * Payload Preference + */ +export type PayloadPreferencePatchRequestBody = { + /** + * ID of the users + */ + user?: string; + key?: string | null; + value?: { + [key: string]: unknown; + } | Array | string | number | boolean | null; +}; + +/** + * PayloadMigration + * + * Payload Migration + */ +export type PayloadMigrationRequestBody = { + name?: string | null; + batch?: number | null; +}; + +/** + * PayloadMigration + * + * Payload Migration + */ +export type PayloadMigrationPatchRequestBody = { + name?: string | null; + batch?: number | null; +}; + +/** + * Header + */ +export type HeaderRequestBody = HeaderWrite; + +/** + * Footer + */ +export type FooterRequestBody = FooterWrite; + +export type ListPagesData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'title' | '-title' | 'publishedAt' | '-publishedAt' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PageQueryOperations | PageQueryOperationsAnd | PageQueryOperationsOr); + }; + url: '/api/pages'; +}; + +export type ListPagesResponses = { + /** + * List of Pages + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPagesResponse = ListPagesResponses[keyof ListPagesResponses]; + +export type CreatePageData = { + /** + * Page + */ + body?: PageRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/pages'; +}; + +export type CreatePageResponses = { + /** + * Page object + */ + 201: { + message: string; + doc: Page & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePageResponse = CreatePageResponses[keyof CreatePageResponses]; + +export type DeletePageData = { + body?: never; + path: { + /** + * ID of the Page + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/pages/{id}'; +}; + +export type DeletePageErrors = { + /** + * Page not found + */ + 404: unknown; +}; + +export type DeletePageResponses = { + /** + * Page object + */ + 200: Page; +}; + +export type DeletePageResponse = DeletePageResponses[keyof DeletePageResponses]; + +export type FindPageByIdData = { + body?: never; + path: { + /** + * ID of the Page + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/pages/{id}'; +}; + +export type FindPageByIdErrors = { + /** + * Page not found + */ + 404: unknown; +}; + +export type FindPageByIdResponses = { + /** + * Page object + */ + 200: Page; +}; + +export type FindPageByIdResponse = FindPageByIdResponses[keyof FindPageByIdResponses]; + +export type UpdatePageData = { + /** + * Page + */ + body?: PagePatchRequestBody; + path: { + /** + * ID of the Page + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/pages/{id}'; +}; + +export type UpdatePageErrors = { + /** + * Page not found + */ + 404: unknown; +}; + +export type UpdatePageResponses = { + /** + * Page object + */ + 200: Page; +}; + +export type UpdatePageResponse = UpdatePageResponses[keyof UpdatePageResponses]; + +export type ListPostsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'title' | '-title' | 'publishedAt' | '-publishedAt' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PostQueryOperations | PostQueryOperationsAnd | PostQueryOperationsOr); + }; + url: '/api/posts'; +}; + +export type ListPostsResponses = { + /** + * List of Posts + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPostsResponse = ListPostsResponses[keyof ListPostsResponses]; + +export type CreatePostData = { + /** + * Post + */ + body?: PostRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/posts'; +}; + +export type CreatePostResponses = { + /** + * Post object + */ + 201: { + message: string; + doc: Post & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePostResponse = CreatePostResponses[keyof CreatePostResponses]; + +export type DeletePostData = { + body?: never; + path: { + /** + * ID of the Post + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/posts/{id}'; +}; + +export type DeletePostErrors = { + /** + * Post not found + */ + 404: unknown; +}; + +export type DeletePostResponses = { + /** + * Post object + */ + 200: Post; +}; + +export type DeletePostResponse = DeletePostResponses[keyof DeletePostResponses]; + +export type FindPostByIdData = { + body?: never; + path: { + /** + * ID of the Post + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/posts/{id}'; +}; + +export type FindPostByIdErrors = { + /** + * Post not found + */ + 404: unknown; +}; + +export type FindPostByIdResponses = { + /** + * Post object + */ + 200: Post; +}; + +export type FindPostByIdResponse = FindPostByIdResponses[keyof FindPostByIdResponses]; + +export type UpdatePostData = { + /** + * Post + */ + body?: PostPatchRequestBody; + path: { + /** + * ID of the Post + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/posts/{id}'; +}; + +export type UpdatePostErrors = { + /** + * Post not found + */ + 404: unknown; +}; + +export type UpdatePostResponses = { + /** + * Post object + */ + 200: Post; +}; + +export type UpdatePostResponse = UpdatePostResponses[keyof UpdatePostResponses]; + +export type ListMediaData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'alt' | '-alt' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt' | 'url' | '-url' | 'thumbnailURL' | '-thumbnailURL' | 'filename' | '-filename' | 'mimeType' | '-mimeType' | 'filesize' | '-filesize' | 'width' | '-width' | 'height' | '-height' | 'focalX' | '-focalX' | 'focalY' | '-focalY'; + where?: { + [key: string]: unknown; + } & (MediaQueryOperations | MediaQueryOperationsAnd | MediaQueryOperationsOr); + }; + url: '/api/media'; +}; + +export type ListMediaResponses = { + /** + * List of Media + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListMediaResponse = ListMediaResponses[keyof ListMediaResponses]; + +export type CreateMediaData = { + /** + * Media + */ + body?: MediaRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/media'; +}; + +export type CreateMediaResponses = { + /** + * Media object + */ + 201: { + message: string; + doc: Media & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateMediaResponse = CreateMediaResponses[keyof CreateMediaResponses]; + +export type DeleteMediaData = { + body?: never; + path: { + /** + * ID of the Media + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/media/{id}'; +}; + +export type DeleteMediaErrors = { + /** + * Media not found + */ + 404: unknown; +}; + +export type DeleteMediaResponses = { + /** + * Media object + */ + 200: Media; +}; + +export type DeleteMediaResponse = DeleteMediaResponses[keyof DeleteMediaResponses]; + +export type FindMediaByIdData = { + body?: never; + path: { + /** + * ID of the Media + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/media/{id}'; +}; + +export type FindMediaByIdErrors = { + /** + * Media not found + */ + 404: unknown; +}; + +export type FindMediaByIdResponses = { + /** + * Media object + */ + 200: Media; +}; + +export type FindMediaByIdResponse = FindMediaByIdResponses[keyof FindMediaByIdResponses]; + +export type UpdateMediaData = { + /** + * Media + */ + body?: MediaPatchRequestBody; + path: { + /** + * ID of the Media + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/media/{id}'; +}; + +export type UpdateMediaErrors = { + /** + * Media not found + */ + 404: unknown; +}; + +export type UpdateMediaResponses = { + /** + * Media object + */ + 200: Media; +}; + +export type UpdateMediaResponse = UpdateMediaResponses[keyof UpdateMediaResponses]; + +export type ListCategoriesData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'title' | '-title' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (CategoryQueryOperations | CategoryQueryOperationsAnd | CategoryQueryOperationsOr); + }; + url: '/api/categories'; +}; + +export type ListCategoriesResponses = { + /** + * List of Categories + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListCategoriesResponse = ListCategoriesResponses[keyof ListCategoriesResponses]; + +export type CreateCategoryData = { + /** + * Category + */ + body?: CategoryRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/categories'; +}; + +export type CreateCategoryResponses = { + /** + * Category object + */ + 201: { + message: string; + doc: Category & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateCategoryResponse = CreateCategoryResponses[keyof CreateCategoryResponses]; + +export type DeleteCategoryData = { + body?: never; + path: { + /** + * ID of the Category + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/categories/{id}'; +}; + +export type DeleteCategoryErrors = { + /** + * Category not found + */ + 404: unknown; +}; + +export type DeleteCategoryResponses = { + /** + * Category object + */ + 200: Category; +}; + +export type DeleteCategoryResponse = DeleteCategoryResponses[keyof DeleteCategoryResponses]; + +export type FindCategoryByIdData = { + body?: never; + path: { + /** + * ID of the Category + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/categories/{id}'; +}; + +export type FindCategoryByIdErrors = { + /** + * Category not found + */ + 404: unknown; +}; + +export type FindCategoryByIdResponses = { + /** + * Category object + */ + 200: Category; +}; + +export type FindCategoryByIdResponse = FindCategoryByIdResponses[keyof FindCategoryByIdResponses]; + +export type UpdateCategoryData = { + /** + * Category + */ + body?: CategoryPatchRequestBody; + path: { + /** + * ID of the Category + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/categories/{id}'; +}; + +export type UpdateCategoryErrors = { + /** + * Category not found + */ + 404: unknown; +}; + +export type UpdateCategoryResponses = { + /** + * Category object + */ + 200: Category; +}; + +export type UpdateCategoryResponse = UpdateCategoryResponses[keyof UpdateCategoryResponses]; + +export type ListUsersData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'id' | '-id' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (UserQueryOperations | UserQueryOperationsAnd | UserQueryOperationsOr); + }; + url: '/api/users'; +}; + +export type ListUsersResponses = { + /** + * List of Users + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListUsersResponse = ListUsersResponses[keyof ListUsersResponses]; + +export type CreateUserData = { + /** + * User + */ + body?: UserRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/users'; +}; + +export type CreateUserResponses = { + /** + * User object + */ + 201: { + message: string; + doc: User & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateUserResponse = CreateUserResponses[keyof CreateUserResponses]; + +export type DeleteUserData = { + body?: never; + path: { + /** + * ID of the User + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/users/{id}'; +}; + +export type DeleteUserErrors = { + /** + * User not found + */ + 404: unknown; +}; + +export type DeleteUserResponses = { + /** + * User object + */ + 200: User; +}; + +export type DeleteUserResponse = DeleteUserResponses[keyof DeleteUserResponses]; + +export type FindUserByIdData = { + body?: never; + path: { + /** + * ID of the User + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/users/{id}'; +}; + +export type FindUserByIdErrors = { + /** + * User not found + */ + 404: unknown; +}; + +export type FindUserByIdResponses = { + /** + * User object + */ + 200: User; +}; + +export type FindUserByIdResponse = FindUserByIdResponses[keyof FindUserByIdResponses]; + +export type UpdateUserData = { + /** + * User + */ + body?: UserPatchRequestBody; + path: { + /** + * ID of the User + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/users/{id}'; +}; + +export type UpdateUserErrors = { + /** + * User not found + */ + 404: unknown; +}; + +export type UpdateUserResponses = { + /** + * User object + */ + 200: User; +}; + +export type UpdateUserResponse = UpdateUserResponses[keyof UpdateUserResponses]; + +export type ListTenantsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'name' | '-name' | 'slug' | '-slug' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (TenantQueryOperations | TenantQueryOperationsAnd | TenantQueryOperationsOr); + }; + url: '/api/tenants'; +}; + +export type ListTenantsResponses = { + /** + * List of Tenants + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListTenantsResponse = ListTenantsResponses[keyof ListTenantsResponses]; + +export type CreateTenantData = { + /** + * Tenant + */ + body?: TenantRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/tenants'; +}; + +export type CreateTenantResponses = { + /** + * Tenant object + */ + 201: { + message: string; + doc: Tenant & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateTenantResponse = CreateTenantResponses[keyof CreateTenantResponses]; + +export type DeleteTenantData = { + body?: never; + path: { + /** + * ID of the Tenant + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/tenants/{id}'; +}; + +export type DeleteTenantErrors = { + /** + * Tenant not found + */ + 404: unknown; +}; + +export type DeleteTenantResponses = { + /** + * Tenant object + */ + 200: Tenant; +}; + +export type DeleteTenantResponse = DeleteTenantResponses[keyof DeleteTenantResponses]; + +export type FindTenantByIdData = { + body?: never; + path: { + /** + * ID of the Tenant + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/tenants/{id}'; +}; + +export type FindTenantByIdErrors = { + /** + * Tenant not found + */ + 404: unknown; +}; + +export type FindTenantByIdResponses = { + /** + * Tenant object + */ + 200: Tenant; +}; + +export type FindTenantByIdResponse = FindTenantByIdResponses[keyof FindTenantByIdResponses]; + +export type UpdateTenantData = { + /** + * Tenant + */ + body?: TenantPatchRequestBody; + path: { + /** + * ID of the Tenant + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/tenants/{id}'; +}; + +export type UpdateTenantErrors = { + /** + * Tenant not found + */ + 404: unknown; +}; + +export type UpdateTenantResponses = { + /** + * Tenant object + */ + 200: Tenant; +}; + +export type UpdateTenantResponse = UpdateTenantResponses[keyof UpdateTenantResponses]; + +export type ListRedirectsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'from' | '-from' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (RedirectQueryOperations | RedirectQueryOperationsAnd | RedirectQueryOperationsOr); + }; + url: '/api/redirects'; +}; + +export type ListRedirectsResponses = { + /** + * List of Redirects + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListRedirectsResponse = ListRedirectsResponses[keyof ListRedirectsResponses]; + +export type CreateRedirectData = { + /** + * Redirect + */ + body?: RedirectRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/redirects'; +}; + +export type CreateRedirectResponses = { + /** + * Redirect object + */ + 201: { + message: string; + doc: Redirect & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateRedirectResponse = CreateRedirectResponses[keyof CreateRedirectResponses]; + +export type DeleteRedirectData = { + body?: never; + path: { + /** + * ID of the Redirect + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/redirects/{id}'; +}; + +export type DeleteRedirectErrors = { + /** + * Redirect not found + */ + 404: unknown; +}; + +export type DeleteRedirectResponses = { + /** + * Redirect object + */ + 200: Redirect; +}; + +export type DeleteRedirectResponse = DeleteRedirectResponses[keyof DeleteRedirectResponses]; + +export type FindRedirectByIdData = { + body?: never; + path: { + /** + * ID of the Redirect + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/redirects/{id}'; +}; + +export type FindRedirectByIdErrors = { + /** + * Redirect not found + */ + 404: unknown; +}; + +export type FindRedirectByIdResponses = { + /** + * Redirect object + */ + 200: Redirect; +}; + +export type FindRedirectByIdResponse = FindRedirectByIdResponses[keyof FindRedirectByIdResponses]; + +export type UpdateRedirectData = { + /** + * Redirect + */ + body?: RedirectPatchRequestBody; + path: { + /** + * ID of the Redirect + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/redirects/{id}'; +}; + +export type UpdateRedirectErrors = { + /** + * Redirect not found + */ + 404: unknown; +}; + +export type UpdateRedirectResponses = { + /** + * Redirect object + */ + 200: Redirect; +}; + +export type UpdateRedirectResponse = UpdateRedirectResponses[keyof UpdateRedirectResponses]; + +export type ListFormsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'title' | '-title' | 'submitButtonLabel' | '-submitButtonLabel' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (FormQueryOperations | FormQueryOperationsAnd | FormQueryOperationsOr); + }; + url: '/api/forms'; +}; + +export type ListFormsResponses = { + /** + * List of Forms + */ + 200: { + docs: Array
; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListFormsResponse = ListFormsResponses[keyof ListFormsResponses]; + +export type CreateFormData = { + /** + * Form + */ + body?: FormRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/forms'; +}; + +export type CreateFormResponses = { + /** + * Form object + */ + 201: { + message: string; + doc: Form & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateFormResponse = CreateFormResponses[keyof CreateFormResponses]; + +export type DeleteFormData = { + body?: never; + path: { + /** + * ID of the Form + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/forms/{id}'; +}; + +export type DeleteFormErrors = { + /** + * Form not found + */ + 404: unknown; +}; + +export type DeleteFormResponses = { + /** + * Form object + */ + 200: Form; +}; + +export type DeleteFormResponse = DeleteFormResponses[keyof DeleteFormResponses]; + +export type FindFormByIdData = { + body?: never; + path: { + /** + * ID of the Form + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/forms/{id}'; +}; + +export type FindFormByIdErrors = { + /** + * Form not found + */ + 404: unknown; +}; + +export type FindFormByIdResponses = { + /** + * Form object + */ + 200: Form; +}; + +export type FindFormByIdResponse = FindFormByIdResponses[keyof FindFormByIdResponses]; + +export type UpdateFormData = { + /** + * Form + */ + body?: FormPatchRequestBody; + path: { + /** + * ID of the Form + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/forms/{id}'; +}; + +export type UpdateFormErrors = { + /** + * Form not found + */ + 404: unknown; +}; + +export type UpdateFormResponses = { + /** + * Form object + */ + 200: Form; +}; + +export type UpdateFormResponse = UpdateFormResponses[keyof UpdateFormResponses]; + +export type ListFormSubmissionsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (FormSubmissionQueryOperations | FormSubmissionQueryOperationsAnd | FormSubmissionQueryOperationsOr); + }; + url: '/api/form-submissions'; +}; + +export type ListFormSubmissionsResponses = { + /** + * List of Form Submissions + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListFormSubmissionsResponse = ListFormSubmissionsResponses[keyof ListFormSubmissionsResponses]; + +export type CreateFormSubmissionData = { + /** + * Form Submission + */ + body?: FormSubmissionRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/form-submissions'; +}; + +export type CreateFormSubmissionResponses = { + /** + * Form Submission object + */ + 201: { + message: string; + doc: FormSubmission & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateFormSubmissionResponse = CreateFormSubmissionResponses[keyof CreateFormSubmissionResponses]; + +export type DeleteFormSubmissionData = { + body?: never; + path: { + /** + * ID of the Form Submission + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/form-submissions/{id}'; +}; + +export type DeleteFormSubmissionErrors = { + /** + * Form Submission not found + */ + 404: unknown; +}; + +export type DeleteFormSubmissionResponses = { + /** + * Form Submission object + */ + 200: FormSubmission; +}; + +export type DeleteFormSubmissionResponse = DeleteFormSubmissionResponses[keyof DeleteFormSubmissionResponses]; + +export type FindFormSubmissionByIdData = { + body?: never; + path: { + /** + * ID of the Form Submission + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/form-submissions/{id}'; +}; + +export type FindFormSubmissionByIdErrors = { + /** + * Form Submission not found + */ + 404: unknown; +}; + +export type FindFormSubmissionByIdResponses = { + /** + * Form Submission object + */ + 200: FormSubmission; +}; + +export type FindFormSubmissionByIdResponse = FindFormSubmissionByIdResponses[keyof FindFormSubmissionByIdResponses]; + +export type UpdateFormSubmissionData = { + /** + * Form Submission + */ + body?: FormSubmissionPatchRequestBody; + path: { + /** + * ID of the Form Submission + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/form-submissions/{id}'; +}; + +export type UpdateFormSubmissionErrors = { + /** + * Form Submission not found + */ + 404: unknown; +}; + +export type UpdateFormSubmissionResponses = { + /** + * Form Submission object + */ + 200: FormSubmission; +}; + +export type UpdateFormSubmissionResponse = UpdateFormSubmissionResponses[keyof UpdateFormSubmissionResponses]; + +export type ListSearchResultsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'title' | '-title' | 'priority' | '-priority' | 'slug' | '-slug' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (SearchResultQueryOperations | SearchResultQueryOperationsAnd | SearchResultQueryOperationsOr); + }; + url: '/api/search'; +}; + +export type ListSearchResultsResponses = { + /** + * List of Search Results + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListSearchResultsResponse = ListSearchResultsResponses[keyof ListSearchResultsResponses]; + +export type CreateSearchResultData = { + /** + * Search Result + */ + body?: SearchResultRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/search'; +}; + +export type CreateSearchResultResponses = { + /** + * Search Result object + */ + 201: { + message: string; + doc: SearchResult & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateSearchResultResponse = CreateSearchResultResponses[keyof CreateSearchResultResponses]; + +export type DeleteSearchResultData = { + body?: never; + path: { + /** + * ID of the Search Result + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/search/{id}'; +}; + +export type DeleteSearchResultErrors = { + /** + * Search Result not found + */ + 404: unknown; +}; + +export type DeleteSearchResultResponses = { + /** + * Search Result object + */ + 200: SearchResult; +}; + +export type DeleteSearchResultResponse = DeleteSearchResultResponses[keyof DeleteSearchResultResponses]; + +export type FindSearchResultByIdData = { + body?: never; + path: { + /** + * ID of the Search Result + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/search/{id}'; +}; + +export type FindSearchResultByIdErrors = { + /** + * Search Result not found + */ + 404: unknown; +}; + +export type FindSearchResultByIdResponses = { + /** + * Search Result object + */ + 200: SearchResult; +}; + +export type FindSearchResultByIdResponse = FindSearchResultByIdResponses[keyof FindSearchResultByIdResponses]; + +export type UpdateSearchResultData = { + /** + * Search Result + */ + body?: SearchResultPatchRequestBody; + path: { + /** + * ID of the Search Result + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/search/{id}'; +}; + +export type UpdateSearchResultErrors = { + /** + * Search Result not found + */ + 404: unknown; +}; + +export type UpdateSearchResultResponses = { + /** + * Search Result object + */ + 200: SearchResult; +}; + +export type UpdateSearchResultResponse = UpdateSearchResultResponses[keyof UpdateSearchResultResponses]; + +export type ListPayloadKvsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'key' | '-key'; + where?: { + [key: string]: unknown; + } & (PayloadKvQueryOperations | PayloadKvQueryOperationsAnd | PayloadKvQueryOperationsOr); + }; + url: '/api/payload-kv'; +}; + +export type ListPayloadKvsResponses = { + /** + * List of Payload Kvs + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPayloadKvsResponse = ListPayloadKvsResponses[keyof ListPayloadKvsResponses]; + +export type CreatePayloadKvData = { + /** + * Payload Kv + */ + body?: PayloadKvRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-kv'; +}; + +export type CreatePayloadKvResponses = { + /** + * Payload Kv object + */ + 201: { + message: string; + doc: PayloadKv & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePayloadKvResponse = CreatePayloadKvResponses[keyof CreatePayloadKvResponses]; + +export type DeletePayloadKvData = { + body?: never; + path: { + /** + * ID of the Payload Kv + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-kv/{id}'; +}; + +export type DeletePayloadKvErrors = { + /** + * Payload Kv not found + */ + 404: unknown; +}; + +export type DeletePayloadKvResponses = { + /** + * Payload Kv object + */ + 200: PayloadKv; +}; + +export type DeletePayloadKvResponse = DeletePayloadKvResponses[keyof DeletePayloadKvResponses]; + +export type FindPayloadKvByIdData = { + body?: never; + path: { + /** + * ID of the Payload Kv + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-kv/{id}'; +}; + +export type FindPayloadKvByIdErrors = { + /** + * Payload Kv not found + */ + 404: unknown; +}; + +export type FindPayloadKvByIdResponses = { + /** + * Payload Kv object + */ + 200: PayloadKv; +}; + +export type FindPayloadKvByIdResponse = FindPayloadKvByIdResponses[keyof FindPayloadKvByIdResponses]; + +export type UpdatePayloadKvData = { + /** + * Payload Kv + */ + body?: PayloadKvPatchRequestBody; + path: { + /** + * ID of the Payload Kv + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-kv/{id}'; +}; + +export type UpdatePayloadKvErrors = { + /** + * Payload Kv not found + */ + 404: unknown; +}; + +export type UpdatePayloadKvResponses = { + /** + * Payload Kv object + */ + 200: PayloadKv; +}; + +export type UpdatePayloadKvResponse = UpdatePayloadKvResponses[keyof UpdatePayloadKvResponses]; + +export type ListPayloadJobsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'queue' | '-queue' | 'waitUntil' | '-waitUntil' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PayloadJobQueryOperations | PayloadJobQueryOperationsAnd | PayloadJobQueryOperationsOr); + }; + url: '/api/payload-jobs'; +}; + +export type ListPayloadJobsResponses = { + /** + * List of Payload Jobs + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPayloadJobsResponse = ListPayloadJobsResponses[keyof ListPayloadJobsResponses]; + +export type CreatePayloadJobData = { + /** + * Payload Job + */ + body?: PayloadJobRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-jobs'; +}; + +export type CreatePayloadJobResponses = { + /** + * Payload Job object + */ + 201: { + message: string; + doc: PayloadJob & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePayloadJobResponse = CreatePayloadJobResponses[keyof CreatePayloadJobResponses]; + +export type DeletePayloadJobData = { + body?: never; + path: { + /** + * ID of the Payload Job + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-jobs/{id}'; +}; + +export type DeletePayloadJobErrors = { + /** + * Payload Job not found + */ + 404: unknown; +}; + +export type DeletePayloadJobResponses = { + /** + * Payload Job object + */ + 200: PayloadJob; +}; + +export type DeletePayloadJobResponse = DeletePayloadJobResponses[keyof DeletePayloadJobResponses]; + +export type FindPayloadJobByIdData = { + body?: never; + path: { + /** + * ID of the Payload Job + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-jobs/{id}'; +}; + +export type FindPayloadJobByIdErrors = { + /** + * Payload Job not found + */ + 404: unknown; +}; + +export type FindPayloadJobByIdResponses = { + /** + * Payload Job object + */ + 200: PayloadJob; +}; + +export type FindPayloadJobByIdResponse = FindPayloadJobByIdResponses[keyof FindPayloadJobByIdResponses]; + +export type UpdatePayloadJobData = { + /** + * Payload Job + */ + body?: PayloadJobPatchRequestBody; + path: { + /** + * ID of the Payload Job + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-jobs/{id}'; +}; + +export type UpdatePayloadJobErrors = { + /** + * Payload Job not found + */ + 404: unknown; +}; + +export type UpdatePayloadJobResponses = { + /** + * Payload Job object + */ + 200: PayloadJob; +}; + +export type UpdatePayloadJobResponse = UpdatePayloadJobResponses[keyof UpdatePayloadJobResponses]; + +export type ListFoldersData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'name' | '-name' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (FolderQueryOperations | FolderQueryOperationsAnd | FolderQueryOperationsOr); + }; + url: '/api/payload-folders'; +}; + +export type ListFoldersResponses = { + /** + * List of Folders + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListFoldersResponse = ListFoldersResponses[keyof ListFoldersResponses]; + +export type CreateFolderData = { + /** + * Folder + */ + body?: FolderRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-folders'; +}; + +export type CreateFolderResponses = { + /** + * Folder object + */ + 201: { + message: string; + doc: Folder & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreateFolderResponse = CreateFolderResponses[keyof CreateFolderResponses]; + +export type DeleteFolderData = { + body?: never; + path: { + /** + * ID of the Folder + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-folders/{id}'; +}; + +export type DeleteFolderErrors = { + /** + * Folder not found + */ + 404: unknown; +}; + +export type DeleteFolderResponses = { + /** + * Folder object + */ + 200: Folder; +}; + +export type DeleteFolderResponse = DeleteFolderResponses[keyof DeleteFolderResponses]; + +export type FindFolderByIdData = { + body?: never; + path: { + /** + * ID of the Folder + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-folders/{id}'; +}; + +export type FindFolderByIdErrors = { + /** + * Folder not found + */ + 404: unknown; +}; + +export type FindFolderByIdResponses = { + /** + * Folder object + */ + 200: Folder; +}; + +export type FindFolderByIdResponse = FindFolderByIdResponses[keyof FindFolderByIdResponses]; + +export type UpdateFolderData = { + /** + * Folder + */ + body?: FolderPatchRequestBody; + path: { + /** + * ID of the Folder + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-folders/{id}'; +}; + +export type UpdateFolderErrors = { + /** + * Folder not found + */ + 404: unknown; +}; + +export type UpdateFolderResponses = { + /** + * Folder object + */ + 200: Folder; +}; + +export type UpdateFolderResponse = UpdateFolderResponses[keyof UpdateFolderResponses]; + +export type ListPayloadLockedDocumentsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'globalSlug' | '-globalSlug' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PayloadLockedDocumentQueryOperations | PayloadLockedDocumentQueryOperationsAnd | PayloadLockedDocumentQueryOperationsOr); + }; + url: '/api/payload-locked-documents'; +}; + +export type ListPayloadLockedDocumentsResponses = { + /** + * List of Payload Locked Documents + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPayloadLockedDocumentsResponse = ListPayloadLockedDocumentsResponses[keyof ListPayloadLockedDocumentsResponses]; + +export type CreatePayloadLockedDocumentData = { + /** + * Payload Locked Document + */ + body?: PayloadLockedDocumentRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-locked-documents'; +}; + +export type CreatePayloadLockedDocumentResponses = { + /** + * Payload Locked Document object + */ + 201: { + message: string; + doc: PayloadLockedDocument & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePayloadLockedDocumentResponse = CreatePayloadLockedDocumentResponses[keyof CreatePayloadLockedDocumentResponses]; + +export type DeletePayloadLockedDocumentData = { + body?: never; + path: { + /** + * ID of the Payload Locked Document + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-locked-documents/{id}'; +}; + +export type DeletePayloadLockedDocumentErrors = { + /** + * Payload Locked Document not found + */ + 404: unknown; +}; + +export type DeletePayloadLockedDocumentResponses = { + /** + * Payload Locked Document object + */ + 200: PayloadLockedDocument; +}; + +export type DeletePayloadLockedDocumentResponse = DeletePayloadLockedDocumentResponses[keyof DeletePayloadLockedDocumentResponses]; + +export type FindPayloadLockedDocumentByIdData = { + body?: never; + path: { + /** + * ID of the Payload Locked Document + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-locked-documents/{id}'; +}; + +export type FindPayloadLockedDocumentByIdErrors = { + /** + * Payload Locked Document not found + */ + 404: unknown; +}; + +export type FindPayloadLockedDocumentByIdResponses = { + /** + * Payload Locked Document object + */ + 200: PayloadLockedDocument; +}; + +export type FindPayloadLockedDocumentByIdResponse = FindPayloadLockedDocumentByIdResponses[keyof FindPayloadLockedDocumentByIdResponses]; + +export type UpdatePayloadLockedDocumentData = { + /** + * Payload Locked Document + */ + body?: PayloadLockedDocumentPatchRequestBody; + path: { + /** + * ID of the Payload Locked Document + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-locked-documents/{id}'; +}; + +export type UpdatePayloadLockedDocumentErrors = { + /** + * Payload Locked Document not found + */ + 404: unknown; +}; + +export type UpdatePayloadLockedDocumentResponses = { + /** + * Payload Locked Document object + */ + 200: PayloadLockedDocument; +}; + +export type UpdatePayloadLockedDocumentResponse = UpdatePayloadLockedDocumentResponses[keyof UpdatePayloadLockedDocumentResponses]; + +export type ListPayloadPreferencesData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'key' | '-key' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PayloadPreferenceQueryOperations | PayloadPreferenceQueryOperationsAnd | PayloadPreferenceQueryOperationsOr); + }; + url: '/api/payload-preferences'; +}; + +export type ListPayloadPreferencesResponses = { + /** + * List of Payload Preferences + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPayloadPreferencesResponse = ListPayloadPreferencesResponses[keyof ListPayloadPreferencesResponses]; + +export type CreatePayloadPreferenceData = { + /** + * Payload Preference + */ + body?: PayloadPreferenceRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-preferences'; +}; + +export type CreatePayloadPreferenceResponses = { + /** + * Payload Preference object + */ + 201: { + message: string; + doc: PayloadPreference & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePayloadPreferenceResponse = CreatePayloadPreferenceResponses[keyof CreatePayloadPreferenceResponses]; + +export type DeletePayloadPreferenceData = { + body?: never; + path: { + /** + * ID of the Payload Preference + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-preferences/{id}'; +}; + +export type DeletePayloadPreferenceErrors = { + /** + * Payload Preference not found + */ + 404: unknown; +}; + +export type DeletePayloadPreferenceResponses = { + /** + * Payload Preference object + */ + 200: PayloadPreference; +}; + +export type DeletePayloadPreferenceResponse = DeletePayloadPreferenceResponses[keyof DeletePayloadPreferenceResponses]; + +export type FindPayloadPreferenceByIdData = { + body?: never; + path: { + /** + * ID of the Payload Preference + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-preferences/{id}'; +}; + +export type FindPayloadPreferenceByIdErrors = { + /** + * Payload Preference not found + */ + 404: unknown; +}; + +export type FindPayloadPreferenceByIdResponses = { + /** + * Payload Preference object + */ + 200: PayloadPreference; +}; + +export type FindPayloadPreferenceByIdResponse = FindPayloadPreferenceByIdResponses[keyof FindPayloadPreferenceByIdResponses]; + +export type UpdatePayloadPreferenceData = { + /** + * Payload Preference + */ + body?: PayloadPreferencePatchRequestBody; + path: { + /** + * ID of the Payload Preference + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-preferences/{id}'; +}; + +export type UpdatePayloadPreferenceErrors = { + /** + * Payload Preference not found + */ + 404: unknown; +}; + +export type UpdatePayloadPreferenceResponses = { + /** + * Payload Preference object + */ + 200: PayloadPreference; +}; + +export type UpdatePayloadPreferenceResponse = UpdatePayloadPreferenceResponses[keyof UpdatePayloadPreferenceResponses]; + +export type ListPayloadMigrationsData = { + body?: never; + path?: never; + query?: { + page?: number; + limit?: number; + depth?: number; + locale?: string; + 'fallback-locale'?: string; + sort?: 'name' | '-name' | 'batch' | '-batch' | 'updatedAt' | '-updatedAt' | 'createdAt' | '-createdAt'; + where?: { + [key: string]: unknown; + } & (PayloadMigrationQueryOperations | PayloadMigrationQueryOperationsAnd | PayloadMigrationQueryOperationsOr); + }; + url: '/api/payload-migrations'; +}; + +export type ListPayloadMigrationsResponses = { + /** + * List of Payload Migrations + */ + 200: { + docs: Array; + totalDocs: number; + limit: number; + totalPages: number; + page: number; + pagingCounter: number; + hasPrevPage: boolean; + hasNextPage: boolean; + prevPage: number | null; + nextPage: number | null; + }; +}; + +export type ListPayloadMigrationsResponse = ListPayloadMigrationsResponses[keyof ListPayloadMigrationsResponses]; + +export type CreatePayloadMigrationData = { + /** + * Payload Migration + */ + body?: PayloadMigrationRequestBody; + path?: never; + query?: { + depth?: number; + locale?: string; + }; + url: '/api/payload-migrations'; +}; + +export type CreatePayloadMigrationResponses = { + /** + * Payload Migration object + */ + 201: { + message: string; + doc: PayloadMigration & { + id: string; + createdAt: string; + updatedAt: string; + }; + }; +}; + +export type CreatePayloadMigrationResponse = CreatePayloadMigrationResponses[keyof CreatePayloadMigrationResponses]; + +export type DeletePayloadMigrationData = { + body?: never; + path: { + /** + * ID of the Payload Migration + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-migrations/{id}'; +}; + +export type DeletePayloadMigrationErrors = { + /** + * Payload Migration not found + */ + 404: unknown; +}; + +export type DeletePayloadMigrationResponses = { + /** + * Payload Migration object + */ + 200: PayloadMigration; +}; + +export type DeletePayloadMigrationResponse = DeletePayloadMigrationResponses[keyof DeletePayloadMigrationResponses]; + +export type FindPayloadMigrationByIdData = { + body?: never; + path: { + /** + * ID of the Payload Migration + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-migrations/{id}'; +}; + +export type FindPayloadMigrationByIdErrors = { + /** + * Payload Migration not found + */ + 404: unknown; +}; + +export type FindPayloadMigrationByIdResponses = { + /** + * Payload Migration object + */ + 200: PayloadMigration; +}; + +export type FindPayloadMigrationByIdResponse = FindPayloadMigrationByIdResponses[keyof FindPayloadMigrationByIdResponses]; + +export type UpdatePayloadMigrationData = { + /** + * Payload Migration + */ + body?: PayloadMigrationPatchRequestBody; + path: { + /** + * ID of the Payload Migration + */ + id: string; + }; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/payload-migrations/{id}'; +}; + +export type UpdatePayloadMigrationErrors = { + /** + * Payload Migration not found + */ + 404: unknown; +}; + +export type UpdatePayloadMigrationResponses = { + /** + * Payload Migration object + */ + 200: PayloadMigration; +}; + +export type UpdatePayloadMigrationResponse = UpdatePayloadMigrationResponses[keyof UpdatePayloadMigrationResponses]; + +export type GetApiGlobalsHeaderData = { + body?: never; + path?: never; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/globals/header'; +}; + +export type GetApiGlobalsHeaderResponses = { + /** + * Header + */ + 200: HeaderRead; +}; + +export type GetApiGlobalsHeaderResponse = GetApiGlobalsHeaderResponses[keyof GetApiGlobalsHeaderResponses]; + +export type PostApiGlobalsHeaderData = { + /** + * Header + */ + body?: HeaderRequestBody; + path?: never; + query?: never; + url: '/api/globals/header'; +}; + +export type PostApiGlobalsHeaderResponses = { + /** + * Header + */ + 200: HeaderRead; +}; + +export type PostApiGlobalsHeaderResponse = PostApiGlobalsHeaderResponses[keyof PostApiGlobalsHeaderResponses]; + +export type GetApiGlobalsFooterData = { + body?: never; + path?: never; + query?: { + depth?: number; + locale?: string; + 'fallback-locale'?: string; + }; + url: '/api/globals/footer'; +}; + +export type GetApiGlobalsFooterResponses = { + /** + * Footer + */ + 200: FooterRead; +}; + +export type GetApiGlobalsFooterResponse = GetApiGlobalsFooterResponses[keyof GetApiGlobalsFooterResponses]; + +export type PostApiGlobalsFooterData = { + /** + * Footer + */ + body?: FooterRequestBody; + path?: never; + query?: never; + url: '/api/globals/footer'; +}; + +export type PostApiGlobalsFooterResponses = { + /** + * Footer + */ + 200: FooterRead; +}; + +export type PostApiGlobalsFooterResponse = PostApiGlobalsFooterResponses[keyof PostApiGlobalsFooterResponses]; diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx new file mode 100644 index 0000000..2c82004 --- /dev/null +++ b/src/components/Footer.tsx @@ -0,0 +1,250 @@ +import { Link } from 'react-router-dom'; +import { motion } from 'framer-motion'; +import { + Building2, + Phone, + Mail, + MapPin, + Clock, + MessageCircle, + Share2, + Linkedin as LinkedinIcon, + ArrowRight, +} from 'lucide-react'; +import { + COMPANY_INFO, + FOOTER_LINKS, + SOCIAL_MEDIA, +} from '../lib/constants'; + +/** + * Footer 组件 - 企业官网页脚 + */ + +// 动画变体配置 +const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + delayChildren: 0.2, + }, + }, +}; + +const itemVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { duration: 0.5 }, + }, +}; + +// 图标映射 +const iconMap: Record> = { + Wechat: MessageCircle, + Weibo: Share2, + Linkedin: LinkedinIcon, +}; + +/** + * 联系方式项组件 + */ +const ContactItem: React.FC<{ + icon: React.ComponentType<{ size?: number; className?: string }>; + title: string; + content: string; +}> = ({ icon: Icon, title, content }) => ( +
+ +
+

{title}

+

{content}

+
+
+); + +/** + * 链接列组件 + */ +const LinkColumn: React.FC<{ + title: string; + links: Array<{ label: string; path: string }>; +}> = ({ title, links }) => ( +
+

+ {title} +

+
    + {links.map((link) => ( +
  • + + + + {link.label} + + +
  • + ))} +
+
+); + +/** + * Footer 组件 + */ +export const Footer: React.FC = () => { + const currentYear = new Date().getFullYear(); + + return ( +
+ {/* 主内容区域 */} +
+ + {/* 企业信息 */} + + {/* Logo */} + +
+ +
+
+ + 示例集团 + +

Chengyu Group

+
+ + + {/* 企业简介 */} +

+ {COMPANY_INFO.description} +

+ + {/* 联系方式 */} +
+ + + + +
+
+ + {/* 产品服务 */} + + + + + {/* 公司信息 */} + + + + + {/* 社交媒体 */} + +

+ 关注我们 +

+

+ 了解更多企业动态 +

+
+ {SOCIAL_MEDIA.map((social) => { + const Icon = iconMap[social.icon] || MessageCircle; + return ( + + + {social.label} + + ); + })} +
+
+
+
+ + {/* 底部版权栏 */} +
+
+
+ {/* 版权信息 */} + + © {currentYear} {COMPANY_INFO.fullName} 版权所有 + + + {/* 备案和链接 */} + + 京ICP备XXXXXXXX号 + | + + 隐私政策 + + | + + 使用条款 + + +
+
+
+
+ ); +}; + +export default Footer; diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..a965e61 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,296 @@ +import { useState, useEffect, useCallback } from 'react'; +import { Link, useLocation } from 'react-router-dom'; +import { motion, AnimatePresence } from 'framer-motion'; +import { Menu, X, Building2, Phone, Mail, Search, ChevronDown } from 'lucide-react'; +import { NAVIGATION_MENU } from '../lib/constants'; + +/** + * Header 组件 - 企业官网导航栏 + */ +export const Header: React.FC = () => { + const [isScrolled, setIsScrolled] = useState(false); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + const location = useLocation(); + + // 监听滚动事件 + useEffect(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > 20); + }; + + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + // 关闭移动端菜单 - 使用回调避免直接 setState + const closeMobileMenu = useCallback(() => { + setIsMobileMenuOpen(false); + }, []); + + useEffect(() => { + closeMobileMenu(); + }, [location.pathname, closeMobileMenu]); + + // 检查当前路径是否为活动状态 + const isActive = (path: string): boolean => { + if (path === '/') { + return location.pathname === '/'; + } + return location.pathname.startsWith(path); + }; + + return ( + <> + {/* 顶部信息栏 - 玻璃效果 */} + +
+
+
+ + 400-123-4567 +
+
+ + contact@chengyu.com +
+
+
+ 欢迎来到示例集团官网 +
+ | + + / + +
+
+
+
+ + {/* 主导航栏 */} + +
+
+ {/* Logo 区域 - 增强版 */} + + + + {/* 光晕效果 */} +
+ +
+ + 示例集团 + +

CHENGYU GROUP

+
+ + + {/* 桌面端导航菜单 - 增强版 */} + + + {/* 桌面端操作区域 */} +
+ {/* 搜索按钮 */} + + + + + {/* 联系电话按钮 */} + + + 400-123-4567 + + + {/* CTA 按钮 - 渐变风格 */} + +
+
+ + 立即咨询 + + → + + + +
+ + {/* 移动端菜单按钮 */} + setIsMobileMenuOpen(!isMobileMenuOpen)} + aria-label={isMobileMenuOpen ? '关闭菜单' : '打开菜单'} + aria-expanded={isMobileMenuOpen} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.95 }} + > + + {isMobileMenuOpen ? : } + + +
+
+ + {/* 移动端菜单 - 玻璃效果版 */} + + {isMobileMenuOpen && ( + +
+ {NAVIGATION_MENU.map((item, index) => ( + + +
+ {item.label} +
+ +
+ ))} + + {/* 移动端联系方式 */} + + + + 400-123-4567 + + + + 立即咨询 + + +
+
+ )} +
+ + + ); +}; + +export default Header; diff --git a/src/components/Hero.tsx b/src/components/Hero.tsx new file mode 100644 index 0000000..d25374c --- /dev/null +++ b/src/components/Hero.tsx @@ -0,0 +1,296 @@ +import { useEffect, useRef } from 'react'; +import { motion, useInView, useAnimation } from 'framer-motion'; +import { ArrowDown, TrendingUp, Users, Building2, Award } from 'lucide-react'; +import { COMPANY_INFO, COMPANY_STATS } from '../lib/constants'; + +/** + * 数字递增动画组件 + */ +const CountUpNumber: React.FC<{ + value: string; + suffix?: string; + duration?: number; +}> = ({ value, suffix = '', duration = 2 }) => { + const ref = useRef(null); + const isInView = useInView(ref, { once: true }); + const controls = useAnimation(); + + useEffect(() => { + if (isInView && ref.current) { + const numValue = parseInt(value.replace(/[^0-9]/g, '')); + const hasPlus = value.includes('+'); + const hasYi = value.includes('亿'); + + controls.start({ + opacity: [0, 1], + scale: [0.5, 1.2, 1], + }); + + // 数字递增动画 + let current = 0; + const increment = numValue / (duration * 60); + const timer = setInterval(() => { + current += increment; + if (current >= numValue) { + current = numValue; + clearInterval(timer); + } + let display = Math.floor(current); + if (hasYi) { + display = Math.floor(current); + } + ref.current.textContent = display.toLocaleString() + (hasPlus ? '+' : '') + (hasYi ? '亿' : suffix); + }, 1000 / 60); + + return () => clearInterval(timer); + } + }, [isInView, value, suffix, duration, controls]); + + return ( + + 0 + + ); +}; + +/** + * 统计数据项组件 + */ +const StatItem: React.FC<{ + icon: React.ComponentType<{ size?: number; className?: string }>; + label: string; + value: string; + suffix?: string; +}> = ({ icon: Icon, label, value, suffix }) => ( + +
+ +
+
+
+ +
+

{label}

+
+
+); + +/** + * Hero 组件 - 首页大图区域 + */ +export const Hero: React.FC = () => { + const containerRef = useRef(null); + const statsRef = useRef(null); + + // 滚动到指定区域 + const scrollToSection = (sectionId: string) => { + const element = document.getElementById(sectionId); + if (element) { + const offset = 80; + const elementPosition = element.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - offset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth', + }); + } + }; + + return ( +
+ {/* 背景层 */} +
+ {/* 图片背景 */} +
+ {/* 渐变遮罩 - 确保文字可读性 */} +
+ {/* 装饰性图案 */} +
+
+
+
+ {/* 网格图案 */} +
+
+ + {/* 主内容区域 */} +
+
+ {/* 左侧内容 */} + + {/* 企业标语 */} + + {COMPANY_INFO.slogan} + + + {/* 企业名称 */} + + {COMPANY_INFO.fullName} + + + {/* 企业简介 */} + + {COMPANY_INFO.description} + + + {/* CTA 按钮组 */} + + scrollToSection('about')} + className="inline-flex items-center gap-2 px-8 py-4 bg-accent text-primary-dark font-semibold rounded-lg shadow-lg shadow-accent/25 hover:shadow-xl hover:shadow-accent/30 transition-all duration-300" + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.98 }} + > + 了解更多 + + + scrollToSection('contact')} + className="inline-flex items-center gap-2 px-8 py-4 border-2 border-white/30 text-white font-semibold rounded-lg hover:bg-white/10 transition-all duration-300" + whileHover={{ scale: 1.05, borderColor: 'rgba(255,255,255,0.5)' }} + whileTap={{ scale: 0.98 }} + > + 联系我们 + + + + + {/* 右侧统计数据 */} + + + 核心数据 + + +
+ {/* 成立年限 */} + + {/* 员工数量 */} + + {/* 服务客户 */} + + {/* 管理资产 */} + +
+ + {/* 额外说明 */} + + 数据截至 2025 年 12 月 + +
+
+
+ + {/* 底部装饰 */} + + + {/* 滚动提示 */} + + + +
+ ); +}; + +export default Hero; diff --git a/src/components/Home/AboutSection.tsx b/src/components/Home/AboutSection.tsx new file mode 100644 index 0000000..c09260c --- /dev/null +++ b/src/components/Home/AboutSection.tsx @@ -0,0 +1,188 @@ +import { motion } from 'framer-motion'; +import { Target, Eye, Heart, Award, ArrowRight } from 'lucide-react'; +import { Link } from 'react-router-dom'; + +/** + * AboutSection 组件 - 关于我们简介区域 + */ + +const values = [ + { + icon: Heart, + title: '诚信为本', + description: '坚守诚信底线,建立长期信任关系', + }, + { + icon: Target, + title: '创新驱动', + description: '持续创新,保持行业领先优势', + }, + { + icon: Award, + title: '品质至上', + description: '追求卓越,提供高品质服务', + }, +]; + +export const AboutSection: React.FC = () => { + return ( +
+
+ {/* 标题区域 */} + +

+ 关于示例集团 +

+

+ 示例集团成立于 2010 年,是一家集科技研发、金融服务、产业投资于一体的综合性企业集团 +

+
+ + {/* 主要内容区域 */} +
+ {/* 左侧内容 */} + +

+ 示例集团 · 稳健前行,携手共赢 +

+
+

+ 示例集团总部位于北京,经过十余年的稳健发展,已形成了以金融服务、科技研发、产业投资为核心的业务体系。集团秉承"诚信、创新、共赢"的核心价值观,致力于为客户创造最大价值。 +

+

+ 我们拥有一支经验丰富、专业高效的管理团队,汇聚了金融、科技、投资等领域的优秀人才。通过持续的业务创新和服务优化,示例集团已成功为超过 1000 家企业客户提供专业服务。 +

+
+ + {/* 使命与愿景 */} +
+
+
+
+ +
+

我们的愿景

+
+

+ 成为具有国际影响力的综合性企业集团 +

+
+
+
+
+ +
+

我们的使命

+
+

+ 为客户创造价值,为社会贡献力量 +

+
+
+ + {/* 更多按钮 */} + + + 了解更多关于我们 + + + +
+ + {/* 右侧图片/装饰 */} + + {/* 主图片区域 */} +
+ 示例集团办公室环境 + {/* 叠加装饰层 */} +
+
+ + {/* 装饰性元素 */} + + + +
+ + {/* 核心价值观 */} + +

+ 核心价值观 +

+
+ {values.map((value, index) => ( + +
+ +
+

+ {value.title} +

+

{value.description}

+
+ ))} +
+
+
+
+ ); +}; + +export default AboutSection; diff --git a/src/components/Home/NewsSection.tsx b/src/components/Home/NewsSection.tsx new file mode 100644 index 0000000..c82a63f --- /dev/null +++ b/src/components/Home/NewsSection.tsx @@ -0,0 +1,183 @@ +import { motion } from 'framer-motion'; +import { Calendar, ArrowRight } from 'lucide-react'; +import { Link } from 'react-router-dom'; +import { formatDate } from '../../lib/utils'; + +/** + * NewsSection 组件 - 最新动态区域 + */ + +// 模拟新闻数据 +const newsItems = [ + { + id: 1, + category: 'company', + title: '示例集团荣获"2025年度优秀企业"称号', + excerpt: '在近日举办的年度企业评选活动中,示例集团凭借其卓越的经营业绩和社会责任表现,荣获"2025年度优秀企业"称号。', + date: '2025-12-20', + image: '/images/news-award.jpg', + }, + { + id: 2, + category: 'industry', + title: '金融科技创新论坛圆满落幕,示例集团分享行业洞察', + excerpt: '示例集团受邀参加金融科技创新论坛,与行业专家共同探讨金融科技发展趋势,分享公司在数字化转型方面的实践经验。', + date: '2025-12-15', + image: '/images/news-tech.jpg', + }, + { + id: 3, + category: 'achievement', + title: '示例集团完成新一轮战略融资,估值突破百亿', + excerpt: '示例集团宣布完成新一轮战略融资,本轮融资由知名投资机构领投,估值突破百亿元人民币,标志着公司发展进入新阶段。', + date: '2025-12-10', + image: '/images/news-company.jpg', + }, +]; + +// 新闻分类映射 +const categoryMap: Record = { + company: { label: '公司动态', color: 'bg-primary/10 text-primary' }, + industry: { label: '行业资讯', color: 'bg-accent/20 text-accent-dark' }, + achievement: { label: '荣誉资质', color: 'bg-green-100 text-green-700' }, +}; + +/** + * 新闻卡片组件 + */ +const NewsCard: React.FC<{ + news: typeof newsItems[0]; + index: number; +}> = ({ news, index }) => { + const category = categoryMap[news.category] || categoryMap.company; + + return ( + + {/* 图片区域 */} +
+ {news.image ? ( + + ) : ( +
+ + + +
+ )} + {/* 分类标签 */} + + {category.label} + + {/* 图片遮罩 */} +
+
+ + {/* 内容区域 */} +
+ {/* 日期 */} +
+ + +
+ + {/* 标题 */} +

+ {news.title} +

+ + {/* 摘要 */} +

+ {news.excerpt} +

+ + {/* 了解更多链接 */} + + 阅读全文 + + + + +
+ + ); +}; + +export const NewsSection: React.FC = () => { + return ( +
+
+ {/* 标题区域 */} + +
+

+ 新闻资讯 +

+

+ 了解示例集团最新动态 +

+
+ + 查看更多新闻 + + +
+ + {/* 新闻卡片网格 */} +
+ {newsItems.map((news, index) => ( + + ))} +
+
+
+ ); +}; + +export default NewsSection; diff --git a/src/components/Home/ServicesSection.tsx b/src/components/Home/ServicesSection.tsx new file mode 100644 index 0000000..c8c44a4 --- /dev/null +++ b/src/components/Home/ServicesSection.tsx @@ -0,0 +1,135 @@ +import { motion } from 'framer-motion'; +import { TrendingUp, Cpu, Building2, Briefcase, ArrowRight } from 'lucide-react'; +import { Link } from 'react-router-dom'; +import { SERVICES } from '../../lib/constants'; + +/** + * ServicesSection 组件 - 核心业务展示区域 + */ + +// 业务卡片组件 +const ServiceCard: React.FC<{ + service: typeof SERVICES[0]; + index: number; +}> = ({ service, index }) => { + const iconMap: Record> = { + TrendingUp, + Cpu, + Building2, + Briefcase, + }; + + const Icon = iconMap[service.icon] || TrendingUp; + + return ( + + {/* 背景装饰 */} +
+ + {/* 图标 */} +
+ +
+ + {/* 内容 */} +

+ {service.title} +

+

+ {service.description} +

+ + {/* 特性列表 */} +
    + {service.features.map((feature) => ( +
  • + + {feature} +
  • + ))} +
+ + {/* 了解更多链接 */} + + 了解更多 + + + + + + {/* 悬停边框效果 */} +
+ + ); +}; + +export const ServicesSection: React.FC = () => { + return ( +
+
+ {/* 标题区域 */} + +

+ 核心业务 +

+

+ 我们提供全方位的专业服务,帮助客户实现商业目标 +

+
+ + {/* 业务卡片网格 */} +
+ {SERVICES.map((service, index) => ( + + ))} +
+ + {/* 查看全部服务 */} + + + 查看全部服务 + + + +
+
+ ); +}; + +export default ServicesSection; diff --git a/src/components/PostCard.tsx b/src/components/PostCard.tsx new file mode 100644 index 0000000..9ce12fc --- /dev/null +++ b/src/components/PostCard.tsx @@ -0,0 +1,41 @@ +import React from 'react' + +interface PostCardProps { + title: string + excerpt: string + category?: string + date: string + onClick?: () => void +} + +export const PostCard: React.FC = ({ + title, + excerpt, + category, + date, + onClick, +}) => { + return ( +
+
+ {category && ( + + {category} + + )} + {date} +
+

{title}

+

{excerpt}

+
+ 阅读更多 + + + +
+
+ ) +} diff --git a/src/components/PostCardSkeleton.tsx b/src/components/PostCardSkeleton.tsx new file mode 100644 index 0000000..b9753fd --- /dev/null +++ b/src/components/PostCardSkeleton.tsx @@ -0,0 +1,19 @@ +import React from 'react' + +export const PostCardSkeleton: React.FC = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+ ) +} diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..7c818df --- /dev/null +++ b/src/config.ts @@ -0,0 +1,3 @@ +export const TENANT_SLUG = "zitadel-example" +export const TENANT_API_KEY = "tenant_new-tenant_jau52FifQXXfnPufibP4NXXu54tHbWRQ5cEdh27j" +export const API_URL = "http://localhost:3000" diff --git a/src/env.ts b/src/env.ts new file mode 100644 index 0000000..07bd7be --- /dev/null +++ b/src/env.ts @@ -0,0 +1,5 @@ +export const ENV = { + VITE_TENANT_SLUG: "zitadel-example", + VITE_TENANT_API_KEY: "tenant_new-tenant_jau52FifQXXfnPufibP4NXXu54tHbWRQ5cEdh27j", + VITE_API_URL: "http://localhost:3000/api", +} diff --git a/src/hooks/usePageTitle.ts b/src/hooks/usePageTitle.ts new file mode 100644 index 0000000..f2b556f --- /dev/null +++ b/src/hooks/usePageTitle.ts @@ -0,0 +1,18 @@ +import { useEffect } from 'react'; + +/** + * 自定义 Hook - 设置页面标题 + * @param title 页面标题 + * @param suffix 标题后缀,默认为 "示例集团" + */ +export const usePageTitle = (title: string, suffix: string = '示例集团') => { + useEffect(() => { + const prevTitle = document.title; + document.title = title ? `${title} - ${suffix}` : suffix; + + // 清理函数:组件卸载时恢复默认标题 + return () => { + document.title = prevTitle; + }; + }, [title, suffix]); +}; diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..5f6a972 --- /dev/null +++ b/src/index.css @@ -0,0 +1,45 @@ +@import "tailwindcss"; + +@plugin "@tailwindcss/typography"; + +@theme { + --color-primary: #1e3a8a; + --color-primary-light: #2563eb; + --color-primary-dark: #1e293b; + --color-accent: #d4af37; + --color-accent-light: #e5c158; + --color-accent-dark: #b8960c; + --color-background: #f8fafc; + --color-background-light: #ffffff; + --color-background-dark: #f1f5f9; +} + +body { + margin: 0; + min-height: 100vh; + background-color: var(--color-background); + color: var(--color-primary-dark); + font-family: 'Inter', system-ui, sans-serif; +} + +html { + scroll-behavior: smooth; +} + +/* 自定义滚动条样式 */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-background-dark); +} + +::-webkit-scrollbar-thumb { + background: var(--color-primary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-primary-light); +} diff --git a/src/lib/constants.ts b/src/lib/constants.ts new file mode 100644 index 0000000..5a758b4 --- /dev/null +++ b/src/lib/constants.ts @@ -0,0 +1,177 @@ +/** + * 示例集团企业官网 - 常量定义 + */ + +// 企业基本信息 +export const COMPANY_INFO = { + name: '示例集团', + nameEn: 'Chengyu Group', + slogan: '稳健前行,携手共赢', + description: '示例集团成立于2010年,是一家集科技研发、金融服务、产业投资于一体的综合性企业集团。秉承"诚信、创新、共赢"的经营理念,致力于为客户提供高品质的产品和服务。', + fullName: '示例集团有限公司', + registrationNumber: '91110000XXXXXXXX', + established: '2010年', + headquarters: '北京市朝阳区建国路88号', + phone: '400-888-8888', + email: 'contact@chengyu-group.com', + workingHours: '周一至周五 9:00-18:00', +}; + +// 导航菜单配置 +export const NAVIGATION_MENU = [ + { id: 'home', label: '首页', path: '/' }, + { id: 'about', label: '关于我们', path: '/about' }, + { id: 'services', label: '产品服务', path: '/services' }, + { id: 'news', label: '新闻资讯', path: '/news' }, + { id: 'contact', label: '联系我们', path: '/contact' }, +]; + +// 底部导航链接 +export const FOOTER_LINKS = { + products: [ + { label: '金融服务', path: '/services/finance' }, + { label: '科技研发', path: '/services/tech' }, + { label: '产业投资', path: '/services/investment' }, + { label: '咨询服务', path: '/services/consulting' }, + ], + company: [ + { label: '关于我们', path: '/about' }, + { label: '新闻资讯', path: '/news' }, + { label: '招贤纳士', path: '/careers' }, + { label: '联系我们', path: '/contact' }, + ], + legal: [ + { label: '隐私政策', path: '/privacy' }, + { label: '使用条款', path: '/terms' }, + { label: '免责声明', path: '/disclaimer' }, + ], +}; + +// 社交媒体链接 +export const SOCIAL_MEDIA = [ + { + id: 'wechat', + label: '微信公众号', + icon: 'Wechat', + url: 'https://weixin.qq.com', + description: '示例集团官方微信公众号', + }, + { + id: 'weibo', + label: '官方微博', + icon: 'Weibo', + url: 'https://weibo.com', + description: '示例集团官方微博账号', + }, + { + id: 'linkedin', + label: 'LinkedIn', + icon: 'Linkedin', + url: 'https://linkedin.com/company/chengyu-group', + description: '示例集团 LinkedIn 主页', + }, +]; + +// 服务项目配置 +export const SERVICES = [ + { + id: 'finance', + title: '金融服务', + description: '提供专业的财富管理、投资顾问、资产配置等金融服务,为客户创造稳健收益。', + icon: 'TrendingUp', + features: ['财富管理', '投资顾问', '资产配置', '风险管理'], + }, + { + id: 'tech', + title: '科技研发', + description: '聚焦人工智能、大数据、云计算等前沿技术,为企业提供数字化转型解决方案。', + icon: 'Cpu', + features: ['人工智能', '大数据分析', '云计算服务', '数字化转型'], + }, + { + id: 'investment', + title: '产业投资', + description: '专注于新兴产业投资机会,通过战略投资推动产业升级和价值创造。', + icon: 'Building2', + features: ['战略投资', '产业并购', '创业孵化', '退出管理'], + }, + { + id: 'consulting', + title: '咨询服务', + description: '为企业提供战略规划、运营优化、风险管理等专业咨询服务。', + icon: 'Briefcase', + features: ['战略规划', '运营优化', '风险管理', '组织变革'], + }, +]; + +// 新闻分类 +export const NEWS_CATEGORIES = [ + { id: 'all', label: '全部' }, + { id: 'company', label: '公司动态' }, + { id: 'industry', label: '行业资讯' }, + { id: 'achievement', label: '荣誉资质' }, +]; + +// 首页统计数据 +export const COMPANY_STATS = [ + { id: 'years', label: '成立年限', value: '15', suffix: '年' }, + { id: 'employees', label: '员工数量', value: '500', suffix: '+' }, + { id: 'clients', label: '服务客户', value: '1000', suffix: '+' }, + { id: 'assets', label: '管理资产', value: '500', suffix: '亿' }, +]; + +// 页面元信息 +export const PAGE_META = { + home: { + title: '示例集团 - 稳健前行,携手共赢', + description: '示例集团是一家集科技研发、金融服务、产业投资于一体的综合性企业集团', + }, + about: { + title: '关于我们 - 示例集团', + description: '了解示例集团的发展历程、企业文化和核心价值观', + }, + services: { + title: '产品服务 - 示例集团', + description: '提供金融服务、科技研发、产业投资、咨询管理等专业服务', + }, + news: { + title: '新闻资讯 - 示例集团', + description: '了解示例集团最新动态、行业资讯和荣誉资质', + }, + contact: { + title: '联系我们 - 示例集团', + description: '获取示例集团联系方式,欢迎随时与我们沟通', + }, +}; + +// 联系方式配置 +export const CONTACT_INFO = [ + { + id: 'address', + type: 'address', + icon: 'MapPin', + title: '总部地址', + content: COMPANY_INFO.headquarters, + }, + { + id: 'phone', + type: 'phone', + icon: 'Phone', + title: '服务热线', + content: COMPANY_INFO.phone, + }, + { + id: 'email', + type: 'email', + icon: 'Mail', + title: '商务邮箱', + content: COMPANY_INFO.email, + }, + { + id: 'hours', + type: 'text', + icon: 'Clock', + title: '工作时间', + content: COMPANY_INFO.workingHours, + }, +]; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..3e12bc2 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,273 @@ +/** + * 示例集团企业官网 - 工具函数 + */ + +/** + * 日期格式化函数 + * @param dateString - ISO 日期字符串 + * @param format - 格式化模板,默认为 'YYYY-MM-DD' + * @returns 格式化后的日期字符串 + */ +export function formatDate(dateString: string, format: 'YYYY-MM-DD' | 'YYYY年MM月DD日' | 'MM/DD/YYYY' = 'YYYY-MM-DD'): string { + const date = new Date(dateString); + + if (isNaN(date.getTime())) { + return ''; + } + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + + switch (format) { + case 'YYYY年MM月DD日': + return `${year}年${String(month).padStart(2, '0')}月${String(day).padStart(2, '0')}日`; + case 'MM/DD/YYYY': + return `${String(month).padStart(2, '0')}/${String(day).padStart(2, '0')}/${year}`; + case 'YYYY-MM-DD': + default: + return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; + } +} + +/** + * 文本截取函数(按字符数) + * @param text - 原始文本 + * @param maxLength - 最大字符数 + * @param suffix - 截取后添加的后缀,默认为 '...' + * @returns 截取后的文本 + */ +export function truncateText(text: string, maxLength: number, suffix: string = '...'): string { + if (!text) return ''; + if (text.length <= maxLength) return text; + return text.slice(0, maxLength - suffix.length) + suffix; +} + +/** + * 文本截取函数(按单词数,适用于英文) + * @param text - 原始文本 + * @param maxWords - 最大单词数 + * @param suffix - 截取后添加的后缀,默认为 '...' + * @returns 截取后的文本 + */ +export function truncateWords(text: string, maxWords: number, suffix: string = '...'): string { + if (!text) return ''; + const words = text.split(/\s+/); + if (words.length <= maxWords) return text; + return words.slice(0, maxWords).join(' ') + suffix; +} + +/** + * 生成随机ID + * @param prefix - ID 前缀,默认为 'id' + * @returns 生成的随机 ID + */ +export function generateId(prefix: string = 'id'): string { + return `${prefix}-${Math.random().toString(36).substring(2, 11)}`; +} + +/** + * 延迟函数 + * @param ms - 延迟毫秒数 + * @returns Promise + */ +export function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +/** + * 滚动到指定元素 + * @param elementId - 元素 ID + * @param offset - 偏移量(像素) + */ +export function scrollToElement(elementId: string, offset: number = 80): void { + const element = document.getElementById(elementId); + if (element) { + const elementPosition = element.getBoundingClientRect().top; + const offsetPosition = elementPosition + window.pageYOffset - offset; + + window.scrollTo({ + top: offsetPosition, + behavior: 'smooth', + }); + } +} + +/** + * 检查是否在客户端环境 + * @returns 是否在浏览器环境中 + */ +export function isClient(): boolean { + return typeof window !== 'undefined'; +} + +/** + * 获取 URL 查询参数 + * @param name - 参数名 + * @returns 参数值或 null + */ +export function getQueryParam(name: string): string | null { + if (!isClient()) return null; + const urlParams = new URLSearchParams(window.location.search); + return urlParams.get(name); +} + +/** + * 数字格式化函数 + * @param num - 数字 + * @param locale - 地区设置,默认为 'zh-CN' + * @returns 格式化后的字符串 + */ +export function formatNumber(num: number, locale: string = 'zh-CN'): string { + return new Intl.NumberFormat(locale).format(num); +} + +/** + * 字节单位转换 + * @param bytes - 字节数 + * @param decimals - 小数位数,默认为 2 + * @returns 格式化后的字符串 + */ +export function formatBytes(bytes: number, decimals: number = 2): string { + if (bytes === 0) return '0 Bytes'; + + const k = 1024; + const dm = decimals < 0 ? 0 : decimals; + const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; + + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i]; +} + +/** + * 驼峰命名转短横线命名 + * @param str - 驼峰命名字符串 + * @returns 短横线命名字符串 + */ +export function camelToKebab(str: string): string { + return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); +} + +/** + * 短横线命名转驼峰命名 + * @param str - 短横线命名字符串 + * @returns 驼峰命名字符串 + */ +export function kebabToCamel(str: string): string { + return str.replace(/-([a-z])/g, (g) => g[1].toUpperCase()); +} + +/** + * 首字母大写 + * @param str - 输入字符串 + * @returns 首字母大写后的字符串 + */ +export function capitalizeFirst(str: string): string { + if (!str) return ''; + return str.charAt(0).toUpperCase() + str.slice(1); +} + +/** + * 移除字符串中的 HTML 标签 + * @param html - 包含 HTML 标签的字符串 + * @returns 纯文本字符串 + */ +export function stripHtml(html: string): string { + if (!html) return ''; + return html.replace(/<[^>]*>/g, ''); +} + +/** + * 生成页面 SEO 元数据 + * @param title - 页面标题 + * @param description - 页面描述 + * @param keywords - 关键词 + * @returns 元数据对象数组 + */ +export function generateSeoMeta( + title: string, + description: string, + keywords: string[] = [] +): Array<{ title: string; name: string; content: string }> { + return [ + { title, name: '', content: title }, + { title: '', name: 'description', content: description }, + { title: '', name: 'keywords', content: keywords.join(', ') }, + ]; +} + +/** + * 检查对象是否为空 + * @param obj - 要检查的对象 + * @returns 是否为空 + */ +export function isEmpty(obj: object): boolean { + if (obj === null || obj === undefined) return true; + if (Array.isArray(obj)) return obj.length === 0; + if (typeof obj === 'object') return Object.keys(obj).length === 0; + return false; +} + +/** + * 深度合并对象 + * @param target - 目标对象 + * @param sources - 源对象数组 + * @returns 合并后的对象 + */ +export function deepMerge(target: T, ...sources: Partial[]): T { + if (!sources.length) return target; + const source = sources.shift(); + + if (source && typeof source === 'object') { + for (const key in source) { + if (source[key] && typeof source[key] === 'object') { + if (!target[key]) Object.assign(target, { [key]: {} }); + deepMerge(target[key], source[key]); + } else { + Object.assign(target, { [key]: source[key] }); + } + } + } + + return deepMerge(target, ...sources); +} + +/** + * 防抖函数 + * @param func - 要防抖的函数 + * @param wait - 等待时间(毫秒) + * @returns 防抖后的函数 + */ +export function debounce unknown>( + func: T, + wait: number +): (...args: Parameters) => void { + let timeout: NodeJS.Timeout | null = null; + + return (...args: Parameters) => { + if (timeout) clearTimeout(timeout); + timeout = setTimeout(() => func(...args), wait); + }; +} + +/** + * 节流函数 + * @param func - 要节流的函数 + * @param limit - 时间限制(毫秒) + * @returns 节流后的函数 + */ +export function throttle unknown>( + func: T, + limit: number +): (...args: Parameters) => void { + let inThrottle = false; + + return (...args: Parameters) => { + if (!inThrottle) { + func(...args); + inThrottle = true; + setTimeout(() => (inThrottle = false), limit); + } + }; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/pages/About.tsx b/src/pages/About.tsx new file mode 100644 index 0000000..9191323 --- /dev/null +++ b/src/pages/About.tsx @@ -0,0 +1,446 @@ +import { motion } from 'framer-motion'; +import { useRef } from 'react'; +import { + Target, + Heart, + Award, + Users, + Calendar, + TrendingUp, + Building2, + Globe, +} from 'lucide-react'; +import { Header } from '../components/Header'; +import { Footer } from '../components/Footer'; +import { COMPANY_INFO } from '../lib/constants'; +import { usePageTitle } from '../hooks/usePageTitle'; + +// 发展历程数据 +const milestones = [ + { + year: '2010', + title: '公司成立', + description: '示例集团在北京成立,开始布局金融服务业务', + icon: Building2, + }, + { + year: '2012', + title: '首次战略融资', + description: '完成 A 轮融资,获得知名投资机构认可', + icon: TrendingUp, + }, + { + year: '2015', + title: '业务扩展', + description: '成立科技研发子公司,进军科技领域', + icon: Globe, + }, + { + year: '2018', + title: '规模扩张', + description: '员工规模突破 200 人,服务客户超过 500 家', + icon: Users, + }, + { + year: '2020', + title: '产业投资布局', + description: '成立产业投资基金,全面进入投资领域', + icon: Target, + }, + { + year: '2023', + title: '集团化运营', + description: '正式更名为示例集团,形成多元化业务体系', + icon: Award, + }, + { + year: '2025', + title: '新里程碑', + description: '管理资产突破 500 亿,员工规模超过 500 人', + icon: Calendar, + }, +]; + +// 核心价值观数据 +const coreValues = [ + { + icon: Heart, + title: '诚信为本', + description: '诚信是企业发展的基石,我们始终坚守诚信底线,与客户、合作伙伴建立长期信任关系', + color: 'from-red-500 to-red-600', + }, + { + icon: Target, + title: '创新驱动', + description: '创新是企业发展的动力,我们持续投入研发,不断推出创新产品和服务', + color: 'from-blue-500 to-blue-600', + }, + { + icon: Award, + title: '卓越品质', + description: '品质是企业生存的根本,我们追求卓越,确保每一个项目都达到最高标准', + color: 'from-yellow-500 to-yellow-600', + }, + { + icon: Users, + title: '共赢合作', + description: '合作是企业成功的关键,我们与客户、员工、合作伙伴实现互利共赢', + color: 'from-green-500 to-green-600', + }, +]; + +// 团队成员数据 +const teamMembers = [ + { + name: '张明远', + position: '董事长兼 CEO', + bio: '毕业于清华大学金融系,拥有 20 年金融行业经验,曾任多家知名金融机构高管。', + }, + { + name: '李晓峰', + position: '首席财务官 CFO', + bio: '持有注册会计师资格,曾在四大会计师事务所工作 15 年,专业财务管理和资本运作专家。', + }, + { + name: '王建华', + position: '首席技术官 CTO', + bio: '计算机科学博士,曾在国内外知名科技公司担任技术负责人,拥有多项技术专利。', + }, + { + name: '陈静雅', + position: '首席运营官 COO', + bio: 'MBA 学位,拥有丰富的企业运营管理经验,擅长战略规划和流程优化。', + }, +]; + +// 荣誉资质数据 +const honors = [ + { name: '国家级高新技术企业认证', year: '2020' }, + { name: '北京市优秀企业', year: '2021' }, + { name: '中国最佳雇主品牌', year: '2022' }, + { name: '金融科技创新奖', year: '2023' }, + { name: '年度优秀企业', year: '2024' }, + { name: 'ESG 最佳实践奖', year: '2025' }, +]; + +/** + * About 组件 - 关于我们页面 + */ +export const About: React.FC = () => { + const timelineRef = useRef(null); + usePageTitle('关于我们'); + + return ( + + {/* 顶部导航 */} +
+ + {/* 主内容 */} +
+ {/* 页面标题区域 */} +
+
+ +

+ 关于示例集团 +

+

+ 示例集团成立于 2010 年,是一家集科技研发、金融服务、产业投资于一体的综合性企业集团 +

+
+
+
+ + {/* 公司简介 */} +
+
+
+ +

+ 公司简介 +

+
+

+ {COMPANY_INFO.fullName} + 成立于 {COMPANY_INFO.established},总部位于 {COMPANY_INFO.headquarters}。 + 经过十余年的稳健发展,示例集团已形成了以金融服务、科技研发、产业投资为核心的多元化业务体系。 +

+

+ 集团秉承"诚信、创新、共赢"的核心价值观,致力于为客户创造最大价值。 + 我们拥有一支经验丰富、专业高效的管理团队,汇聚了金融、科技、投资等领域的优秀人才。 +

+

+ 截至目前,示例集团已成功为超过 1000 家企业客户提供专业服务, + 管理资产规模突破 500 亿元人民币,业务范围覆盖全国主要城市。 +

+
+ + {/* 核心数据 */} +
+
+
15+
+
年发展历程
+
+
+
500+
+
员工人数
+
+
+
1000+
+
服务客户
+
+
+
+ + +
+ 示例集团办公环境 +
+
+
示例集团
+
稳健前行 · 携手共赢
+
+
+
+ {/* 装饰元素 */} +
+
+ +
+
+
+ + {/* 发展历程时间线 */} +
+
+ +

+ 发展历程 +

+

+ 十余年稳健发展,见证示例成长 +

+
+ + {/* 时间线 */} +
+ {/* 中轴线 */} +
+ + {/* 时间线项目 */} +
+ {milestones.map((milestone, index) => ( + + {/* 内容区域 */} +
+
+ + {milestone.year} + +

+ {milestone.title} +

+

{milestone.description}

+
+
+ + {/* 中间图标 */} +
+ +
+ + {/* 空白区域 */} +
+ + ))} +
+
+
+
+ + {/* 企业文化 */} +
+
+ +

企业文化

+

+ 核心价值观驱动企业发展 +

+
+ +
+ {coreValues.map((value, index) => ( + +
+ +
+

{value.title}

+

+ {value.description} +

+
+ ))} +
+
+
+ + {/* 团队介绍 */} +
+
+ +

+ 管理团队 +

+

+ 汇聚行业精英,共创企业未来 +

+
+ +
+ {teamMembers.map((member, index) => ( + + {/* 团队成员头像 */} +
+
+ {member.name.charAt(0)} +
+
+

+ {member.name} +

+

{member.position}

+

+ {member.bio} +

+
+ ))} +
+
+
+ + {/* 资质荣誉 */} +
+
+ +

+ 资质荣誉 +

+

+ 行业认可,品质保证 +

+
+ +
+ {honors.map((honor, index) => ( + +
+ +
+
+

+ {honor.name} +

+

获得年份:{honor.year}

+
+
+ ))} +
+
+
+
+ + {/* 页脚 */} +