Compare commits
64 Commits
3.0.0-beta
...
v3.0.0-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a54aa7776 | ||
|
|
4fddea86eb | ||
|
|
547acfe876 | ||
|
|
56c6700cf2 | ||
|
|
a352ebc552 | ||
|
|
312b4a94c3 | ||
|
|
cb3dbed127 | ||
|
|
3a73a9696f | ||
|
|
d587441e80 | ||
|
|
fcfc3c593f | ||
|
|
baf945b1ea | ||
|
|
4f9d78df5e | ||
|
|
f07783a279 | ||
|
|
eeddeceda9 | ||
|
|
4c832ad984 | ||
|
|
81bc777dfe | ||
|
|
a757635bc7 | ||
|
|
80bf0a3067 | ||
|
|
a06c06415c | ||
|
|
2dd7e82fdc | ||
|
|
63e04e2ae0 | ||
|
|
e71888a625 | ||
|
|
c5514f1441 | ||
|
|
23e2fe643e | ||
|
|
bdef2f1bc7 | ||
|
|
8f03cd7c78 | ||
|
|
c0092191a6 | ||
|
|
b9854ed60a | ||
|
|
576ee14976 | ||
|
|
bf77cec7e9 | ||
|
|
ab8b2f3fb8 | ||
|
|
db5f3f3ccd | ||
|
|
cece39957f | ||
|
|
157fff0417 | ||
|
|
88e113a545 | ||
|
|
d33afe48fe | ||
|
|
e76df32f09 | ||
|
|
b068f30f51 | ||
|
|
82bd5c656f | ||
|
|
afe8992ca6 | ||
|
|
48a410e294 | ||
|
|
82b88a315f | ||
|
|
cc94078607 | ||
|
|
1c30ad73b6 | ||
|
|
d9442dcce3 | ||
|
|
de92c50847 | ||
|
|
b7f5f932f6 | ||
|
|
bc9e591e37 | ||
|
|
ad38011348 | ||
|
|
d02b1fb084 | ||
|
|
51efe4f39b | ||
|
|
30e535b5b9 | ||
|
|
4bd3bb9400 | ||
|
|
69c93d3c62 | ||
|
|
78aa957043 | ||
|
|
cd06c022c0 | ||
|
|
8299e9fc33 | ||
|
|
9df5ab8a10 | ||
|
|
6979f5a1b1 | ||
|
|
723791b94f | ||
|
|
aa3833ec83 | ||
|
|
2972af2af1 | ||
|
|
857b9a4ac3 | ||
|
|
f829b084ba |
57
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
Normal file
57
.github/ISSUE_TEMPLATE/1.bug_report_v3.yml
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
name: Bug Report v3
|
||||
description: Create a bug report for Payload v3 (beta)
|
||||
labels: ['status: needs-triage', 'v3']
|
||||
body:
|
||||
- type: input
|
||||
id: reproduction-link
|
||||
attributes:
|
||||
label: Link to reproduction
|
||||
description: Want us to look into your issue faster? Follow the [reproduction-guide](https://github.com/payloadcms/payload/blob/main/.github/reproduction-guide.md) for more information.
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Payload Version
|
||||
description: What version of Payload are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: node-version
|
||||
attributes:
|
||||
label: Node Version
|
||||
description: What version of Node are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: nextjs-version
|
||||
attributes:
|
||||
label: Next.js Version
|
||||
description: What version of Next.js are you running?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: adapters-plugins
|
||||
attributes:
|
||||
label: Adapters and Plugins
|
||||
description: What adapters and plugins are you using if relevant? ie. db-mongodb, db-postgres, storage-vercel-blob, etc.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before submitting the issue, go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to add integration tests to ensure the same issue does not happen again. Thanks in advance!
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Create a bug report for Payload
|
||||
labels: ['[possible-bug]']
|
||||
labels: ['status: needs-triage']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
80
.github/workflows/label-author.yml
vendored
Normal file
80
.github/workflows/label-author.yml
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
name: label-author
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
debug-context:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: View context attributes
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: console.log(context)
|
||||
|
||||
label-created-by:
|
||||
name: Label pr/issue on opening
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Tag with 'created-by'
|
||||
uses: actions/github-script@v7
|
||||
if: github.event.action == 'opened'
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const payloadTeamUsernames = [
|
||||
'denolfe',
|
||||
'jmikrut',
|
||||
'DanRibbens',
|
||||
'jacobsfletch',
|
||||
'JarrodMFlesch',
|
||||
'AlessioGr',
|
||||
'JessChowdhury',
|
||||
'kendelljoseph',
|
||||
'PatrikKozak',
|
||||
'tylandavis',
|
||||
'paulpopus',
|
||||
];
|
||||
|
||||
const type = context.payload.pull_request ? 'pull_request' : 'issue';
|
||||
|
||||
const isTeamMember = payloadTeamUsernames
|
||||
.map(n => n.toLowerCase())
|
||||
.includes(context.payload[type].user.login.toLowerCase());
|
||||
|
||||
if (isTeamMember) {
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: ['created-by: Payload team'],
|
||||
});
|
||||
console.log(`Added 'created-by: Payload team' label`);
|
||||
return;
|
||||
}
|
||||
|
||||
const association = context.payload[type].author_association;
|
||||
let label = ''
|
||||
if (association === 'MEMBER' || association === 'OWNER') {
|
||||
label = 'created-by: Payload team';
|
||||
} else if (association === 'CONTRIBUTOR') {
|
||||
label = 'created-by: Contributor';
|
||||
}
|
||||
|
||||
if (!label) return;
|
||||
|
||||
github.rest.issues.addLabels({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
labels: [label],
|
||||
});
|
||||
console.log(`Added '${label}' label.`);
|
||||
116
.github/workflows/label-on-change.yml
vendored
Normal file
116
.github/workflows/label-on-change.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
name: label-on-change
|
||||
|
||||
on:
|
||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
|
||||
issues:
|
||||
types:
|
||||
- assigned
|
||||
- closed
|
||||
- labeled
|
||||
- reopened
|
||||
|
||||
# TODO: Handle labeling on comment
|
||||
|
||||
jobs:
|
||||
on-labeled-ensure-one-status:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
# Only run on issue labeled and if label starts with 'status:'
|
||||
if: github.event.action == 'labeled' && startsWith(github.event.label.name, 'status:')
|
||||
steps:
|
||||
- name: Ensure only one status label
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Get all labels that start with 'status:' and are not the incoming label
|
||||
const incomingLabelName = context.payload.label.name;
|
||||
const labelNamesToRemove = context.payload.issue.labels
|
||||
.filter(label => label.name.startsWith('status:') && label.name !== incomingLabelName)
|
||||
.map(label => label.name);
|
||||
|
||||
if (!labelNamesToRemove.length) {
|
||||
console.log('No labels to remove');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
||||
|
||||
// If there is more than one status label, remove all but the incoming label
|
||||
for (const labelName of labelNamesToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
name: labelName,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
console.log(`Removed '${labelName}' label`);
|
||||
}
|
||||
|
||||
on-issue-close:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'closed'
|
||||
steps:
|
||||
- name: Remove all labels on issue close
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
// Get all labels that start with 'status:' and 'stale'
|
||||
const labelNamesToRemove = context.payload.issue.labels
|
||||
.filter(label => label.name.startsWith('status:') || label.name === 'stale')
|
||||
.map(label => label.name);
|
||||
|
||||
if (!labelNamesToRemove.length) {
|
||||
console.log('No labels to remove');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Labels to remove: '${labelNamesToRemove}'`);
|
||||
|
||||
for (const labelName of labelNamesToRemove) {
|
||||
await github.rest.issues.removeLabel({
|
||||
issue_number: context.issue.number,
|
||||
name: labelName,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
console.log(`Removed '${labelName}' label`);
|
||||
}
|
||||
|
||||
on-issue-reopen:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: github.event.action == 'reopened'
|
||||
steps:
|
||||
- name: Add needs-triage label on issue reopen
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
with:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
on-issue-assigned:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
if: >
|
||||
github.event.action == 'assigned' &&
|
||||
contains(github.event.issue.labels.*.name, 'status: needs-triage')
|
||||
steps:
|
||||
- name: Remove needs-triage label on issue assign
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
labels: 'status: needs-triage'
|
||||
|
||||
# on-pr-merge:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event.pull_request.merged == true
|
||||
# steps:
|
||||
|
||||
# on-pr-close:
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.event_name == 'pull_request_target' && github.event.pull_request.merged == false
|
||||
# steps:
|
||||
@@ -1,4 +1,4 @@
|
||||
name: pr-title
|
||||
name: release-canary
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -8,4 +8,4 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Echo
|
||||
run: echo "Register pr-title workflow"
|
||||
run: echo "Register release-canary workflow"
|
||||
42
.github/workflows/stale.yml
vendored
Normal file
42
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
name: stale
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
id: stale
|
||||
with:
|
||||
debug-only: true
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
ascending: true
|
||||
operations-per-run: 300
|
||||
|
||||
# Ignore all assigned
|
||||
exempt-all-assignees: false
|
||||
|
||||
# Issues
|
||||
stale-issue-label: 'stale'
|
||||
exempt-issue-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-issue-message: >
|
||||
This issue has been marked as stale due to lack of activity. To keep the ticket open, please indicate that it is still relevant in a comment below.
|
||||
close-issue-message: >
|
||||
This issue was automatically closed due to lack of activity.
|
||||
|
||||
# Pull Requests
|
||||
stale-pr-label: 'stale'
|
||||
exempt-pr-labels: 'blocked,must,should,keep,created-by: Payload team,created-by: Contributor'
|
||||
stale-pr-message: >
|
||||
This PR is stale due to lack of activity. To keep the PR open, please indicate that it is still relevant in a comment below.
|
||||
close-pr-message: >
|
||||
This pull request was automatically closed due to lack of activity.
|
||||
|
||||
- name: Print outputs
|
||||
run: echo ${{ format('{0},{1}', toJSON(steps.stale.outputs.staled-issues-prs), toJSON(steps.stale.outputs.closed-issues-prs)) }}
|
||||
68
CHANGELOG.md
68
CHANGELOG.md
@@ -1,3 +1,71 @@
|
||||
## [2.18.3](https://github.com/payloadcms/payload/compare/v2.18.2...v2.18.3) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** query with like on id columns ([#6416](https://github.com/payloadcms/payload/issues/6416)) ([bf77cec](https://github.com/payloadcms/payload/commit/bf77cec7e9e7db4988e481d464178636203fca32))
|
||||
* **db-postgres:** uuid custom db name ([#6409](https://github.com/payloadcms/payload/issues/6409)) ([db5f3f3](https://github.com/payloadcms/payload/commit/db5f3f3ccdaedd9e8036c3e39fc20650a309a151))
|
||||
* nested `disableListColumn` in rows ([#6412](https://github.com/payloadcms/payload/issues/6412)) ([ab8b2f3](https://github.com/payloadcms/payload/commit/ab8b2f3fb87864484582b7d819ca307888a9449b)), closes [#6407](https://github.com/payloadcms/payload/issues/6407)
|
||||
|
||||
## [2.18.2](https://github.com/payloadcms/payload/compare/v2.18.1...v2.18.2) (2024-05-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow focal point when no sizes defined ([#6397](https://github.com/payloadcms/payload/issues/6397)) ([88e113a](https://github.com/payloadcms/payload/commit/88e113a5452300434f690186d10ea02ab159ffc3))
|
||||
|
||||
## [2.18.1](https://github.com/payloadcms/payload/compare/v2.18.0...v2.18.1) (2024-05-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add back explicit crop x and y values ([#6391](https://github.com/payloadcms/payload/issues/6391)) ([e76df32](https://github.com/payloadcms/payload/commit/e76df32f0987cc92dc8d9c693950e650c52576bf))
|
||||
|
||||
## [2.18.0](https://github.com/payloadcms/payload/compare/v2.17.0...v2.18.0) (2024-05-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* store focal point on uploads ([#6364](https://github.com/payloadcms/payload/issues/6364)) ([82b88a3](https://github.com/payloadcms/payload/commit/82b88a315ff1d52f0b19a70224d5c600a3a97eb5))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **db-postgres:** filter with ID not_in AND queries - postgres ([#6358](https://github.com/payloadcms/payload/issues/6358)) ([cc94078](https://github.com/payloadcms/payload/commit/cc940786072c0065f10fdd2893050bddc4595a21)), closes [#5151](https://github.com/payloadcms/payload/issues/5151)
|
||||
* **richtext-lexical:** upload, relationship and block node insertion fails sometimes ([#6390](https://github.com/payloadcms/payload/issues/6390)) ([48a410e](https://github.com/payloadcms/payload/commit/48a410e294598af9c73577a04f86466248f93da0))
|
||||
|
||||
## [2.17.0](https://github.com/payloadcms/payload/compare/v2.16.1...v2.17.0) (2024-05-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds misc translations to API view and react select ([#6138](https://github.com/payloadcms/payload/issues/6138)) ([30e535b](https://github.com/payloadcms/payload/commit/30e535b5b929dddead007d8a9adca62808595e2c))
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types ([#6279](https://github.com/payloadcms/payload/issues/6279)) ([9df5ab8](https://github.com/payloadcms/payload/commit/9df5ab8a10a35ad34615d7e4da024f59ff037e0e))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* appends `editDepth` value to `radio` & `checkbox` IDs when inside drawer ([#6181](https://github.com/payloadcms/payload/issues/6181)) ([69c93d3](https://github.com/payloadcms/payload/commit/69c93d3c62394a5cf995a2eaec9a3ab30e0f77af))
|
||||
* collection labels with locales not working when creating new doc ([#5995](https://github.com/payloadcms/payload/issues/5995)) ([51efe4f](https://github.com/payloadcms/payload/commit/51efe4f39bcaadccb109a2a02a690ca65041ee57))
|
||||
* safely access cookie header for uploads ([#6367](https://github.com/payloadcms/payload/issues/6367)) ([de92c50](https://github.com/payloadcms/payload/commit/de92c50847640661f915455f8db0029873ddc7ab))
|
||||
* step-nav breadcrumbs ellipsis ([#6345](https://github.com/payloadcms/payload/issues/6345)) ([d02b1fb](https://github.com/payloadcms/payload/commit/d02b1fb084e636e49122ad55b25b9c49eb761f1c))
|
||||
*
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **richtext-lexical:** remove LexicalBlock, RichTextFieldRequiredEditor and FieldWithRichTextRequiredEditor types (#6279)
|
||||
|
||||
## [2.16.1](https://github.com/payloadcms/payload/compare/v2.16.0...v2.16.1) (2024-05-07)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **richtext-lexical:** add maxDepth property to various lexical features ([#6250](https://github.com/payloadcms/payload/issues/6250)) ([857b9a4](https://github.com/payloadcms/payload/commit/857b9a4ac3236c740458750f156a3a4274eda210)), closes [#6242](https://github.com/payloadcms/payload/issues/6242)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **richtext-lexical:** export missing HorizontalRuleFeature ([#6236](https://github.com/payloadcms/payload/issues/6236)) ([f829b08](https://github.com/payloadcms/payload/commit/f829b084ba9649ef596cce4a7bf6ae8c7ccf57e3))
|
||||
|
||||
## [2.16.0](https://github.com/payloadcms/payload/compare/v2.15.0...v2.16.0) (2024-05-06)
|
||||
|
||||
|
||||
|
||||
39
docs/examples/overview.mdx
Normal file
39
docs/examples/overview.mdx
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
title: Examples
|
||||
label: Overview
|
||||
order: 10
|
||||
desc:
|
||||
keywords: example, examples, starter, boilerplate, template, templates
|
||||
---
|
||||
|
||||
Payload provides a vast array of examples to help you get started with your project no matter what you are working on. These examples are designed to be easy to get up and running, and to be easy to understand. They showcase nothing more than the specific features being demonstrated, so you can easily decipher what is going on.
|
||||
|
||||
Examples are changing every day, so be sure to check back often to see what new examples have been added. If you have a specific example you would like to see, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
|
||||
- [Auth](https://github.com/payloadcms/payload/tree/main/examples/auth)
|
||||
- [Custom Server](https://github.com/payloadcms/payload/tree/main/examples/custom-server)
|
||||
- [Draft Preview](https://github.com/payloadcms/payload/tree/main/examples/draft-preview)
|
||||
- [Email](https://github.com/payloadcms/payload/tree/main/examples/email)
|
||||
- [Form Builder](https://github.com/payloadcms/payload/tree/main/examples/form-builder)
|
||||
- [Hierarchy](https://github.com/payloadcms/payload/tree/main/examples/hierarchy)
|
||||
- [Live Preview](https://github.com/payloadcms/payload/tree/main/examples/live-preview)
|
||||
- [Multi-tenant](https://github.com/payloadcms/payload/tree/main/examples/multi-tenant)
|
||||
- [Nested Docs](https://github.com/payloadcms/payload/tree/main/examples/nested-docs)
|
||||
- [Redirects](https://github.com/payloadcms/payload/tree/main/examples/redirects)
|
||||
- [Tests](https://github.com/payloadcms/payload/tree/main/examples/testing)
|
||||
- [Virtual Fields](https://github.com/payloadcms/payload/tree/main/examples/virtual-fields)
|
||||
- [White-label Admin UI](https://github.com/payloadcms/payload/tree/main/examples/whitelabel)
|
||||
|
||||
Where necessary, some examples include a front-end. Examples that require a front-end share this folder structure:
|
||||
|
||||
```plaintext
|
||||
example/
|
||||
├── payload/
|
||||
├── next-app/
|
||||
├── next-pages/
|
||||
├── react-router/
|
||||
├── vue/
|
||||
├── svelte/
|
||||
```
|
||||
|
||||
Where `payload` is your Payload project, and the other directories are dedicated to their respective front-end framework. We are adding new examples every day, so if your framework of choice is not yet supported in any particular example, please feel free to start a new [Discussion](https://github.com/payloadcms/payload/discussions) or open a new [PR](https://github.com/payloadcms/payload/pulls) to add it yourself.
|
||||
@@ -31,6 +31,7 @@ With this field, you can also inject custom `Cell` components that appear as add
|
||||
| **`label`** | Human-readable label for this UI field. |
|
||||
| **`admin.components.Field`** \* | React component to be rendered for this field within the Edit view. [More](/docs/admin/components/#field-component) |
|
||||
| **`admin.components.Cell`** | React component to be rendered as a Cell within collection List views. [More](/docs/admin/components/#field-component) |
|
||||
| **`admin.disableListColumn`** | Set `disableListColumn` to `true` to prevent the UI field from appearing in the list view column selector. |
|
||||
| **`custom`** | Extension point for adding custom data (e.g. for plugins) |
|
||||
|
||||
_\* An asterisk denotes that a property is required._
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
---
|
||||
title: Vercel Visual Editing
|
||||
label: Vercel Visual Editing
|
||||
title: Vercel Content Link
|
||||
label: Vercel Content Link
|
||||
order: 10
|
||||
desc: Payload + Vercel Visual Editing allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
|
||||
keywords: vercel, vercel visual editing, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
|
||||
desc: Payload + Vercel Content Link allows yours editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it.
|
||||
keywords: vercel, vercel content link, visual editing, content source maps, Content Management System, cms, headless, javascript, node, react, express
|
||||
---
|
||||
|
||||
[Vercel Visual Editing](https://vercel.com/docs/workflow-collaboration/visual-editing) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
|
||||
[Vercel Content Link](https://vercel.com/docs/workflow-collaboration/edit-mode#content-link) will allow your editors to navigate directly from the content rendered on your front-end to the fields in Payload that control it. This requires no changes to your front-end code and very few changes to your Payload config.
|
||||
|
||||

|
||||
|
||||
<Banner type="warning">
|
||||
Vercel Visual Editing is an enterprise-only feature and only available for deployments hosted on
|
||||
Vercel Content Link is an enterprise-only feature and only available for deployments hosted on
|
||||
Vercel. If you are an existing enterprise customer, [contact our sales
|
||||
team](https://payloadcms.com/for-enterprise) for help with your integration.
|
||||
</Banner>
|
||||
|
||||
### How it works
|
||||
|
||||
To power Vercel Visual Editing, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Visual Editing interface.
|
||||
To power Vercel Content Link, Payload embeds Content Source Maps into its API responses. Content Source Maps are invisible, encoded JSON values that include a link back to the field in the CMS that generated the content. When rendered on the page, Vercel detects and decodes these values to display the Content Link interface.
|
||||
|
||||
For full details on how the encoding and decoding algorithm works, check out [`@vercel/stega`](https://www.npmjs.com/package/@vercel/stega).
|
||||
|
||||
### Getting Started
|
||||
|
||||
Setting up Payload with Vercel Visual Editing is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
|
||||
Setting up Payload with Vercel Content Link is easy. First, install the `@payloadcms/plugin-csm` plugin into your project. This plugin requires an API key to install, [contact our sales team](https://payloadcms.com/for-enterprise) if you don't already have one.
|
||||
|
||||
```bash
|
||||
npm i @payloadcms/plugin-csm
|
||||
@@ -76,7 +76,7 @@ And that's it! You are now ready to enter Edit Mode and begin visually editing y
|
||||
|
||||
##### Edit Mode
|
||||
|
||||
To see Visual Editing on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
|
||||
To see Content Link on your site, you first need to visit any preview deployment on Vercel and login using the Vercel Toolbar. When Content Source Maps are detected on the page, a pencil icon will appear in the toolbar. Clicking this icon will enable Edit Mode, highlighting all editable fields on the page in blue.
|
||||
|
||||

|
||||
|
||||
@@ -93,7 +93,7 @@ const { cleaned, encoded } = vercelStegaSplit(text)
|
||||
|
||||
##### Blocks
|
||||
|
||||
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Visual Editing on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
|
||||
All `blocks` fields by definition do not have plain text strings to encode. For this reason, blocks are given an additional `encodedSourceMap` key, which you can use to enable Content Link on entire sections of your site. You can then specify the editing container by adding the `data-vercel-edit-target` HTML attribute to any top-level element of your block.
|
||||
|
||||
```ts
|
||||
<div data-vercel-edit-target>
|
||||
133
docs/plugins/sentry.mdx
Normal file
133
docs/plugins/sentry.mdx
Normal file
@@ -0,0 +1,133 @@
|
||||
---
|
||||
title: Sentry Plugin
|
||||
label: Sentry
|
||||
order: 20
|
||||
desc: Integrate Sentry error tracking into your Payload application
|
||||
keywords: plugins, sentry, error, tracking, monitoring, logging, bug, reporting, performance
|
||||
---
|
||||
|
||||
[](https://www.npmjs.com/package/@payloadcms/plugin-sentry)
|
||||
|
||||
This plugin allows you to integrate [Sentry](https://sentry.io/) seamlessly with your [Payload](https://github.com/payloadcms/payload) application.
|
||||
|
||||
### What is Sentry?
|
||||
|
||||
Sentry is a powerful error tracking and performance monitoring tool that helps developers identify, diagnose, and resolve issues in their applications.
|
||||
|
||||
<Banner type="success">
|
||||
Sentry does smart stuff with error data to make bugs easier to find and fix. - [sentry.io](https://sentry.io/)
|
||||
</Banner>
|
||||
|
||||
This multi-faceted software offers a range of features that will help you manage errors with greater ease and ultimately ensure your application is running smoothly:
|
||||
|
||||
#### Core Features
|
||||
|
||||
- **Error Tracking**: Instantly captures and logs errors as they occur in your application
|
||||
- **Performance Monitoring**: Tracks application performance to identify slowdowns and bottlenecks
|
||||
- **Detailed Reports**: Provides comprehensive insights into errors, including stack traces and context
|
||||
- **Alerts and Notifications**: Send and customize event-triggered notifications
|
||||
- **Issue Grouping, Filtering and Search**: Automatically groups similar errors, and allows filtering and searching issues by custom criteria
|
||||
- **Breadcrumbs**: Records user actions and events leading up to an error
|
||||
- **Integrations**: Connects with various tools and services for enhanced workflow and issue management
|
||||
|
||||
<Banner type="info">
|
||||
This plugin is completely open-source and the [source code can be found here](https://github.com/payloadcms/payload/tree/main/packages/plugin-sentry). If you need help, check out our [Community Help](https://payloadcms.com/community-help). If you think you've found a bug, please [open a new issue](https://github.com/payloadcms/payload/issues/new?assignees=&labels=plugin%3A%20seo&template=bug_report.md&title=plugin-seo%3A) with as much detail as possible.
|
||||
</Banner>
|
||||
|
||||
## Installation
|
||||
|
||||
Install the plugin using any JavaScript package manager like [Yarn](https://yarnpkg.com), [NPM](https://npmjs.com), or [PNPM](https://pnpm.io):
|
||||
|
||||
```bash
|
||||
yarn add @payloadcms/plugin-sentry
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
In the `plugins` array of your [Payload config](https://payloadcms.com/docs/configuration/overview), call the plugin and pass in your Sentry DSN as an option.
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { sentry } from '@payloadcms/plugin-sentry'
|
||||
import { Pages, Media } from './collections'
|
||||
|
||||
const config = buildConfig({
|
||||
collections: [Pages, Media],
|
||||
plugins: [
|
||||
sentry({
|
||||
dsn: 'https://61edebas776889984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export default config
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
- `dsn` : string | **required**
|
||||
|
||||
Sentry automatically assigns a DSN when you create a project, the unique DSN informs Sentry where to send events so they are associated with the correct project.
|
||||
|
||||
<Banner type="warning">
|
||||
You can find your project DSN (Data Source Name) by visiting [sentry.io](sentry.io) and navigating to your [Project] > Settings > Client Keys (DSN).
|
||||
</Banner>
|
||||
|
||||
- `enabled`: boolean | optional
|
||||
|
||||
Set to false to disable the plugin. Defaults to true.
|
||||
|
||||
- `init` : ClientOptions | optional
|
||||
|
||||
Sentry allows a variety of options to be passed into the Sentry.init() function, see the full list of options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
|
||||
|
||||
- `requestHandler` : RequestHandlerOptions | optional
|
||||
|
||||
Accepts options that let you decide what data should be included in the event sent to Sentry, checkout the options [here](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
|
||||
|
||||
- `captureErrors`: number[] | optional
|
||||
|
||||
By default, `Sentry.errorHandler` will capture only errors with a status code of 500 or higher. To capture additional error codes, pass the values as numbers in an array.
|
||||
|
||||
To see all options available, visit the [Sentry Docs](https://docs.sentry.io/platforms/node/guides/express/configuration/options).
|
||||
|
||||
### Example
|
||||
|
||||
Configure any of these options by passing them to the plugin:
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { sentry } from '@payloadcms/plugin-sentry'
|
||||
import { Pages, Media } from './collections'
|
||||
|
||||
const config = buildConfig({
|
||||
collections: [Pages, Media],
|
||||
plugins: [
|
||||
sentry({
|
||||
dsn: 'https://61edebas777689984d323d777@o4505289711681536.ingest.sentry.io/4505357433352176',
|
||||
options: {
|
||||
init: {
|
||||
debug: true,
|
||||
environment: 'development',
|
||||
tracesSampleRate: 1.0,
|
||||
},
|
||||
requestHandler: {
|
||||
serverName: false,
|
||||
user: ['email'],
|
||||
},
|
||||
captureErrors: [400, 403, 404],
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
export default config
|
||||
```
|
||||
|
||||
## TypeScript
|
||||
|
||||
All types can be directly imported:
|
||||
|
||||
```ts
|
||||
import { PluginOptions } from '@payloadcms/plugin-sentry/types'
|
||||
```
|
||||
10
examples/db-example/.eslintignore
Normal file
10
examples/db-example/.eslintignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
37
examples/db-example/.eslintrc.js
Normal file
37
examples/db-example/.eslintrc.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import('prettier').Config} */
|
||||
module.exports = {
|
||||
extends: ['@payloadcms'],
|
||||
overrides: [
|
||||
{
|
||||
extends: ['plugin:@typescript-eslint/disable-type-checked'],
|
||||
files: ['*.js', '*.cjs', '*.json', '*.md', '*.yml', '*.yaml'],
|
||||
},
|
||||
{
|
||||
files: ['package.json', 'tsconfig.json'],
|
||||
rules: {
|
||||
'perfectionist/sort-array-includes': 'off',
|
||||
'perfectionist/sort-astro-attributes': 'off',
|
||||
'perfectionist/sort-classes': 'off',
|
||||
'perfectionist/sort-enums': 'off',
|
||||
'perfectionist/sort-exports': 'off',
|
||||
'perfectionist/sort-imports': 'off',
|
||||
'perfectionist/sort-interfaces': 'off',
|
||||
'perfectionist/sort-jsx-props': 'off',
|
||||
'perfectionist/sort-keys': 'off',
|
||||
'perfectionist/sort-maps': 'off',
|
||||
'perfectionist/sort-named-exports': 'off',
|
||||
'perfectionist/sort-named-imports': 'off',
|
||||
'perfectionist/sort-object-types': 'off',
|
||||
'perfectionist/sort-objects': 'off',
|
||||
'perfectionist/sort-svelte-attributes': 'off',
|
||||
'perfectionist/sort-union-types': 'off',
|
||||
'perfectionist/sort-vue-attributes': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
root: true,
|
||||
}
|
||||
1
examples/db-example/.gitignore
vendored
Normal file
1
examples/db-example/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/migrations
|
||||
10
examples/db-example/.prettierignore
Normal file
10
examples/db-example/.prettierignore
Normal file
@@ -0,0 +1,10 @@
|
||||
.tmp
|
||||
**/.git
|
||||
**/.hg
|
||||
**/.pnp.*
|
||||
**/.svn
|
||||
**/.yarn/**
|
||||
**/build
|
||||
**/dist/**
|
||||
**/node_modules
|
||||
**/temp
|
||||
15
examples/db-example/.swcrc
Normal file
15
examples/db-example/.swcrc
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/swcrc",
|
||||
"sourceMaps": "inline",
|
||||
"jsc": {
|
||||
"target": "esnext",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true,
|
||||
"dts": true
|
||||
}
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs"
|
||||
}
|
||||
}
|
||||
32
examples/db-example/README.md
Normal file
32
examples/db-example/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Example Database Adapter for Payload
|
||||
|
||||
- [Main Repository](https://github.com/payloadcms/payload)
|
||||
- [Payload Docs](https://payloadcms.com/docs)
|
||||
|
||||
To build a fully working database adapter for Payload you must implement the database adapter interface. There is a mix
|
||||
of required and not required methods depending on the areas you need to support. Payload will call the adapter's `init`
|
||||
function followed by the `connect` method if it exists. The adapter must create any schema necessary on the target
|
||||
database in the init function. The adapter can be extended as needed to keep track of models for collections and other
|
||||
artifacts necessary to perform all the needed operations.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @payloadcms/db-example
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { buildConfig } from 'payload/config'
|
||||
import { exampleAdapter } from '@payloadcms/db-example'
|
||||
|
||||
export default buildConfig({
|
||||
db: exampleAdapter({
|
||||
url: process.env.DATABASE_URI,
|
||||
}),
|
||||
// ...rest of config
|
||||
})
|
||||
```
|
||||
|
||||
More detailed usage can be found in the [Payload Docs](https://payloadcms.com/docs/configuration/overview).
|
||||
40
examples/db-example/package.json
Normal file
40
examples/db-example/package.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@payloadcms/db-example",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "A sample implementation of a Payload database adapter",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/payloadcms/payload.git",
|
||||
"directory": "examples/db-example"
|
||||
},
|
||||
"license": "MIT",
|
||||
"homepage": "https://payloadcms.com",
|
||||
"author": {
|
||||
"email": "info@payloadcms.com",
|
||||
"name": "Payload",
|
||||
"url": "https://payloadcms.com"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "pnpm build:swc && pnpm build:types",
|
||||
"build:swc": "swc ./src -d ./dist --config-file .swcrc",
|
||||
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
||||
"clean": "rimraf {dist,*.tsbuildinfo}",
|
||||
"prepublishOnly": "pnpm clean && pnpm build"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@payloadcms/eslint-config": "^1.1.1",
|
||||
"payload": "^2.18.3",
|
||||
"rimraf": "^4.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"payload": "^2.0.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
26
examples/db-example/src/connect.ts
Normal file
26
examples/db-example/src/connect.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { Connect } from 'payload/database'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Implement the connect feature here. This will connect to the underlying resource and set the this.connection property.
|
||||
*
|
||||
* Optional - this method is not required
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {Payload} payload
|
||||
* @returns {Promise<void>} A promise that resolves when the connection is established.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* this.connection = await myUnderlyingStore.connect(this.url)
|
||||
* ```
|
||||
*/
|
||||
export const connect: Connect = async function connect(this: ExampleAdapter, payload) {
|
||||
try {
|
||||
// this.connection = await myUnderlyingStore.connect(this.url)
|
||||
} catch (err) {
|
||||
this.payload.logger.error(`Error: cannot connect to DB. Details: ${err.message}`, err)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
26
examples/db-example/src/count.ts
Normal file
26
examples/db-example/src/count.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { Count } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Counts the total number of documents that match the query in the specified collection.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for counting documents.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the documents for counting.
|
||||
* @returns {Promise<{ totalDocs: number }>} A promise resolving to an object containing the total number of documents.
|
||||
*
|
||||
* Implement the count function here for the specified collection and return the total number of documents that match the query.
|
||||
*/
|
||||
export const count: Count = async function count(
|
||||
this: ExampleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
): Promise<{ totalDocs: number }> {
|
||||
return {
|
||||
totalDocs: 0,
|
||||
}
|
||||
}
|
||||
51
examples/db-example/src/create.ts
Normal file
51
examples/db-example/src/create.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { Create } from 'payload/database'
|
||||
import type { Document, PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
import handleError from './errors/handleError'
|
||||
|
||||
/**
|
||||
* Creates a new document in the specified collection.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for creating documents.
|
||||
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
|
||||
* @param {boolean} draft - Determine whether or not to create as a draft or not.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @returns {Promise<Document>} A promise resolving to the created document.
|
||||
*/
|
||||
export const create: Create = async function create(
|
||||
this: ExampleAdapter,
|
||||
{ collection, data, draft, locale, req = {} as PayloadRequest },
|
||||
) {
|
||||
let doc
|
||||
try {
|
||||
/**
|
||||
* Here you would send your data to where ever it needs to go
|
||||
*
|
||||
* Implement the logic to create the document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* doc = await adapterSpecificModel.create(data, options)
|
||||
* ```
|
||||
*/
|
||||
} catch (error) {
|
||||
handleError(error, req)
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result: Document = doc
|
||||
|
||||
return result
|
||||
}
|
||||
40
examples/db-example/src/createGlobal.ts
Normal file
40
examples/db-example/src/createGlobal.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { CreateGlobal } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Creates a global document in the database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} slug - The specified slug of the global.
|
||||
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @returns {Promise<T>} A promise that resolves with the created global document.
|
||||
*/
|
||||
export const createGlobal: CreateGlobal = async function createGlobal(
|
||||
this: ExampleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest },
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to create the global document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* result = await adapterSpecificModel.create(global, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
53
examples/db-example/src/createGlobalVersion.ts
Normal file
53
examples/db-example/src/createGlobalVersion.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { CreateGlobalVersion } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Creates a global version document in the database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {boolean} autosave - Indicates if autosave is enabled.
|
||||
* @param {Date} createdAt - Created-At date of the document.
|
||||
* @param {string} globalSlug - The global slug of the document.
|
||||
* @param {string} parent - ID of the parent document for which the version should be created for.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Date} updatedAt - Updated-At date of the document.
|
||||
* @param {object} versionData - Full version data passed to create the global version.
|
||||
* @returns {Promise<TypeWithVersion<T>>} A promise that resolves with the created global version document.
|
||||
*/
|
||||
export const createGlobalVersion: CreateGlobalVersion = async function createGlobalVersion(
|
||||
this: ExampleAdapter,
|
||||
{ autosave, createdAt, globalSlug, parent, req = {} as PayloadRequest, updatedAt, versionData },
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to create the global version document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* doc = await adapterSpecificVersionModel.create({
|
||||
* autosave,
|
||||
* createdAt,
|
||||
* latest: true,
|
||||
* parent,
|
||||
* updatedAt,
|
||||
* version: versionData,
|
||||
* }, options, req)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result: Document = doc
|
||||
|
||||
return result
|
||||
}
|
||||
61
examples/db-example/src/createVersion.ts
Normal file
61
examples/db-example/src/createVersion.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { CreateVersion, TypeWithVersion } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Creates a version document in the database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {boolean} autosave - Indicates if autosave is enabled.
|
||||
* @param {string} collectionSlug - The collection slug of the document.
|
||||
* @param {Date} createdAt - Created-At date of the document.
|
||||
* @param {string} parent - ID of the parent document for which the version should be created for.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Date} updatedAt - Updated-At date of the document.
|
||||
* @param {object} versionData - Full version data passed to create the version.
|
||||
* @returns {Promise<TypeWithVersion<T>>} A promise that resolves with the created version document.
|
||||
*
|
||||
*/
|
||||
export const createVersion: CreateVersion = async function createVersion(
|
||||
this: ExampleAdapter,
|
||||
{
|
||||
autosave,
|
||||
collectionSlug,
|
||||
createdAt,
|
||||
parent,
|
||||
req = {} as PayloadRequest,
|
||||
updatedAt,
|
||||
versionData,
|
||||
},
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to create the version document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* doc = await adapterSpecificVersionModel.create({
|
||||
* autosave,
|
||||
* createdAt,
|
||||
* latest: true,
|
||||
* parent,
|
||||
* updatedAt,
|
||||
* version: versionData,
|
||||
* }, options, req)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result: Document = doc
|
||||
return result
|
||||
}
|
||||
28
examples/db-example/src/deleteMany.ts
Normal file
28
examples/db-example/src/deleteMany.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { DeleteMany } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Deletes multiple documents from the specified collection in the database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for deleting documents.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the documents for deleting.
|
||||
* @returns {Promise<void>} A promise that resolves when the documents are deleted.
|
||||
*/
|
||||
export const deleteMany: DeleteMany = async function deleteMany(
|
||||
this: ExampleAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
/**
|
||||
* Implement the logic to delete many documents from your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await adapterSpecificModel.deleteMany(query, options)
|
||||
* ```
|
||||
*/
|
||||
}
|
||||
43
examples/db-example/src/deleteOne.ts
Normal file
43
examples/db-example/src/deleteOne.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { DeleteOne } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Deletes a single document from the specified collection in the database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for deleting a document.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the document for deleting.
|
||||
* @returns {Promise<Document>} A promise that resolves with the deleted document.
|
||||
*/
|
||||
export const deleteOne: DeleteOne = async function deleteOne(
|
||||
this: ExampleAdapter,
|
||||
{ collection, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Need to go delete your document through the API
|
||||
*
|
||||
* Implement the logic to delete the document from your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* doc = await adapterSpecificModel.delete(query, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result: Document = doc
|
||||
return result
|
||||
}
|
||||
29
examples/db-example/src/deleteVersions.ts
Normal file
29
examples/db-example/src/deleteVersions.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { DeleteVersions } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Deletes many version documents from your database.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for deleting versions.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the documents for deleting versions.
|
||||
* @returns {Promise<void>} A promise that resolves with the created global document.
|
||||
*/
|
||||
export const deleteVersions: DeleteVersions = async function deleteVersions(
|
||||
this: ExampleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
/**
|
||||
* Implement the logic to delete many version documents from your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* await adapterSpecificVersionsModel.deleteMany(query, options)
|
||||
* ```
|
||||
*/
|
||||
}
|
||||
33
examples/db-example/src/destroy.ts
Normal file
33
examples/db-example/src/destroy.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { Destroy } from 'payload/database'
|
||||
|
||||
import type { ExampleAdapter } from './index'
|
||||
|
||||
/**
|
||||
* Closes the database connection and cleans up resources used by the adapter.
|
||||
*
|
||||
* This function is typically used to gracefully shutdown the database connections
|
||||
* when the application is closing or when the adapter is no longer needed.
|
||||
*
|
||||
* Optional - this method is not required
|
||||
*
|
||||
* @param {ExampleAdapter} - The ExampleAdapter instance.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const destroy: Destroy = async function destroy(this: ExampleAdapter) {
|
||||
/**
|
||||
* If using an in-memory database or a similar service, add the specific steps to drop the database and stop the server.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* if (this.inMemoryDatabase) {
|
||||
* await this.connection.dropDatabase()
|
||||
*
|
||||
* await this.connection.close()
|
||||
*
|
||||
* await this.inMemoryDatabase.stop()
|
||||
* } else {
|
||||
* await this.connection.close()
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
}
|
||||
18
examples/db-example/src/errors/handleError.ts
Normal file
18
examples/db-example/src/errors/handleError.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Handle uniqueness error
|
||||
*
|
||||
* If the error is from a unique or required field - return the expected format using validation error
|
||||
*/
|
||||
const handleError = (error, req) => {
|
||||
// throw new ValidationError(
|
||||
// [
|
||||
// {
|
||||
// field: field.name,
|
||||
// message: req.t('error:valueMustBeUnique'),
|
||||
// },
|
||||
// ],
|
||||
// req.t,
|
||||
// )
|
||||
}
|
||||
|
||||
export default handleError
|
||||
45
examples/db-example/src/find.ts
Normal file
45
examples/db-example/src/find.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { Find, PaginatedDocs } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Queries for documents in the specified collection based on the provided criteria using the incoming where,
|
||||
* sort, page query and then only return the correct documents in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to query for documents.
|
||||
* @param {number} limit - The maximum number of documents to return.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {number} page - The page number of the results to return.
|
||||
* @param {boolean} pagination - Determines whether pagination is enabled.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {string} sort - The top-level field to sort the results by.
|
||||
* @param {Where} where - The specific query used to filter documents.
|
||||
* @returns {Promise<PaginatedDocs<T>>} A promise resolving to the paginated documents matching the query criteria.
|
||||
*/
|
||||
export const find: Find = async function find(
|
||||
this: ExampleAdapter,
|
||||
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to paginate the query results according to your database's methods.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
return result
|
||||
}
|
||||
44
examples/db-example/src/findGlobal.ts
Normal file
44
examples/db-example/src/findGlobal.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { FindGlobal } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Finds a global document based on the specified criteria using the incoming slug, locale, and
|
||||
* where query, then returns it in the format expected by Payload.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} slug - The specified slug of the global document.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the global document.
|
||||
* @returns {Promise<T>} A promise resolving to the found global document or null if not found.
|
||||
*/
|
||||
export const findGlobal: FindGlobal = async function findGlobal(
|
||||
this: ExampleAdapter,
|
||||
{ slug, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to find a global in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* let doc = await adapterSpecificModel.findOne(query, {}, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
*/
|
||||
|
||||
return doc
|
||||
}
|
||||
57
examples/db-example/src/findGlobalVersions.ts
Normal file
57
examples/db-example/src/findGlobalVersions.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { FindGlobalVersions, PaginatedDocs, TypeWithVersion } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Finds versions of a global document based on the specified criteria using the incoming global, limit, locale, page,
|
||||
* pagination, req, skip, sort, and where parameters, and returns them in the format expected by Payload.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} global - The name of the global document to reference for finding versions.
|
||||
* @param {number} limit - The maximum number of versions to return.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {number} page - The page number of the results to return.
|
||||
* @param {boolean} pagination - Determines whether pagination is enabled.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {boolean} skip - Middleware function that can bypass the limit if it returns true.
|
||||
* @param {string} sort - The top-level field to sort the results by.
|
||||
* @param {Where} where - The specific query used to filter global versions.
|
||||
* @returns {Promise<PaginatedDocs<TypeWithVersion<T>>>} A promise resolving to the paginated versions matching the query criteria.
|
||||
*/
|
||||
export const findGlobalVersions: FindGlobalVersions = async function findGlobalVersions(
|
||||
this: ExampleAdapter,
|
||||
{
|
||||
global,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to paginate the query results according to your database's methods.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
47
examples/db-example/src/findOne.ts
Normal file
47
examples/db-example/src/findOne.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { FindOne } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
import type { Document } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Finds a single document in the specified collection based on the provided criteria using
|
||||
* the incoming locale and where query, and returns it in the format expected by Payload.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for finding the document.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the document.
|
||||
* @returns {Promise<T>} A promise resolving to the found document or null if not found.
|
||||
*/
|
||||
export const findOne: FindOne = async function findOne(
|
||||
this: ExampleAdapter,
|
||||
{ collection, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to find one document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const doc = await adapterSpecificModel.findOne(query, {}, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
if (!doc) {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result: Document = doc
|
||||
return result
|
||||
}
|
||||
56
examples/db-example/src/findVersions.ts
Normal file
56
examples/db-example/src/findVersions.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { FindVersions, PaginatedDocs } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Queries for versions of documents in the specified collection based on the provided criteria using the incoming where,
|
||||
* sort, page query and then only returns the correct document versions in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for finding versions.
|
||||
* @param {number} limit - The maximum number of versions to return.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {number} page - The page number of the results to return.
|
||||
* @param {boolean} pagination - Determines whether pagination is enabled.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {boolean} skip - Middleware function that can bypass the limit if it returns true.
|
||||
* @param {string} sort - The top-level field to sort the results by.
|
||||
* @param {Where} where - The specific query used to filter document versions.
|
||||
* @returns {Promise<PaginatedDocs<TypeWithVersion<T>>>} A promise resolving to the paginated document versions matching the query criteria.
|
||||
*/
|
||||
export const findVersions: FindVersions = async function findVersions(
|
||||
this: ExampleAdapter,
|
||||
{
|
||||
collection,
|
||||
limit,
|
||||
locale,
|
||||
page,
|
||||
pagination,
|
||||
req = {} as PayloadRequest,
|
||||
skip,
|
||||
sort: sortArg,
|
||||
where,
|
||||
},
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to paginate the query results according to your database's methods.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await adapterSpecificModel.paginate(query, paginationOptions)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
130
examples/db-example/src/index.ts
Normal file
130
examples/db-example/src/index.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import type { Payload } from 'payload'
|
||||
import type { BaseDatabaseAdapter } from 'payload/database'
|
||||
|
||||
import { createDatabaseAdapter } from 'payload/database'
|
||||
|
||||
import { connect } from './connect'
|
||||
import { count } from './count'
|
||||
import { create } from './create'
|
||||
import { createGlobal } from './createGlobal'
|
||||
import { createGlobalVersion } from './createGlobalVersion'
|
||||
import { createVersion } from './createVersion'
|
||||
import { deleteMany } from './deleteMany'
|
||||
import { deleteOne } from './deleteOne'
|
||||
import { deleteVersions } from './deleteVersions'
|
||||
import { destroy } from './destroy'
|
||||
import { find } from './find'
|
||||
import { findGlobal } from './findGlobal'
|
||||
import { findGlobalVersions } from './findGlobalVersions'
|
||||
import { findOne } from './findOne'
|
||||
import { findVersions } from './findVersions'
|
||||
import { init } from './init'
|
||||
import { queryDrafts } from './queryDrafts'
|
||||
import { beginTransaction } from './transactions/beginTransaction'
|
||||
import { commitTransaction } from './transactions/commitTransaction'
|
||||
import { rollbackTransaction } from './transactions/rollbackTransaction'
|
||||
import { updateGlobal } from './updateGlobal'
|
||||
import { updateGlobalVersion } from './updateGlobalVersion'
|
||||
import { updateOne } from './updateOne'
|
||||
import { updateVersion } from './updateVersion'
|
||||
|
||||
/**
|
||||
* Configure any adapter-specific options here
|
||||
*
|
||||
* For example: connection options, transaction options, etc.
|
||||
*/
|
||||
export interface Args {
|
||||
url: string
|
||||
}
|
||||
|
||||
/**
|
||||
* The exported adapter Type
|
||||
*
|
||||
* _Optionally_, expose any adapter-specific methods here. How much you expose here relies on your underlying implementation.
|
||||
*/
|
||||
export type ExampleAdapter = BaseDatabaseAdapter &
|
||||
Args & {
|
||||
/**
|
||||
* This will allow access to the underlying model via `payload.db.collections.<slug>.find()
|
||||
*
|
||||
* This optional
|
||||
*/
|
||||
collections: {
|
||||
[slug: string]: unknown
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to underlying global model via `payload.db.globals`
|
||||
*/
|
||||
globals: {
|
||||
[slug: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
type ExampleAdapterResult = (args: { payload: Payload }) => ExampleAdapter
|
||||
|
||||
/**
|
||||
* This declaration injects the proper types for the DB Adapter into Payload when accessing the adapter in code
|
||||
*
|
||||
* Optional
|
||||
*/
|
||||
declare module 'payload' {
|
||||
export interface DatabaseAdapter extends BaseDatabaseAdapter {
|
||||
collections: {
|
||||
[slug: string]: unknown
|
||||
}
|
||||
globals: {
|
||||
[slug: string]: unknown
|
||||
}
|
||||
versions: {
|
||||
[slug: string]: unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function exampleAdapter({ url }: Args): ExampleAdapterResult {
|
||||
function adapter({ payload }: { payload: Payload }) {
|
||||
return createDatabaseAdapter<ExampleAdapter>({
|
||||
name: 'example',
|
||||
|
||||
// Example adapter-specific
|
||||
collections: {},
|
||||
count,
|
||||
globals: undefined,
|
||||
url,
|
||||
|
||||
/**
|
||||
* Configure DatabaseAdapter
|
||||
*
|
||||
* Many of these are optional and will fallback on some default functionality if not defined.
|
||||
*/
|
||||
beginTransaction,
|
||||
commitTransaction,
|
||||
connect,
|
||||
create,
|
||||
createGlobal,
|
||||
createGlobalVersion,
|
||||
createVersion,
|
||||
defaultIDType: 'text',
|
||||
deleteMany,
|
||||
deleteOne,
|
||||
deleteVersions,
|
||||
destroy,
|
||||
find,
|
||||
findGlobal,
|
||||
findGlobalVersions,
|
||||
findOne,
|
||||
findVersions,
|
||||
init,
|
||||
payload,
|
||||
queryDrafts,
|
||||
rollbackTransaction,
|
||||
updateGlobal,
|
||||
updateGlobalVersion,
|
||||
updateOne,
|
||||
updateVersion,
|
||||
})
|
||||
}
|
||||
|
||||
return adapter
|
||||
}
|
||||
40
examples/db-example/src/init.ts
Normal file
40
examples/db-example/src/init.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import type { Init } from 'payload/database'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Perform any initialization actions here.
|
||||
* This will run immediately when the DB adapter is initialized and will take place before the connect method.
|
||||
* If your implementation needs models created from Payload collections and globals, you would do this here.
|
||||
* If your implementation needs to maintain a persistent connection, you should call this.connect here.
|
||||
*
|
||||
* Your DB adapter needs to be able to handle all the different Payload field types found in collections & globals:
|
||||
*
|
||||
* - Fields containing subfields:
|
||||
* - 'array'
|
||||
* - 'blocks'
|
||||
* - 'collapsible'
|
||||
* - 'group'
|
||||
* - 'row'
|
||||
* - 'tabs'
|
||||
*
|
||||
* - Other fields:
|
||||
* - 'checkbox'
|
||||
* - 'code'
|
||||
* - 'date'
|
||||
* - 'email'
|
||||
* - 'json'
|
||||
* - 'number'
|
||||
* - 'point'
|
||||
* - 'radio'
|
||||
* - 'relationship'
|
||||
* - 'richText'
|
||||
* - 'select'
|
||||
* - 'text'
|
||||
* - 'textarea'
|
||||
* - 'upload'
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const init: Init = async function init(this: ExampleAdapter) {}
|
||||
46
examples/db-example/src/queryDrafts.ts
Normal file
46
examples/db-example/src/queryDrafts.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { PaginatedDocs, QueryDrafts } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Queries for drafts in the specified collection based on the provided criteria using
|
||||
* the incoming where, sort, page query and then only returns the correct drafts in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} collection - The name of the collection to reference for querying drafts.
|
||||
* @param {number} limit - The maximum number of drafts to return.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {number} page - The page number of the results to return.
|
||||
* @param {boolean} pagination - Determines whether pagination is enabled.
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {string} sort - The top-level field to sort the results by.
|
||||
* @param {Where} where - The specific query used to filter drafts.
|
||||
* @returns {Promise<PaginatedDocs<T>>} A promise resolving to the paginated drafts matching the query criteria.
|
||||
*/
|
||||
export const queryDrafts: QueryDrafts = async function queryDrafts(
|
||||
this: ExampleAdapter,
|
||||
{ collection, limit, locale, page, pagination, req = {} as PayloadRequest, sort: sortArg, where },
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to paginate the query results according to your database's methods.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await adapterSpecificModel.paginate(versionQuery, paginationOptions)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
24
examples/db-example/src/transactions/beginTransaction.ts
Normal file
24
examples/db-example/src/transactions/beginTransaction.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { TransactionOptions } from 'mongodb'
|
||||
import type { BeginTransaction } from 'payload/database'
|
||||
|
||||
import type { ExampleAdapter } from '../index'
|
||||
|
||||
/**
|
||||
* Begins a new transaction with the provided options.
|
||||
*
|
||||
* If you want to support database transactions, you would initialize them within this function.
|
||||
* Alternatively, if transactions are not supported or not needed, this function can do nothing and return null.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {TransactionOptions} options - The options for the transaction.
|
||||
* @returns {Promise<null>} A promise resolving to null.
|
||||
*
|
||||
* This function is optional and can be implemented as needed for database adapters that support transactions.
|
||||
*/
|
||||
export const beginTransaction: BeginTransaction = async function beginTransaction(
|
||||
this: ExampleAdapter,
|
||||
options: TransactionOptions,
|
||||
) {
|
||||
return null
|
||||
}
|
||||
14
examples/db-example/src/transactions/commitTransaction.ts
Normal file
14
examples/db-example/src/transactions/commitTransaction.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { CommitTransaction } from 'payload/database'
|
||||
|
||||
/**
|
||||
* Commits a transaction identified by its ID.
|
||||
*
|
||||
* Optional - this method is not required
|
||||
*
|
||||
* @param {string} id - The ID of the transaction to commit.
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* This function is optional and can be implemented as needed for database adapters that support transactions.
|
||||
*/
|
||||
export const commitTransaction: CommitTransaction = async function commitTransaction(id) {}
|
||||
14
examples/db-example/src/transactions/rollbackTransaction.ts
Normal file
14
examples/db-example/src/transactions/rollbackTransaction.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import type { RollbackTransaction } from 'payload/database'
|
||||
|
||||
/**
|
||||
* Rolls back a transaction identified by its ID.
|
||||
*
|
||||
* @param {string} id - The ID of the transaction to rollback.
|
||||
* @returns {Promise<void>}
|
||||
*
|
||||
* This function is optional and can be implemented as needed for database adapters that support transactions.
|
||||
*/
|
||||
export const rollbackTransaction: RollbackTransaction = async function rollbackTransaction(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
id = '',
|
||||
) {}
|
||||
40
examples/db-example/src/updateGlobal.ts
Normal file
40
examples/db-example/src/updateGlobal.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { UpdateGlobal } from 'payload/database'
|
||||
import type { PayloadRequest } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Updates a global document in the specified collection based on the provided criteria.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} slug - The specified slug of the global.
|
||||
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @returns {Promise<T>} A promise resolving to the updated global document.
|
||||
*/
|
||||
export const updateGlobal: UpdateGlobal = async function updateGlobal(
|
||||
this: ExampleAdapter,
|
||||
{ slug, data, req = {} as PayloadRequest },
|
||||
) {
|
||||
/**
|
||||
* Implement the logic to find one and update the document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* result = await adapterSpecificModel.findOneAndUpdate({ globalType: slug }, data, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
let result
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
52
examples/db-example/src/updateGlobalVersion.ts
Normal file
52
examples/db-example/src/updateGlobalVersion.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { UpdateGlobalVersionArgs } from 'payload/database'
|
||||
import type { PayloadRequest, TypeWithID, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Updates a global version document in the specified collection based on the provided criteria using
|
||||
* the incoming ID, global, locale, and versionData, then returns the updated global version document in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} id - The ID of the global version.
|
||||
* @param {string} global - The name of the global to reference for updating a global's version.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {object} versionData - Full version data passed to update the global version.
|
||||
* @param {Where} where - The specific query for querying the global version documents in question to update.
|
||||
* @returns {Promise<any>} A promise resolving to the updated global version document.
|
||||
*/
|
||||
export async function updateGlobalVersion<T extends TypeWithID>(
|
||||
this: ExampleAdapter,
|
||||
{
|
||||
id,
|
||||
global,
|
||||
locale,
|
||||
req = {} as PayloadRequest,
|
||||
versionData,
|
||||
where,
|
||||
}: UpdateGlobalVersionArgs<T>,
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to find one and update the document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const doc = await adapterSpecificModel.findOneAndUpdate(query, versionData, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result = doc
|
||||
|
||||
return result
|
||||
}
|
||||
44
examples/db-example/src/updateOne.ts
Normal file
44
examples/db-example/src/updateOne.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { UpdateOne } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Updates a single document in the specified collection based on the provided criteria using
|
||||
* the incoming ID, collection, locale, and data, then returns the updated document in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} id - The ID of the collection document.
|
||||
* @param {string} collection - The name of the collection to reference for updating one.
|
||||
* @param {object} data - The full data passed to create (data will have all locales and depth 0).
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {Where} where - The specific query used to find the documents for updating.
|
||||
* @returns {Promise<Document>} A promise resolving to the updated document.
|
||||
*/
|
||||
export const updateOne: UpdateOne = async function updateOne(
|
||||
this: ExampleAdapter,
|
||||
{ id, collection, data, locale, req = {} as PayloadRequest, where },
|
||||
) {
|
||||
let result
|
||||
/**
|
||||
* Implement the logic to update one document in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const result = await adapterSpecificModel.findOneAndUpdate(query, data, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
|
||||
return result
|
||||
}
|
||||
45
examples/db-example/src/updateVersion.ts
Normal file
45
examples/db-example/src/updateVersion.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
import type { UpdateVersion } from 'payload/database'
|
||||
import type { PayloadRequest, Where } from 'payload/types'
|
||||
|
||||
import type { ExampleAdapter } from '.'
|
||||
|
||||
/**
|
||||
* Updates a version of a document in the specified collection based on the provided criteria using
|
||||
* the incoming ID, collection, locale, and versionData, then returns the updated version in the format Payload expects.
|
||||
*
|
||||
* @param {ExampleAdapter} this - The ExampleAdapter instance.
|
||||
* @param {string} id - The ID of the collection document.
|
||||
* @param {string} collection - The name of the collection to reference for updating a document's version.
|
||||
* @param {string} locale - The locale being used - can be one locale or "all" (locale="all").
|
||||
* @param {PayloadRequest} req - The Express request object containing the currently authenticated user.
|
||||
* @param {object} versionData - Full version data passed to create the version.
|
||||
* @param {Where} where - The specific query used to find the documents for updating its versions.
|
||||
* @returns {Promise<TypeWithVersion<T>>} A promise resolving to the updated version document.
|
||||
*/
|
||||
export const updateVersion: UpdateVersion = async function updateVersion(
|
||||
this: ExampleAdapter,
|
||||
{ id, collection, locale, req = {} as PayloadRequest, versionData, where },
|
||||
) {
|
||||
let doc
|
||||
/**
|
||||
* Implement the logic to update a document's version in your database.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const doc = await adapterSpecificModel.findOneAndUpdate(query, versionData, options)
|
||||
* ```
|
||||
*/
|
||||
|
||||
/**
|
||||
* This should be the shape of the data that gets returned in Payload when you do:
|
||||
*
|
||||
* ?depth=0&locale=all&fallbackLocale=null
|
||||
*
|
||||
* The result of the outgoing data is always going to be the same shape that Payload expects
|
||||
*
|
||||
*/
|
||||
const result = doc
|
||||
|
||||
return result
|
||||
}
|
||||
24
examples/db-example/tsconfig.json
Normal file
24
examples/db-example/tsconfig.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"noEmit": false /* Do not emit outputs. */,
|
||||
"emitDeclarationOnly": true,
|
||||
"outDir": "./dist" /* Specify an output folder for all emitted files. */,
|
||||
"rootDir": "./src" /* Specify the root folder within your source files. */
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"build",
|
||||
"tests",
|
||||
"test",
|
||||
"node_modules",
|
||||
".eslintrc.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx"
|
||||
],
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", "src/**/*.json"],
|
||||
"references": [{ "path": "../payload" }] // db-mongodb depends on payload
|
||||
}
|
||||
@@ -7,4 +7,4 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
max_line_length = off
|
||||
|
||||
@@ -7,4 +7,4 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
max_line_length = off
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,4 +7,4 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
max_line_length = off
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -7,4 +7,4 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
max_line_length = off
|
||||
|
||||
@@ -7,4 +7,4 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
max_line_length = null
|
||||
max_line_length = off
|
||||
|
||||
@@ -129,6 +129,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@sentry/react": "^7.77.0",
|
||||
"ajv": "^8.12.0"
|
||||
"ajv": "8.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"webpack": "^5.78.0",
|
||||
"webpack-bundle-analyzer": "^4.8.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-middleware": "6.0.1",
|
||||
"webpack-dev-middleware": "6.1.2",
|
||||
"webpack-hot-middleware": "^2.25.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@payloadcms/db-postgres",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.4",
|
||||
"description": "The officially supported Postgres database adapter for Payload",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -45,8 +45,7 @@ export async function createVersion<T extends TypeWithID>(
|
||||
|
||||
const table = this.tables[tableName]
|
||||
|
||||
const relationshipsTable =
|
||||
this.tables[`_${defaultTableName}${this.versionsSuffix}${this.relationshipsSuffix}`]
|
||||
const relationshipsTable = this.tables[`${tableName}${this.relationshipsSuffix}`]
|
||||
|
||||
if (collection.versions.drafts) {
|
||||
await db.execute(sql`
|
||||
|
||||
@@ -10,8 +10,8 @@ import type { GenericColumn, PostgresAdapter } from '../types'
|
||||
import type { BuildQueryJoinAliases, BuildQueryJoins } from './buildQuery'
|
||||
|
||||
import { buildAndOrConditions } from './buildAndOrConditions'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
|
||||
import { createJSONQuery } from './createJSONQuery'
|
||||
import { convertPathToJSONTraversal } from './createJSONQuery/convertPathToJSONTraversal'
|
||||
import { getTableColumnFromPath } from './getTableColumnFromPath'
|
||||
import { operatorMap } from './operatorMap'
|
||||
import { sanitizeQueryValue } from './sanitizeQueryValue'
|
||||
@@ -71,7 +71,7 @@ export async function parseParams({
|
||||
// So we need to loop on keys again here to handle each operator independently
|
||||
const pathOperators = where[relationOrPath]
|
||||
if (typeof pathOperators === 'object') {
|
||||
for (const operator of Object.keys(pathOperators)) {
|
||||
for (let operator of Object.keys(pathOperators)) {
|
||||
if (validOperators.includes(operator as Operator)) {
|
||||
const val = where[relationOrPath][operator]
|
||||
const {
|
||||
@@ -157,6 +157,13 @@ export async function parseParams({
|
||||
break
|
||||
}
|
||||
|
||||
if (
|
||||
operator === 'like' &&
|
||||
(field.type === 'number' || table[columnName].columnType === 'PgUUID')
|
||||
) {
|
||||
operator = 'equals'
|
||||
}
|
||||
|
||||
if (operator === 'like') {
|
||||
constraints.push(
|
||||
and(...val.split(' ').map((word) => ilike(table[columnName], `%${word}%`))),
|
||||
@@ -195,10 +202,10 @@ export async function parseParams({
|
||||
operator === 'not_in'
|
||||
) {
|
||||
constraints.push(
|
||||
sql`${notInArray(table[columnName], queryValue)} OR
|
||||
sql`(${notInArray(table[columnName], queryValue)} OR
|
||||
${table[columnName]}
|
||||
IS
|
||||
NULL`,
|
||||
NULL)`,
|
||||
)
|
||||
|
||||
break
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "payload",
|
||||
"version": "2.16.0",
|
||||
"version": "2.18.3",
|
||||
"description": "Node, React and MongoDB Headless CMS and Application Framework",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
@@ -19,6 +19,7 @@ import { ConfigProvider } from './components/utilities/Config'
|
||||
import { CustomProvider } from './components/utilities/CustomProvider'
|
||||
import { DocumentEventsProvider } from './components/utilities/DocumentEvents'
|
||||
import { I18n } from './components/utilities/I18n'
|
||||
import { LanguageWrap } from './components/utilities/LanguageWrap'
|
||||
import { LoadingOverlayProvider } from './components/utilities/LoadingOverlay'
|
||||
import { LocaleProvider } from './components/utilities/Locale'
|
||||
import { PreferencesProvider } from './components/utilities/Preferences'
|
||||
@@ -46,21 +47,23 @@ const Root = ({ config: incomingConfig }: { config?: SanitizedConfig }) => {
|
||||
<AuthProvider>
|
||||
<PreferencesProvider>
|
||||
<ThemeProvider>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentEventsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>
|
||||
<Routes />
|
||||
</CustomProvider>
|
||||
</NavProvider>
|
||||
</DocumentEventsProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
<LanguageWrap>
|
||||
<SearchParamsProvider>
|
||||
<LocaleProvider>
|
||||
<StepNavProvider>
|
||||
<LoadingOverlayProvider>
|
||||
<DocumentEventsProvider>
|
||||
<NavProvider>
|
||||
<CustomProvider>
|
||||
<Routes />
|
||||
</CustomProvider>
|
||||
</NavProvider>
|
||||
</DocumentEventsProvider>
|
||||
</LoadingOverlayProvider>
|
||||
</StepNavProvider>
|
||||
</LocaleProvider>
|
||||
</SearchParamsProvider>
|
||||
</LanguageWrap>
|
||||
</ThemeProvider>
|
||||
<ModalContainer />
|
||||
</PreferencesProvider>
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { CollectionPermission, GlobalPermission } from '../../../../auth'
|
||||
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
|
||||
import type { SanitizedGlobalConfig } from '../../../../globals/config/types'
|
||||
|
||||
import { getTranslation } from '../../../../utilities/getTranslation'
|
||||
import { formatDate } from '../../../utilities/formatDate'
|
||||
import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
@@ -63,6 +64,12 @@ export const DocumentControls: React.FC<{
|
||||
collection && id && !disableActions && (hasCreatePermission || hasDeletePermission),
|
||||
)
|
||||
|
||||
const collectionLabel = () => {
|
||||
const label = collection?.labels?.singular
|
||||
if (!label) return t('document')
|
||||
return typeof label === 'string' ? label : getTranslation(label, i18n)
|
||||
}
|
||||
|
||||
return (
|
||||
<Gutter className={baseClass}>
|
||||
<div className={`${baseClass}__wrapper`}>
|
||||
@@ -71,12 +78,7 @@ export const DocumentControls: React.FC<{
|
||||
{collection && !isEditing && !isAccountView && (
|
||||
<li className={`${baseClass}__list-item`}>
|
||||
<p className={`${baseClass}__value`}>
|
||||
{t('creatingNewLabel', {
|
||||
label:
|
||||
typeof collection?.labels?.singular === 'string'
|
||||
? collection.labels.singular
|
||||
: t('document'),
|
||||
})}
|
||||
{t('creatingNewLabel', { label: collectionLabel() })}
|
||||
</p>
|
||||
</li>
|
||||
)}
|
||||
|
||||
@@ -32,7 +32,7 @@ export const EditUpload: React.FC<{
|
||||
imageCacheTag?: string
|
||||
showCrop?: boolean
|
||||
showFocalPoint?: boolean
|
||||
}> = ({ fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
|
||||
}> = ({ doc, fileName, fileSrc, imageCacheTag, showCrop, showFocalPoint }) => {
|
||||
const { closeModal } = useModal()
|
||||
const { t } = useTranslation(['general', 'upload'])
|
||||
const { formQueryParams, setFormQueryParams } = useFormQueryParams()
|
||||
@@ -45,10 +45,11 @@ export const EditUpload: React.FC<{
|
||||
y: uploadEdits?.crop?.y || 0,
|
||||
})
|
||||
|
||||
const [pointPosition, setPointPosition] = useState<{ x: number; y: number }>({
|
||||
x: uploadEdits?.focalPoint?.x || 50,
|
||||
y: uploadEdits?.focalPoint?.y || 50,
|
||||
const [focalPosition, setFocalPosition] = useState<{ x: number; y: number }>({
|
||||
x: uploadEdits?.focalPoint?.x || doc.focalX || 50,
|
||||
y: uploadEdits?.focalPoint?.y || doc.focalY || 50,
|
||||
})
|
||||
|
||||
const [checkBounds, setCheckBounds] = useState<boolean>(false)
|
||||
const [originalHeight, setOriginalHeight] = useState<number>(0)
|
||||
const [originalWidth, setOriginalWidth] = useState<number>(0)
|
||||
@@ -72,10 +73,16 @@ export const EditUpload: React.FC<{
|
||||
})
|
||||
}
|
||||
|
||||
const fineTuneFocalPoint = ({ coordinate, value }: { coordinate: 'x' | 'y'; value: string }) => {
|
||||
const fineTuneFocalPosition = ({
|
||||
coordinate,
|
||||
value,
|
||||
}: {
|
||||
coordinate: 'x' | 'y'
|
||||
value: string
|
||||
}) => {
|
||||
const intValue = parseInt(value)
|
||||
if (intValue >= 0 && intValue <= 100) {
|
||||
setPointPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
|
||||
setFocalPosition((prevPosition) => ({ ...prevPosition, [coordinate]: intValue }))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,14 +91,14 @@ export const EditUpload: React.FC<{
|
||||
...formQueryParams,
|
||||
uploadEdits: {
|
||||
crop: crop || undefined,
|
||||
focalPoint: pointPosition ? pointPosition : undefined,
|
||||
focalPoint: focalPosition ? focalPosition : undefined,
|
||||
},
|
||||
})
|
||||
closeModal(editDrawerSlug)
|
||||
}
|
||||
|
||||
const onDragEnd = React.useCallback(({ x, y }) => {
|
||||
setPointPosition({ x, y })
|
||||
setFocalPosition({ x, y })
|
||||
setCheckBounds(false)
|
||||
}, [])
|
||||
|
||||
@@ -104,7 +111,7 @@ export const EditUpload: React.FC<{
|
||||
((boundsRect.left - containerRect.left + boundsRect.width / 2) / containerRect.width) * 100
|
||||
const yCenter =
|
||||
((boundsRect.top - containerRect.top + boundsRect.height / 2) / containerRect.height) * 100
|
||||
setPointPosition({ x: xCenter, y: yCenter })
|
||||
setFocalPosition({ x: xCenter, y: yCenter })
|
||||
}
|
||||
|
||||
const fileSrcToUse = imageCacheTag ? `${fileSrc}?${imageCacheTag}` : fileSrc
|
||||
@@ -180,7 +187,7 @@ export const EditUpload: React.FC<{
|
||||
checkBounds={showCrop ? checkBounds : false}
|
||||
className={`${baseClass}__focalPoint`}
|
||||
containerRef={focalWrapRef}
|
||||
initialPosition={pointPosition}
|
||||
initialPosition={focalPosition}
|
||||
onDragEnd={onDragEnd}
|
||||
setCheckBounds={showCrop ? setCheckBounds : false}
|
||||
>
|
||||
@@ -251,13 +258,13 @@ export const EditUpload: React.FC<{
|
||||
<div className={`${baseClass}__inputsWrap`}>
|
||||
<Input
|
||||
name="X %"
|
||||
onChange={(value) => fineTuneFocalPoint({ coordinate: 'x', value })}
|
||||
value={pointPosition.x.toFixed(0)}
|
||||
onChange={(value) => fineTuneFocalPosition({ coordinate: 'x', value })}
|
||||
value={focalPosition.x.toFixed(0)}
|
||||
/>
|
||||
<Input
|
||||
name="Y %"
|
||||
onChange={(value) => fineTuneFocalPoint({ coordinate: 'y', value })}
|
||||
value={pointPosition.y.toFixed(0)}
|
||||
onChange={(value) => fineTuneFocalPosition({ coordinate: 'y', value })}
|
||||
value={focalPosition.y.toFixed(0)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,6 @@ export const LoadingOverlay: React.FC<Props> = ({
|
||||
show = true,
|
||||
}) => {
|
||||
const { t } = useTranslation('general')
|
||||
|
||||
return (
|
||||
<div
|
||||
className={[
|
||||
@@ -59,25 +58,25 @@ type UseLoadingOverlayToggleT = {
|
||||
}
|
||||
export const LoadingOverlayToggle: React.FC<UseLoadingOverlayToggleT> = ({
|
||||
name: key,
|
||||
type = 'fullscreen',
|
||||
loadingText,
|
||||
show,
|
||||
type = 'fullscreen',
|
||||
}) => {
|
||||
const { toggleLoadingOverlay } = useLoadingOverlay()
|
||||
|
||||
React.useEffect(() => {
|
||||
toggleLoadingOverlay({
|
||||
type,
|
||||
isLoading: show,
|
||||
key,
|
||||
loadingText: loadingText || undefined,
|
||||
type,
|
||||
})
|
||||
|
||||
return () => {
|
||||
toggleLoadingOverlay({
|
||||
type,
|
||||
isLoading: false,
|
||||
key,
|
||||
type,
|
||||
})
|
||||
}
|
||||
}, [show, toggleLoadingOverlay, key, type, loadingText])
|
||||
@@ -94,10 +93,10 @@ type FormLoadingOverlayToggleT = {
|
||||
}
|
||||
export const FormLoadingOverlayToggle: React.FC<FormLoadingOverlayToggleT> = ({
|
||||
name,
|
||||
type = 'fullscreen',
|
||||
action,
|
||||
formIsLoading = false,
|
||||
loadingSuffix,
|
||||
type = 'fullscreen',
|
||||
}) => {
|
||||
const isProcessing = useFormProcessing()
|
||||
const { i18n, t } = useTranslation('general')
|
||||
|
||||
@@ -40,7 +40,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isCreatable,
|
||||
isLoading,
|
||||
isSearchable = true,
|
||||
noOptionsMessage,
|
||||
noOptionsMessage = () => t('general:noOptions'),
|
||||
numberOnly = false,
|
||||
onChange,
|
||||
onMenuOpen,
|
||||
@@ -50,6 +50,8 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
value,
|
||||
} = props
|
||||
|
||||
const loadingMessage = () => t('general:loading') + '...'
|
||||
|
||||
const classes = [className, 'react-select', showError && 'react-select--error']
|
||||
.filter(Boolean)
|
||||
.join(' ')
|
||||
@@ -79,6 +81,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isClearable={isClearable}
|
||||
isDisabled={disabled}
|
||||
isSearchable={isSearchable}
|
||||
loadingMessage={loadingMessage}
|
||||
menuPlacement="auto"
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
onChange={onChange}
|
||||
@@ -148,6 +151,7 @@ const SelectAdapter: React.FC<ReactSelectAdapterProps> = (props) => {
|
||||
isClearable={isClearable}
|
||||
isDisabled={disabled}
|
||||
isSearchable={isSearchable}
|
||||
loadingMessage={loadingMessage}
|
||||
menuPlacement="auto"
|
||||
noOptionsMessage={noOptionsMessage}
|
||||
onChange={onChange}
|
||||
|
||||
@@ -2,9 +2,10 @@ import React, { useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import useHotkey from '../../../hooks/useHotkey'
|
||||
import { useForm } from '../../forms/Form/context'
|
||||
import { useForm, useFormModified } from '../../forms/Form/context'
|
||||
import FormSubmit from '../../forms/Submit'
|
||||
import { useEditDepth } from '../../utilities/EditDepth'
|
||||
import { useOperation } from '../../utilities/OperationProvider'
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
|
||||
|
||||
export type CustomSaveButtonProps = React.ComponentType<
|
||||
@@ -20,17 +21,26 @@ type DefaultSaveButtonProps = {
|
||||
const DefaultSaveButton: React.FC<DefaultSaveButtonProps> = ({ label, save }) => {
|
||||
const ref = useRef<HTMLButtonElement>(null)
|
||||
const editDepth = useEditDepth()
|
||||
const operation = useOperation()
|
||||
const modified = useFormModified()
|
||||
|
||||
const forceDisable = operation === 'update' && !modified
|
||||
|
||||
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if (forceDisable) {
|
||||
return
|
||||
}
|
||||
|
||||
if (ref?.current) {
|
||||
ref.current.click()
|
||||
}
|
||||
})
|
||||
|
||||
return (
|
||||
<FormSubmit buttonId="action-save" onClick={save} ref={ref} type="button" size="small">
|
||||
<FormSubmit buttonId="action-save" onClick={save} ref={ref} size="small" type="button">
|
||||
{label}
|
||||
</FormSubmit>
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useConfig } from '../../utilities/Config'
|
||||
import { useDocumentInfo } from '../../utilities/DocumentInfo'
|
||||
import { useEditDepth } from '../../utilities/EditDepth'
|
||||
import { useLocale } from '../../utilities/Locale'
|
||||
import { useOperation } from '../../utilities/OperationProvider'
|
||||
import RenderCustomComponent from '../../utilities/RenderCustomComponent'
|
||||
|
||||
const baseClass = 'save-draft'
|
||||
@@ -31,12 +32,13 @@ const DefaultSaveDraftButton: React.FC<DefaultSaveDraftButtonProps> = ({
|
||||
const editDepth = useEditDepth()
|
||||
|
||||
useHotkey({ cmdCtrlKey: true, editDepth, keyCodes: ['s'] }, (e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if (disabled) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
if (ref?.current) {
|
||||
ref.current.click()
|
||||
}
|
||||
@@ -68,11 +70,13 @@ export const SaveDraft: React.FC<Props> = ({ CustomComponent }) => {
|
||||
} = useConfig()
|
||||
const { submit } = useForm()
|
||||
const { id, collection, global } = useDocumentInfo()
|
||||
const operation = useOperation()
|
||||
const modified = useFormModified()
|
||||
|
||||
const { code: locale } = useLocale()
|
||||
const { t } = useTranslation('version')
|
||||
|
||||
const canSaveDraft = modified
|
||||
const canSaveDraft = operation === 'update' && modified
|
||||
|
||||
const saveDraft = useCallback(async () => {
|
||||
const search = `?locale=${locale}&depth=0&fallback-locale=null&draft=true`
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
.step-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: calc(var(--base) / 2);
|
||||
|
||||
&::after {
|
||||
@@ -56,8 +57,6 @@
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
max-width: base(8);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -9,12 +9,12 @@ import React, {
|
||||
} from 'react'
|
||||
|
||||
import type { SanitizedCollectionConfig } from '../../../../collections/config/types'
|
||||
import type { Field } from '../../../../fields/config/types'
|
||||
import type { Props as CellProps } from '../../views/collections/List/Cell/types'
|
||||
import type { ListPreferences } from '../../views/collections/List/types'
|
||||
import type { Column } from '../Table/types'
|
||||
import type { Action } from './columnReducer'
|
||||
|
||||
import { type Field, fieldHasSubFields } from '../../../../fields/config/types'
|
||||
import { usePreferences } from '../../utilities/Preferences'
|
||||
import formatFields from '../../views/collections/List/formatFields'
|
||||
import buildColumns from './buildColumns'
|
||||
@@ -35,6 +35,12 @@ export const useTableColumns = (): ITableColumns => useContext(TableColumnContex
|
||||
|
||||
const filterTableFields = (fields: Field[]): Field[] => {
|
||||
return fields.reduce((acc, field) => {
|
||||
if (fieldHasSubFields(field)) {
|
||||
field = {
|
||||
...field,
|
||||
fields: filterTableFields(field.fields),
|
||||
}
|
||||
}
|
||||
if (!field.admin?.disableListColumn) acc.push(field)
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
@@ -58,7 +58,8 @@ const reduceFields = (fields, i18n) =>
|
||||
},
|
||||
}
|
||||
|
||||
if (field.admin?.disableListFilter) return reduced
|
||||
if (field.admin && 'disableListFilter' in field.admin && field.admin?.disableListFilter)
|
||||
return reduced
|
||||
|
||||
return [...reduced, formattedField]
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import type { Props } from './types'
|
||||
|
||||
import { checkbox } from '../../../../../fields/validations'
|
||||
import { getTranslation } from '../../../../../utilities/getTranslation'
|
||||
import { useEditDepth } from '../../../utilities/EditDepth'
|
||||
import DefaultError from '../../Error'
|
||||
import FieldDescription from '../../FieldDescription'
|
||||
import useField from '../../useField'
|
||||
@@ -41,6 +42,8 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
|
||||
const path = pathFromProps || name
|
||||
|
||||
const editDepth = useEditDepth()
|
||||
|
||||
const memoizedValidate = useCallback(
|
||||
(value, options) => {
|
||||
return validate(value, { ...options, required })
|
||||
@@ -62,7 +65,7 @@ const Checkbox: React.FC<Props> = (props) => {
|
||||
}
|
||||
}, [onChange, readOnly, setValue, value])
|
||||
|
||||
const fieldID = `field-${path.replace(/\./g, '__')}`
|
||||
const fieldID = `field-${path.replace(/\./g, '__')}${editDepth > 1 ? `-${editDepth}` : ''}`
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import type { Props } from './types'
|
||||
|
||||
import { getTranslation } from '../../../../../../utilities/getTranslation'
|
||||
import { useEditDepth } from '../../../../utilities/EditDepth'
|
||||
import './index.scss'
|
||||
|
||||
const baseClass = 'radio-input'
|
||||
@@ -12,9 +13,11 @@ const RadioInput: React.FC<Props> = (props) => {
|
||||
const { isSelected, onChange, option, path, readOnly } = props
|
||||
const { i18n } = useTranslation()
|
||||
|
||||
const editDepth = useEditDepth()
|
||||
|
||||
const classes = [baseClass, isSelected && `${baseClass}--is-selected`].filter(Boolean).join(' ')
|
||||
|
||||
const id = `field-${path}-${option.value}`
|
||||
const id = `field-${path}-${option.value}${editDepth > 1 ? `-${editDepth}` : ''}`
|
||||
|
||||
return (
|
||||
<label htmlFor={id}>
|
||||
|
||||
@@ -69,14 +69,24 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
serverURL,
|
||||
} = config
|
||||
|
||||
const hasMultipleRelations = Array.isArray(relationTo)
|
||||
const initialLoadedPageState = hasMultipleRelations
|
||||
? relationTo.reduce((acc, relation) => {
|
||||
return {
|
||||
...acc,
|
||||
[relation]: 0,
|
||||
}
|
||||
}, {})
|
||||
: {}
|
||||
|
||||
const { i18n, t } = useTranslation('fields')
|
||||
const { permissions } = useAuth()
|
||||
const { code: locale } = useLocale()
|
||||
const formProcessing = useFormProcessing()
|
||||
const hasMultipleRelations = Array.isArray(relationTo)
|
||||
const [options, dispatchOptions] = useReducer(optionsReducer, [])
|
||||
const [lastFullyLoadedRelation, setLastFullyLoadedRelation] = useState(-1)
|
||||
const [lastLoadedPage, setLastLoadedPage] = useState<Record<string, number>>({})
|
||||
const [lastLoadedPage, setLastLoadedPage] =
|
||||
useState<Record<string, number>>(initialLoadedPageState)
|
||||
const [errorLoading, setErrorLoading] = useState('')
|
||||
const [filterOptionsResult, setFilterOptionsResult] = useState<FilterOptionsResult>()
|
||||
const [search, setSearch] = useState('')
|
||||
@@ -146,13 +156,12 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
|
||||
if (resultsFetched < 10) {
|
||||
const collection = collections.find((coll) => coll.slug === relation)
|
||||
let fieldToSearch = collection?.defaultSort || collection?.admin?.useAsTitle || 'id'
|
||||
if (!searchArg) {
|
||||
if (typeof sortOptions === 'string') {
|
||||
fieldToSearch = sortOptions
|
||||
} else if (sortOptions?.[relation]) {
|
||||
fieldToSearch = sortOptions[relation]
|
||||
}
|
||||
const fieldToSearch = collection?.admin?.useAsTitle || 'id'
|
||||
let fieldToSort = collection?.defaultSort || 'id'
|
||||
if (typeof sortOptions === 'string') {
|
||||
fieldToSort = sortOptions
|
||||
} else if (sortOptions?.[relation]) {
|
||||
fieldToSort = sortOptions[relation]
|
||||
}
|
||||
|
||||
const query: {
|
||||
@@ -164,7 +173,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
limit: maxResultsPerRequest,
|
||||
locale,
|
||||
page: lastLoadedPageToUse,
|
||||
sort: fieldToSearch,
|
||||
sort: fieldToSort,
|
||||
where: {
|
||||
and: [
|
||||
{
|
||||
@@ -267,7 +276,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
const handleInputChange = useCallback(
|
||||
(searchArg: string, valueArg: Value | Value[]) => {
|
||||
if (search !== searchArg) {
|
||||
setLastLoadedPage({})
|
||||
setLastLoadedPage(initialLoadedPageState)
|
||||
updateSearch(searchArg, valueArg, searchArg !== '')
|
||||
}
|
||||
},
|
||||
@@ -374,7 +383,7 @@ const Relationship: React.FC<Props> = (props) => {
|
||||
|
||||
dispatchOptions({ type: 'CLEAR' })
|
||||
setLastFullyLoadedRelation(-1)
|
||||
setLastLoadedPage({})
|
||||
setLastLoadedPage(initialLoadedPageState)
|
||||
setHasLoadedFirstPage(false)
|
||||
}, [relationTo, filterOptionsResult, locale])
|
||||
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
@import '../../../scss/styles';
|
||||
|
||||
.icon--plus {
|
||||
width: $baseline;
|
||||
height: $baseline;
|
||||
|
||||
.stroke {
|
||||
stroke: var(--theme-elevation-800);
|
||||
stroke-width: $style-stroke-width;
|
||||
|
||||
@@ -3,7 +3,12 @@ import React from 'react'
|
||||
import './index.scss'
|
||||
|
||||
const Plus: React.FC = () => (
|
||||
<svg className="icon icon--plus" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg
|
||||
className="icon icon--plus"
|
||||
viewBox="0 0 25 25"
|
||||
width="25"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<line className="stroke" x1="12.4589" x2="12.4589" y1="16.9175" y2="8.50115" />
|
||||
<line className="stroke" x1="8.05164" x2="16.468" y1="12.594" y2="12.594" />
|
||||
</svg>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import '../../../scss/app.scss'
|
||||
|
||||
const scriptLanguages = ['ar', 'fa']
|
||||
|
||||
export const LanguageWrap: React.FC<{ children?: React.ReactNode }> = ({ children }) => {
|
||||
const { i18n } = useTranslation()
|
||||
const currentLanguage = i18n?.language
|
||||
const isScriptLanguage = currentLanguage && scriptLanguages.includes(currentLanguage)
|
||||
|
||||
return <div className={isScriptLanguage ? `script-language` : ''}>{children}</div>
|
||||
}
|
||||
@@ -27,9 +27,9 @@ const chars = {
|
||||
const baseClass = 'query-inspector'
|
||||
|
||||
const Bracket = ({
|
||||
type,
|
||||
comma = false,
|
||||
position,
|
||||
type,
|
||||
}: {
|
||||
comma?: boolean
|
||||
position: 'end' | 'start'
|
||||
@@ -64,9 +64,9 @@ const RecursivelyRenderObjectData = ({
|
||||
const objectKeys = Object.keys(object)
|
||||
const objectLength = objectKeys.length
|
||||
const [isOpen, setIsOpen] = React.useState<boolean>(true)
|
||||
|
||||
const isNestedAndEmpty = isEmpty && (parentType === 'object' || parentType === 'array')
|
||||
return (
|
||||
<li>
|
||||
<li className={isNestedAndEmpty ? `${baseClass}__row-line--nested` : ''}>
|
||||
<button
|
||||
aria-label="toggle"
|
||||
className={`${baseClass}__list-toggle ${isEmpty ? `${baseClass}__list-toggle--empty` : ''}`}
|
||||
@@ -173,7 +173,7 @@ function createURL(url: string) {
|
||||
|
||||
export const API: React.FC<EditViewProps> = (props) => {
|
||||
const { apiURL } = props
|
||||
const { i18n } = useTranslation()
|
||||
const { i18n, t } = useTranslation()
|
||||
const {
|
||||
localization,
|
||||
routes: { api },
|
||||
@@ -262,14 +262,14 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
<CheckboxInput
|
||||
checked={draft}
|
||||
id="draft-checkbox"
|
||||
label="Draft"
|
||||
label={t('version:draft')}
|
||||
onToggle={() => setDraft(!draft)}
|
||||
/>
|
||||
)}
|
||||
<CheckboxInput
|
||||
checked={authenticated}
|
||||
id="auth-checkbox"
|
||||
label="Authenticated"
|
||||
label={t('authentication:authenticated')}
|
||||
onToggle={() => setAuthenticated(!authenticated)}
|
||||
/>
|
||||
</div>
|
||||
@@ -280,7 +280,7 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
label: locale,
|
||||
value: locale,
|
||||
}}
|
||||
label="Locale"
|
||||
label={t('general:locale')}
|
||||
name="locale"
|
||||
onChange={(e) => setLocale(e.value as string)}
|
||||
options={localeOptions}
|
||||
@@ -292,7 +292,7 @@ export const API: React.FC<EditViewProps> = (props) => {
|
||||
label: depth,
|
||||
value: depth,
|
||||
}}
|
||||
label="Depth"
|
||||
label={t('general:depth')}
|
||||
name="depth"
|
||||
onChange={(e) => setDepth(e.value as string)}
|
||||
options={[
|
||||
|
||||
@@ -116,10 +116,12 @@ export const Upload: React.FC<Props> = (props) => {
|
||||
|
||||
const hasImageSizes = collection?.upload?.imageSizes?.length > 0
|
||||
const hasResizeOptions = Boolean(collection?.upload?.resizeOptions)
|
||||
// Explicitly check if set to true, default is undefined
|
||||
const focalPointEnabled = collection?.upload?.focalPoint === true
|
||||
|
||||
const { collection: { upload: { crop: showCrop = true, focalPoint = true } } = {} } = props
|
||||
|
||||
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions)
|
||||
const showFocalPoint = focalPoint && (hasImageSizes || hasResizeOptions || focalPointEnabled)
|
||||
|
||||
const lastSubmittedTime = submitted ? new Date().toISOString() : null
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import type { CheckboxField } from '../../../../../../../../exports/types'
|
||||
import type { CellComponentProps } from '../../types'
|
||||
@@ -6,9 +7,13 @@ import type { CellComponentProps } from '../../types'
|
||||
import './index.scss'
|
||||
|
||||
// Handles boolean values
|
||||
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => (
|
||||
<code className="bool-cell">
|
||||
<span>{JSON.stringify(data)}</span>
|
||||
</code>
|
||||
)
|
||||
const Checkbox: React.FC<CellComponentProps<CheckboxField>> = ({ data }) => {
|
||||
const { t } = useTranslation('general')
|
||||
if (typeof data !== 'boolean') return null
|
||||
return (
|
||||
<code className="bool-cell">
|
||||
<span>{t(`${data}`).toLowerCase()}</span>
|
||||
</code>
|
||||
)
|
||||
}
|
||||
export default Checkbox
|
||||
|
||||
@@ -200,4 +200,11 @@ dialog {
|
||||
z-index: var(--z-modal);
|
||||
}
|
||||
|
||||
.script-language {
|
||||
& > *,
|
||||
& > * > * {
|
||||
letter-spacing: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@import '~payload-user-css';
|
||||
|
||||
@@ -32,7 +32,7 @@ export const registerLocalStrategy = async ({
|
||||
|
||||
if (existingUser.docs.length > 0) {
|
||||
throw new ValidationError([
|
||||
{ field: 'email', message: 'A user with the given email is already registered' },
|
||||
{ field: 'email', message: req.t('error:userEmailAlreadyRegistered') },
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +130,7 @@ async function create<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'create',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile:
|
||||
|
||||
@@ -157,6 +157,7 @@ async function update<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data: bulkUpdateData,
|
||||
operation: 'update',
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -148,6 +148,8 @@ async function updateByID<TSlug extends keyof GeneratedTypes['collections']>(
|
||||
collection,
|
||||
config,
|
||||
data,
|
||||
operation: 'update',
|
||||
originalDoc,
|
||||
overwriteExistingFiles,
|
||||
req,
|
||||
throwOnMissingFile: false,
|
||||
|
||||
@@ -68,7 +68,6 @@ export type {
|
||||
FieldWithMany,
|
||||
FieldWithMaxDepth,
|
||||
FieldWithPath,
|
||||
FieldWithRichTextRequiredEditor,
|
||||
FieldWithSubFields,
|
||||
FilterOptions,
|
||||
FilterOptionsProps,
|
||||
@@ -87,7 +86,6 @@ export type {
|
||||
RelationshipField,
|
||||
RelationshipValue,
|
||||
RichTextField,
|
||||
RichTextFieldRequiredEditor,
|
||||
RowAdmin,
|
||||
RowField,
|
||||
SelectField,
|
||||
|
||||
@@ -477,6 +477,7 @@ export const richText = baseField.keys({
|
||||
validate: joi.func().required(),
|
||||
})
|
||||
.unknown(),
|
||||
maxDepth: joi.number(),
|
||||
})
|
||||
|
||||
export const date = baseField.keys({
|
||||
@@ -520,6 +521,7 @@ export const ui = joi.object().keys({
|
||||
})
|
||||
.default({}),
|
||||
condition: joi.func(),
|
||||
disableListColumn: joi.boolean().default(false),
|
||||
position: joi.string().valid('sidebar'),
|
||||
width: joi.string(),
|
||||
})
|
||||
|
||||
@@ -390,7 +390,6 @@ export type UIField = {
|
||||
condition?: Condition
|
||||
disableBulkEdit?: boolean
|
||||
disableListColumn?: boolean
|
||||
disableListFilter?: boolean
|
||||
position?: string
|
||||
width?: string
|
||||
}
|
||||
@@ -551,17 +550,13 @@ export type RichTextField<
|
||||
}
|
||||
}
|
||||
editor?: RichTextAdapter<Value, AdapterProps, AdapterProps>
|
||||
/**
|
||||
* Sets a maximum population depth for this field, regardless of the remaining depth when this field is reached.
|
||||
*/
|
||||
maxDepth?: number
|
||||
type: 'richText'
|
||||
} & ExtraProperties
|
||||
|
||||
export type RichTextFieldRequiredEditor<
|
||||
Value extends object = any,
|
||||
AdapterProps = any,
|
||||
ExtraProperties = object,
|
||||
> = Omit<RichTextField<Value, AdapterProps, ExtraProperties>, 'editor'> & {
|
||||
editor: RichTextAdapter<Value, AdapterProps, ExtraProperties>
|
||||
}
|
||||
|
||||
export type ArrayField = FieldBase & {
|
||||
admin?: Admin & {
|
||||
components?: {
|
||||
@@ -679,10 +674,6 @@ export type Field =
|
||||
| UIField
|
||||
| UploadField
|
||||
|
||||
export type FieldWithRichTextRequiredEditor =
|
||||
| Exclude<Field, RichTextField>
|
||||
| RichTextFieldRequiredEditor
|
||||
|
||||
export type FieldAffectingData =
|
||||
| ArrayField
|
||||
| BlockField
|
||||
|
||||
@@ -143,10 +143,13 @@ export const promise = async ({
|
||||
const editor: RichTextAdapter = field?.editor
|
||||
// This is run here AND in the GraphQL Resolver
|
||||
if (editor?.populationPromise) {
|
||||
const populateDepth =
|
||||
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
|
||||
|
||||
const populationPromise = editor.populationPromise({
|
||||
context,
|
||||
currentDepth,
|
||||
depth,
|
||||
depth: populateDepth,
|
||||
draft,
|
||||
field,
|
||||
findMany,
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import Ajv from 'ajv'
|
||||
import ObjectID from 'bson-objectid'
|
||||
|
||||
import type { RichTextAdapter } from '../exports/types'
|
||||
import type {
|
||||
ArrayField,
|
||||
@@ -159,7 +161,7 @@ export const json: Validate<unknown, unknown, JSONField & { jsonError?: string }
|
||||
return true
|
||||
}
|
||||
|
||||
const fetchSchema = ({ uri, schema }) => {
|
||||
const fetchSchema = ({ schema, uri }) => {
|
||||
if (uri && schema) return schema
|
||||
return fetch(uri)
|
||||
.then((response) => {
|
||||
@@ -344,8 +346,12 @@ const validateFilterOptions: Validate = async (
|
||||
const valueIDs: (number | string)[] = []
|
||||
|
||||
values.forEach((val) => {
|
||||
if (typeof val === 'object' && val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
if (typeof val === 'object') {
|
||||
if (val?.value) {
|
||||
valueIDs.push(val.value)
|
||||
} else if (ObjectID.isValid(val)) {
|
||||
valueIDs.push(new ObjectID(val).toHexString())
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
@@ -397,6 +403,10 @@ const validateFilterOptions: Validate = async (
|
||||
if (typeof val === 'string' || typeof val === 'number') {
|
||||
requestedID = val
|
||||
}
|
||||
|
||||
if (typeof val === 'object' && ObjectID.isValid(val)) {
|
||||
requestedID = new ObjectID(val).toHexString()
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(relationTo) && typeof val === 'object' && val?.relationTo) {
|
||||
|
||||
@@ -476,9 +476,12 @@ function buildObjectType({
|
||||
// In the graphql find.ts resolver, the depth is then hard-coded to 0.
|
||||
// Effectively, this means that the populationPromise for GraphQL is only run here, and not in the find.ts resolver / normal population promise.
|
||||
if (editor?.populationPromise) {
|
||||
const populateDepth =
|
||||
field?.maxDepth !== undefined && field?.maxDepth < depth ? field?.maxDepth : depth
|
||||
|
||||
await editor?.populationPromise({
|
||||
context,
|
||||
depth,
|
||||
depth: populateDepth,
|
||||
draft: args.draft,
|
||||
field,
|
||||
findMany: false,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "تمّ التّفعيل بالفعل",
|
||||
"alreadyLoggedIn": "تمّ تسجيل الدّخول بالفعل",
|
||||
"apiKey": "مفتاح API",
|
||||
"authenticated": "مصادق عليه",
|
||||
"backToLogin": "العودة لتسجيل الدخول",
|
||||
"beginCreateFirstUser": "للبدء, قم بإنشاء المستخدم الأوّل.",
|
||||
"changePassword": "تغيير كلمة المرور",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "غير مصرّح لك ، عليك أن تقوم بتسجيل الدّخول لتتمكّن من تقديم هذا الطّلب.",
|
||||
"unknown": "حدث خطأ غير معروف.",
|
||||
"unspecific": "حدث خطأ.",
|
||||
"userEmailAlreadyRegistered": "يوجد مستخدم مسجل بالفعل بهذا البريد الإلكتروني.",
|
||||
"userLocked": "تمّ قفل هذا المستخدم نظرًا لوجود عدد كبير من محاولات تسجيل الدّخول الغير ناجحة.",
|
||||
"valueMustBeUnique": "على القيمة أن تكون فريدة",
|
||||
"verificationTokenInvalid": "رمز التحقّق غير صالح."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "تمّ حذف {{count}} {{label}} بنجاح.",
|
||||
"deletedSuccessfully": "تمّ الحذف بنجاح.",
|
||||
"deleting": "يتمّ الحذف...",
|
||||
"depth": "عمق",
|
||||
"descending": "تنازلي",
|
||||
"deselectAllRows": "إلغاء تحديد جميع الصفوف",
|
||||
"document": "وثيقة",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "خطأ",
|
||||
"errors": "أخطاء",
|
||||
"fallbackToDefaultLocale": "الرجوع إلى اللغة الافتراضية",
|
||||
"false": "كاذب",
|
||||
"filter": "تصفية",
|
||||
"filterWhere": "تصفية {{label}} حيث",
|
||||
"filters": "عوامل التصفية",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} تم استنساخها بنجاح.",
|
||||
"thisLanguage": "العربية",
|
||||
"titleDeleted": "تم حذف {{label}} \"{{title}}\" بنجاح.",
|
||||
"true": "صحيح",
|
||||
"unauthorized": "غير مصرح به",
|
||||
"unsavedChangesDuplicate": "لديك تغييرات لم يتم حفظها. هل تريد الاستمرار في الاستنساخ؟",
|
||||
"untitled": "بدون عنوان",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Artıq Aktivləşdirilib",
|
||||
"alreadyLoggedIn": "Artıq daxil olunub",
|
||||
"apiKey": "API Açarı",
|
||||
"authenticated": "Doğrulandı",
|
||||
"backToLogin": "Girişə qayıt",
|
||||
"beginCreateFirstUser": "Başlamaq üçün ilk istifadəçinizi yaradın.",
|
||||
"changePassword": "Parolu dəyişdir",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "İcazəniz yoxdur, bu tələbi yerinə yetirmək üçün daxil olmalısınız.",
|
||||
"unknown": "Naməlum bir xəta baş verdi.",
|
||||
"unspecific": "Xəta baş verdi.",
|
||||
"userEmailAlreadyRegistered": "Verilən e-poçt ünvanı ilə artıq istifadəçi qeydiyyatdan keçib.",
|
||||
"userLocked": "Bu istifadəçi çoxsaylı uğursuz giriş cəhdləri səbəbindən kilidlənib.",
|
||||
"valueMustBeUnique": "Dəyər təkrar olmamalıdır",
|
||||
"verificationTokenInvalid": "Doğrulama tokenı yanlışdır."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} uğurla silindi.",
|
||||
"deletedSuccessfully": "Uğurla silindi.",
|
||||
"deleting": "Silinir...",
|
||||
"depth": "Dərinlik",
|
||||
"descending": "Azalan",
|
||||
"deselectAllRows": "Bütün sıraları seçimi ləğv edin",
|
||||
"document": "Sənəd",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Xəta",
|
||||
"errors": "Xətalar",
|
||||
"fallbackToDefaultLocale": "Standart lokalə keçid",
|
||||
"false": "Yalan",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "{{label}} filtrlə",
|
||||
"filters": "Filtərlər",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} uğurla dublikatlandı.",
|
||||
"thisLanguage": "Azərbaycan dili",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" uğurla silindi.",
|
||||
"true": "Doğru",
|
||||
"unauthorized": "İcazəsiz",
|
||||
"unsavedChangesDuplicate": "Saxlanılmamış dəyişiklikləriniz var. Dublikatla davam etmək istəyirsiniz?",
|
||||
"untitled": "Başlıqsız",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Вече активиран",
|
||||
"alreadyLoggedIn": "Вече влязъл",
|
||||
"apiKey": "API ключ",
|
||||
"authenticated": "Аутентикиран",
|
||||
"backToLogin": "Обратно към влизане",
|
||||
"beginCreateFirstUser": "За да започнеш, създай първия си потребител",
|
||||
"changePassword": "Промяна на паролата",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Неавторизиран, трябва да влезеш, за да извършиш тази заявка.",
|
||||
"unknown": "Неизвестна грешка.",
|
||||
"unspecific": "Грешка.",
|
||||
"userEmailAlreadyRegistered": "Потребител с дадения имейл вече е регистриран.",
|
||||
"userLocked": "Този потребител има прекалено много невалидни опити за влизане и е заключен.",
|
||||
"valueMustBeUnique": "Стойността трябва да е уникална",
|
||||
"verificationTokenInvalid": "Ключът за верификация е невалиден."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Изтрити {{count}} {{label}} успешно.",
|
||||
"deletedSuccessfully": "Изтрито успешно.",
|
||||
"deleting": "Изтриване...",
|
||||
"depth": "Дълбочина",
|
||||
"descending": "Низходящо",
|
||||
"deselectAllRows": "Деселектирай всички редове",
|
||||
"document": "Документ",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Грешка",
|
||||
"errors": "Грешки",
|
||||
"fallbackToDefaultLocale": "Използвай локализация по подразбиране",
|
||||
"false": "Ложно",
|
||||
"filter": "Филтрирай",
|
||||
"filterWhere": "Филтрирай {{label}} където",
|
||||
"filters": "Филтри",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} успешно дупликиран.",
|
||||
"thisLanguage": "Български",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" успешно изтрит.",
|
||||
"true": "Истина",
|
||||
"unauthorized": "Неавторизиран",
|
||||
"unsavedChangesDuplicate": "Имаш незапазени промени. Искаш ли да продължиш да дупликираш?",
|
||||
"untitled": "Неозаглавен",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Již aktivováno",
|
||||
"alreadyLoggedIn": "Již přihlášen",
|
||||
"apiKey": "API klíč",
|
||||
"authenticated": "Ověřený",
|
||||
"backToLogin": "Zpět na přihlášení",
|
||||
"beginCreateFirstUser": "Začněte vytvořením svého prvního uživatele.",
|
||||
"changePassword": "Změnit heslo",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Neautorizováno, pro zadání tohoto požadavku musíte být přihlášeni.",
|
||||
"unknown": "Došlo k neznámé chybě.",
|
||||
"unspecific": "Došlo k chybě.",
|
||||
"userEmailAlreadyRegistered": "Uživatel s daným e-mailem je již zaregistrován.",
|
||||
"userLocked": "Tento uživatel je uzamčen kvůli příliš mnoha neúspěšným pokusům o přihlášení.",
|
||||
"valueMustBeUnique": "Hodnota musí být jedinečná",
|
||||
"verificationTokenInvalid": "Ověřovací token je neplatný."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Úspěšně smazáno {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Úspěšně odstraněno.",
|
||||
"deleting": "Odstraňování...",
|
||||
"depth": "Hloubka",
|
||||
"descending": "Sestupně",
|
||||
"deselectAllRows": "Zrušte výběr všech řádků",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Chyba",
|
||||
"errors": "Chyby",
|
||||
"fallbackToDefaultLocale": "Zpětné přepnutí do výchozího locale",
|
||||
"false": "Nepravda",
|
||||
"filter": "Filtr",
|
||||
"filterWhere": "Filtrovat {{label}} kde",
|
||||
"filters": "Filtry",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} úspěšně duplikováno.",
|
||||
"thisLanguage": "Čeština",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" úspěšně smazáno.",
|
||||
"true": "Pravda",
|
||||
"unauthorized": "Neoprávněný",
|
||||
"unsavedChangesDuplicate": "Máte neuložené změny. Chtěli byste pokračovat v duplikování?",
|
||||
"untitled": "Bez názvu",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Bereits aktiviert",
|
||||
"alreadyLoggedIn": "Bereits angemeldet",
|
||||
"apiKey": "API-Key",
|
||||
"authenticated": "Authentifiziert",
|
||||
"backToLogin": "Zurück zur Anmeldung",
|
||||
"beginCreateFirstUser": "Erstelle deinen ersten Benutzer um zu beginnen",
|
||||
"changePassword": "Passwort ändern",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Nicht autorisiert - du musst angemeldet sein, um diese Anfrage zu stellen.",
|
||||
"unknown": "Ein unbekannter Fehler ist aufgetreten.",
|
||||
"unspecific": "Ein Fehler ist aufgetreten.",
|
||||
"userEmailAlreadyRegistered": "Ein Benutzer mit der angegebenen E-Mail ist bereits registriert.",
|
||||
"userLocked": "Dieser Benutzer ist auf Grund zu vieler unerfolgreicher Anmelde-Versuche gesperrt.",
|
||||
"valueMustBeUnique": "Wert muss einzigartig sein",
|
||||
"verificationTokenInvalid": "Verifizierungs-Token ist nicht korrekt."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} erfolgreich gelöscht.",
|
||||
"deletedSuccessfully": "Erfolgreich gelöscht.",
|
||||
"deleting": "Lösche...",
|
||||
"depth": "Tiefe",
|
||||
"descending": "Absteigend",
|
||||
"deselectAllRows": "Alle Zeilen abwählen",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Fehler",
|
||||
"errors": "Fehler",
|
||||
"fallbackToDefaultLocale": "Rückgriff auf das Standardgebietsschema",
|
||||
"false": "Falsch",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} wo",
|
||||
"filters": "Filter",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} wurde erfolgreich dupliziert.",
|
||||
"thisLanguage": "Deutsch",
|
||||
"titleDeleted": "{{label}} {{title}} wurde erfolgreich gelöscht.",
|
||||
"true": "Wahr",
|
||||
"unauthorized": "Nicht autorisiert",
|
||||
"unsavedChangesDuplicate": "Du hast ungespeicherte Änderungen, möchtest du mit dem Duplizieren fortfahren?",
|
||||
"untitled": "ohne Titel",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Already Activated",
|
||||
"alreadyLoggedIn": "Already logged in",
|
||||
"apiKey": "API Key",
|
||||
"authenticated": "Authenticated",
|
||||
"backToLogin": "Back to login",
|
||||
"beginCreateFirstUser": "To begin, create your first user.",
|
||||
"changePassword": "Change Password",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Unauthorized, you must be logged in to make this request.",
|
||||
"unknown": "An unknown error has occurred.",
|
||||
"unspecific": "An error has occurred.",
|
||||
"userEmailAlreadyRegistered": "A user with the given email is already registered.",
|
||||
"userLocked": "This user is locked due to having too many failed login attempts.",
|
||||
"valueMustBeUnique": "Value must be unique",
|
||||
"verificationTokenInvalid": "Verification token is invalid."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Deleted {{count}} {{label}} successfully.",
|
||||
"deletedSuccessfully": "Deleted successfully.",
|
||||
"deleting": "Deleting...",
|
||||
"depth": "Depth",
|
||||
"descending": "Descending",
|
||||
"deselectAllRows": "Deselect all rows",
|
||||
"document": "Document",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Error",
|
||||
"errors": "Errors",
|
||||
"fallbackToDefaultLocale": "Fallback to default locale",
|
||||
"false": "False",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} where",
|
||||
"filters": "Filters",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} successfully duplicated.",
|
||||
"thisLanguage": "English",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" successfully deleted.",
|
||||
"true": "True",
|
||||
"unauthorized": "Unauthorized",
|
||||
"unsavedChangesDuplicate": "You have unsaved changes. Would you like to continue to duplicate?",
|
||||
"untitled": "Untitled",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Ya Activado",
|
||||
"alreadyLoggedIn": "Sesión iniciada",
|
||||
"apiKey": "Clave API",
|
||||
"authenticated": "Autenticado",
|
||||
"backToLogin": "Regresar al inicio de sesión",
|
||||
"beginCreateFirstUser": "Para empezar, crea tu primer usuario.",
|
||||
"changePassword": "Cambiar contraseña",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "No autorizado, debes iniciar sesión para realizar esta solicitud.",
|
||||
"unknown": "Ocurrió un error desconocido.",
|
||||
"unspecific": "Ocurrió un error.",
|
||||
"userEmailAlreadyRegistered": "Ya hay un usuario registrado con el correo electrónico proporcionado.",
|
||||
"userLocked": "Este usuario ha sido bloqueado debido a que tiene muchos intentos fallidos para iniciar sesión.",
|
||||
"valueMustBeUnique": "El valor debe ser único",
|
||||
"verificationTokenInvalid": "Token de verificación inválido."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Se eliminó {{count}} {{label}} con éxito.",
|
||||
"deletedSuccessfully": "Borrado exitosamente.",
|
||||
"deleting": "Eliminando...",
|
||||
"depth": "Profundidad",
|
||||
"descending": "Descendente",
|
||||
"deselectAllRows": "Deselecciona todas las filas",
|
||||
"document": "Documento",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Error",
|
||||
"errors": "Errores",
|
||||
"fallbackToDefaultLocale": "Volver a la configuración regional por defecto",
|
||||
"false": "Falso",
|
||||
"filter": "Filtro",
|
||||
"filterWhere": "Filtrar {{label}} donde",
|
||||
"filters": "Filtros",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} duplicado correctamente.",
|
||||
"thisLanguage": "Español",
|
||||
"titleDeleted": "{{label}} {{title}} eliminado correctamente.",
|
||||
"true": "Verdadero",
|
||||
"unauthorized": "No autorizado",
|
||||
"unsavedChangesDuplicate": "Tienes cambios sin guardar. ¿Deseas continuar para duplicar?",
|
||||
"untitled": "Sin título",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "قبلاً فعال شده است",
|
||||
"alreadyLoggedIn": "قبلاً وارد شدهاید",
|
||||
"apiKey": "کلید اِیپیآی",
|
||||
"authenticated": "احراز هویت شده",
|
||||
"backToLogin": "بازگشت به برگه ورود",
|
||||
"beginCreateFirstUser": "برای آغاز، نخستین کاربر خود را بسازید.",
|
||||
"changePassword": "تغییر گذرواژه",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "درخواست نامعتبر، جهت فرستادن این درخواست باید وارد شوید.",
|
||||
"unknown": "یک خطای ناشناخته رخ داد.",
|
||||
"unspecific": "خطایی رخ داد.",
|
||||
"userEmailAlreadyRegistered": "کاربری با ایمیل داده شده قبلاً ثبت نام کرده است.",
|
||||
"userLocked": "این کاربر به دلیل تلاش های زیاد برای ورود ناموفق قفل شده است.",
|
||||
"valueMustBeUnique": "مقدار باید منحصر به فرد باشد",
|
||||
"verificationTokenInvalid": "ژتون تأیید نامعتبر است."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "تعداد {{count}} {{label}} با موفقیت پاک گردید.",
|
||||
"deletedSuccessfully": "با موفقیت حذف شد.",
|
||||
"deleting": "در حال حذف...",
|
||||
"depth": "عمق",
|
||||
"descending": "رو به پایین",
|
||||
"deselectAllRows": "تمام سطرها را از انتخاب خارج کنید",
|
||||
"document": "سند",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "خطا",
|
||||
"errors": "خطاها",
|
||||
"fallbackToDefaultLocale": "بازگردان پیشفرض زبان",
|
||||
"false": "غلط",
|
||||
"filter": "علامتگذاری",
|
||||
"filterWhere": "علامت گذاری کردن {{label}} جایی که",
|
||||
"filters": "علامتگذاریها",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} با موفقیت رونوشت شد.",
|
||||
"thisLanguage": "فارسی",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" با موفقیت پاک شد.",
|
||||
"true": "درست",
|
||||
"unauthorized": "غیرمجاز",
|
||||
"unsavedChangesDuplicate": "شما تغییرات ذخیره نشده دارید. مطمئنید میخواهید به رونوشت ادامه دهید؟",
|
||||
"untitled": "بدون عنوان",
|
||||
|
||||
@@ -2,10 +2,11 @@
|
||||
"$schema": "./translation-schema.json",
|
||||
"authentication": {
|
||||
"account": "Compte",
|
||||
"accountOfCurrentUser": "Compte de l'utilisateur actuel",
|
||||
"accountOfCurrentUser": "Compte de l’utilisateur actuel",
|
||||
"alreadyActivated": "Déjà activé",
|
||||
"alreadyLoggedIn": "Déjà connecté",
|
||||
"apiKey": "Clé API",
|
||||
"authenticated": "Authentifié",
|
||||
"backToLogin": "Retour à la connexion",
|
||||
"beginCreateFirstUser": "Pour commencer, créez votre premier utilisateur.",
|
||||
"changePassword": "Changer le mot de passe",
|
||||
@@ -13,7 +14,7 @@
|
||||
"confirmGeneration": "Confirmer la génération",
|
||||
"confirmPassword": "Confirmez le mot de passe",
|
||||
"createFirstUser": "Créer le premier utilisateur",
|
||||
"emailNotValid": "L'adresse e-mail fourni n'est pas valide",
|
||||
"emailNotValid": "L’adresse e-mail fournie n’est pas valide",
|
||||
"emailSent": "E-mail envoyé",
|
||||
"enableAPIKey": "Activer la clé API",
|
||||
"failedToUnlock": "Déverrouillage échoué",
|
||||
@@ -23,76 +24,77 @@
|
||||
"forgotPasswordQuestion": "Mot de passe oublié ?",
|
||||
"generate": "Générer",
|
||||
"generateNewAPIKey": "Générer une nouvelle clé API",
|
||||
"generatingNewAPIKeyWillInvalidate": "La génération d'une nouvelle clé API <1>invalidera</1> la clé précédente. Êtes-vous sûr de vouloir continuer ?",
|
||||
"lockUntil": "Verrouiller jusqu'à",
|
||||
"generatingNewAPIKeyWillInvalidate": "La génération d’une nouvelle clé API <1>invalidera</1> la clé précédente. Êtes-vous sûr de vouloir continuer ?",
|
||||
"lockUntil": "Verrouiller jusqu’à",
|
||||
"logBackIn": "Se reconnecter",
|
||||
"logOut": "Se déconnecter",
|
||||
"loggedIn": "Pour vous connecter en tant qu'un autre utilisateur, vous devez d'abord vous <0>déconnecter</0>.",
|
||||
"loggedIn": "Pour vous connecter en tant qu’un autre utilisateur, vous devez d’abord vous <0>déconnecter</0>.",
|
||||
"loggedInChangePassword": "Pour changer votre mot de passe, rendez-vous sur votre <0>compte</0> puis modifiez-y votre mot de passe.",
|
||||
"loggedOutInactivity": "Vous avez été déconnecté pour cause d'inactivité.",
|
||||
"loggedOutInactivity": "Vous avez été déconnecté pour cause d’inactivité.",
|
||||
"loggedOutSuccessfully": "Vous avez été déconnecté avec succès.",
|
||||
"login": "Se connecter",
|
||||
"loginAttempts": "Tentatives de connexion",
|
||||
"loginUser": "Connecter l'utilisateur",
|
||||
"loginWithAnotherUser": "Pour vous connecter en tant qu'un autre utilisateur, vous devez d'abord vous <0>déconnecter</0>.",
|
||||
"loginUser": "Connecter l’utilisateur",
|
||||
"loginWithAnotherUser": "Pour vous connecter en tant qu’un autre utilisateur, vous devez d’abord vous <0>déconnecter</0>.",
|
||||
"logout": "Se déconnecter",
|
||||
"logoutUser": "Déconnecter l'utilisateur",
|
||||
"logoutUser": "Déconnecter l’utilisateur",
|
||||
"newAPIKeyGenerated": "Nouvelle clé API générée.",
|
||||
"newAccountCreated": "Un nouveau compte vient d'être créé pour vous permettre d'accéder <a href=\"{{serverURL}}\">{{serverURL}}</a>. Veuillez cliquer sur le lien suivant ou collez l'URL ci-dessous dans votre navigateur pour vérifier votre adresse e-mail: <a href=\"{{verificationURL}}\">{{verificationURL}}</a><br>. Après avoir vérifié votre adresse e-mail, vous pourrez vous connecter avec succès.",
|
||||
"newAccountCreated": "Un nouveau compte vient d’être créé pour vous permettre d’accéder <a href=\"{{serverURL}}\">{{serverURL}}</a>. Veuillez cliquer sur le lien suivant ou collez l’URL ci-dessous dans votre navigateur pour vérifier votre adresse e-mail: <a href=\"{{verificationURL}}\">{{verificationURL}}</a><br>. Après avoir vérifié votre adresse e-mail, vous pourrez vous connecter avec succès.",
|
||||
"newPassword": "Nouveau mot de passe",
|
||||
"resetPassword": "Réinitialiser le mot de passe",
|
||||
"resetPasswordExpiration": "Réinitialiser l'expiration du mot de passe",
|
||||
"resetPasswordExpiration": "Réinitialiser l’expiration du mot de passe",
|
||||
"resetPasswordToken": "Réinitialiser le jeton de mot de passe",
|
||||
"resetYourPassword": "Réinitialisez votre mot de passe",
|
||||
"stayLoggedIn": "Rester connecté",
|
||||
"successfullyUnlocked": "Déverrouillé avec succès",
|
||||
"unableToVerify": "Vérification échoué",
|
||||
"unableToVerify": "Vérification échouée",
|
||||
"verified": "Vérifié",
|
||||
"verifiedSuccessfully": "Vérifié avec succès",
|
||||
"verify": "Vérifier",
|
||||
"verifyUser": "Vérifier l'utilisateur",
|
||||
"verifyUser": "Vérifier l’utilisateur",
|
||||
"verifyYourEmail": "Vérifiez votre e-mail",
|
||||
"youAreInactive": "Vous n'avez pas été actif depuis un moment alors vous serez bientôt automatiquement déconnecté pour votre propre sécurité. Souhaitez-vous rester connecté ?",
|
||||
"youAreReceivingResetPassword": "Vous recevez ceci parce que vous (ou quelqu'un d'autre) avez demandé la réinitialisation du mot de passe de votre compte. Veuillez cliquer sur le lien suivant ou le coller dans votre navigateur pour terminer le processus :",
|
||||
"youDidNotRequestPassword": "Si vous ne l'avez pas demandé, veuillez ignorer cet e-mail et votre mot de passe restera inchangé."
|
||||
"youAreInactive": "Vous n’avez pas été actif depuis un moment alors vous serez bientôt automatiquement déconnecté pour votre propre sécurité. Souhaitez-vous rester connecté ?",
|
||||
"youAreReceivingResetPassword": "Vous recevez ceci parce que vous (ou quelqu’un d’autre) avez demandé la réinitialisation du mot de passe de votre compte. Veuillez cliquer sur le lien suivant ou le coller dans votre navigateur pour terminer le processus :",
|
||||
"youDidNotRequestPassword": "Si vous ne l’avez pas demandé, veuillez ignorer cet e-mail et votre mot de passe restera inchangé."
|
||||
},
|
||||
"error": {
|
||||
"accountAlreadyActivated": "Ce compte a déjà été activé.",
|
||||
"autosaving": "Un problème est survenu lors de l'enregistrement automatique de ce document.",
|
||||
"autosaving": "Un problème est survenu lors de l’enregistrement automatique de ce document.",
|
||||
"correctInvalidFields": "Veuillez corriger les champs invalides.",
|
||||
"deletingFile": "Une erreur s'est produite lors de la suppression du fichier.",
|
||||
"deletingTitle": "Une erreur s'est produite lors de la suppression de {{title}}. Veuillez vérifier votre connexion puis réessayer.",
|
||||
"emailOrPasswordIncorrect": "L'adresse e-mail ou le mot de passe fourni est incorrect.",
|
||||
"followingFieldsInvalid_one": "Le champ suivant n'est pas valide :",
|
||||
"deletingFile": "Une erreur s’est produite lors de la suppression du fichier.",
|
||||
"deletingTitle": "Une erreur s’est produite lors de la suppression de {{title}}. Veuillez vérifier votre connexion puis réessayer.",
|
||||
"emailOrPasswordIncorrect": "L’adresse e-mail ou le mot de passe fourni est incorrect.",
|
||||
"followingFieldsInvalid_one": "Le champ suivant n’est pas valide :",
|
||||
"followingFieldsInvalid_other": "Les champs suivants ne sont pas valides :",
|
||||
"incorrectCollection": "Collection incorrecte",
|
||||
"invalidFileType": "Type de fichier invalide",
|
||||
"invalidFileTypeValue": "Type de fichier invalide : {{value}}",
|
||||
"loadingDocument": "Un problème est survenu lors du chargement du document qui a pour identifiant {{id}}.",
|
||||
"localesNotSaved_one": "Le paramètre régional suivant n'a pas pu être enregistré :",
|
||||
"localesNotSaved_other": "Les paramètres régionaux suivants n'ont pas pu être enregistrés :",
|
||||
"localesNotSaved_one": "Le paramètre régional suivant n’a pas pu être enregistré :",
|
||||
"localesNotSaved_other": "Les paramètres régionaux suivants n’ont pas pu être enregistrés :",
|
||||
"missingEmail": "E-mail manquant.",
|
||||
"missingIDOfDocument": "Il manque l'identifiant du document à mettre à jour.",
|
||||
"missingIDOfVersion": "Il manque l'identifiant de la version.",
|
||||
"missingIDOfDocument": "Il manque l’identifiant du document à mettre à jour.",
|
||||
"missingIDOfVersion": "Il manque l’identifiant de la version.",
|
||||
"missingRequiredData": "Données requises manquantes.",
|
||||
"noFilesUploaded": "Aucun fichier n'a été téléversé.",
|
||||
"noMatchedField": "Aucun champ correspondant n'a été trouvé pour \"{{label}}\"",
|
||||
"noFilesUploaded": "Aucun fichier n’a été téléversé.",
|
||||
"noMatchedField": "Aucun champ correspondant n’a été trouvé pour \"{{label}}\"",
|
||||
"noUser": "Aucun utilisateur",
|
||||
"notAllowedToAccessPage": "Vous n'êtes pas autorisé à accéder à cette page.",
|
||||
"notAllowedToPerformAction": "Vous n'êtes pas autorisé à effectuer cette action.",
|
||||
"notFound": "La ressource demandée n'a pas été trouvée.",
|
||||
"previewing": "Un problème est survenu lors de l'aperçu de ce document.",
|
||||
"notAllowedToAccessPage": "Vous n’êtes pas autorisé à accéder à cette page.",
|
||||
"notAllowedToPerformAction": "Vous n’êtes pas autorisé à effectuer cette action.",
|
||||
"notFound": "La ressource demandée n’a pas été trouvée.",
|
||||
"previewing": "Un problème est survenu lors de l’aperçu de ce document.",
|
||||
"problemUploadingFile": "Il y a eu un problème lors du téléversement du fichier.",
|
||||
"tokenInvalidOrExpired": "Le jeton n'est soit pas valide ou a expiré.",
|
||||
"unPublishingDocument": "Un problème est survenu lors de l'annulation de la publication de ce document.",
|
||||
"tokenInvalidOrExpired": "Le jeton n’est soit pas valide ou a expiré.",
|
||||
"unPublishingDocument": "Un problème est survenu lors de l’annulation de la publication de ce document.",
|
||||
"unableToDeleteCount": "Impossible de supprimer {{count}} sur {{total}} {{label}}.",
|
||||
"unableToUpdateCount": "Impossible de mettre à jour {{count}} sur {{total}} {{label}}.",
|
||||
"unauthorized": "Non autorisé, vous devez être connecté pour effectuer cette demande.",
|
||||
"unknown": "Une erreur inconnue s'est produite.",
|
||||
"unknown": "Une erreur inconnue s’est produite.",
|
||||
"unspecific": "Une erreur est survenue.",
|
||||
"userLocked": "Cet utilisateur est verrouillé en raison d'un trop grand nombre de tentatives de connexion infructueuses.",
|
||||
"userEmailAlreadyRegistered": "Un utilisateur avec l'email donné est déjà enregistré.",
|
||||
"userLocked": "Cet utilisateur est verrouillé en raison d’un trop grand nombre de tentatives de connexion infructueuses.",
|
||||
"valueMustBeUnique": "La valeur doit être unique",
|
||||
"verificationTokenInvalid": "Le jeton de vérification n'est pas valide."
|
||||
"verificationTokenInvalid": "Le jeton de vérification n’est pas valide."
|
||||
},
|
||||
"fields": {
|
||||
"addLabel": "Ajouter {{label}}",
|
||||
@@ -127,7 +129,7 @@
|
||||
"relatedDocument": "Document connexe",
|
||||
"relationTo": "Lié à",
|
||||
"removeRelationship": "Supprimer la relation",
|
||||
"removeUpload": "Supprimer le Téléversement",
|
||||
"removeUpload": "Supprimer le téléversement",
|
||||
"saveChanges": "Sauvegarder les modifications",
|
||||
"searchForBlock": "Rechercher un bloc",
|
||||
"selectExistingLabel": "Sélectionnez {{label}} existant",
|
||||
@@ -146,14 +148,14 @@
|
||||
"aboutToDeleteCount_other": "Vous êtes sur le point de supprimer {{count}} {{label}}",
|
||||
"addBelow": "Ajoutez ci-dessous",
|
||||
"addFilter": "Ajouter un filtre",
|
||||
"adminTheme": "Thème d'administration",
|
||||
"adminTheme": "Thème d’administration",
|
||||
"and": "Et",
|
||||
"applyChanges": "Appliquer les modifications",
|
||||
"ascending": "Ascendant",
|
||||
"automatic": "Automatique",
|
||||
"backToDashboard": "Retour au tableau de bord",
|
||||
"cancel": "Annuler",
|
||||
"changesNotSaved": "Vos modifications n'ont pas été enregistrées. Vous perdrez vos modifications si vous quittez maintenant.",
|
||||
"changesNotSaved": "Vos modifications n’ont pas été enregistrées. Vous perdrez vos modifications si vous quittez maintenant.",
|
||||
"close": "Fermer",
|
||||
"collapse": "Réduire",
|
||||
"collections": "Collections",
|
||||
@@ -170,13 +172,14 @@
|
||||
"created": "Créé(e)",
|
||||
"createdAt": "Créé(e) à",
|
||||
"creating": "création en cours",
|
||||
"creatingNewLabel": "Création d'un(e) nouveau ou nouvelle {{label}}",
|
||||
"dark": "Nuit",
|
||||
"creatingNewLabel": "Création d’un(e) nouveau ou nouvelle {{label}}",
|
||||
"dark": "Sombre",
|
||||
"dashboard": "Tableau de bord",
|
||||
"delete": "Supprimer",
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} supprimé avec succès.",
|
||||
"deletedSuccessfully": "Supprimé(e) avec succès.",
|
||||
"deleting": "Suppression en cours...",
|
||||
"depth": "Profondeur",
|
||||
"descending": "Descendant(e)",
|
||||
"deselectAllRows": "Désélectionner toutes les lignes",
|
||||
"document": "Document",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Erreur",
|
||||
"errors": "Erreurs",
|
||||
"fallbackToDefaultLocale": "Retour à la locale par défaut",
|
||||
"false": "Faux",
|
||||
"filter": "Filtrer",
|
||||
"filterWhere": "Filtrer {{label}} où",
|
||||
"filters": "Filtres",
|
||||
@@ -203,7 +207,7 @@
|
||||
"lastModified": "Dernière modification",
|
||||
"leaveAnyway": "Quitter quand même",
|
||||
"leaveWithoutSaving": "Quitter sans sauvegarder",
|
||||
"light": "Lumière ou Jour",
|
||||
"light": "Clair",
|
||||
"livePreview": "Aperçu",
|
||||
"loading": "Chargement en cours",
|
||||
"locale": "Paramètres régionaux",
|
||||
@@ -215,11 +219,11 @@
|
||||
"noFiltersSet": "Aucun filtre défini",
|
||||
"noLabel": "<Pas de {{label}}>",
|
||||
"noOptions": "Aucune option",
|
||||
"noResults": "Aucun(e) {{label}} trouvé(e). Soit aucun(e) {{label}} n'existe encore, soit aucun(e) ne correspond aux filtres que vous avez spécifiés ci-dessus",
|
||||
"noResults": "Aucun(e) {{label}} trouvé(e). Soit aucun(e) {{label}} n’existe encore, soit aucun(e) ne correspond aux filtres que vous avez spécifiés ci-dessus",
|
||||
"noValue": "Aucune valeur",
|
||||
"none": "Aucun(e)",
|
||||
"notFound": "Pas trouvé",
|
||||
"nothingFound": "Rien n'a été trouvé",
|
||||
"nothingFound": "Rien n’a été trouvé",
|
||||
"of": "de",
|
||||
"open": "Ouvrir",
|
||||
"or": "ou",
|
||||
@@ -250,18 +254,19 @@
|
||||
"successfullyDuplicated": "{{label}} dupliqué(e) avec succès.",
|
||||
"thisLanguage": "Français",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" supprimé(e) avec succès.",
|
||||
"true": "Vrai",
|
||||
"unauthorized": "Non autorisé",
|
||||
"unsavedChangesDuplicate": "Vous avez des changements non enregistrés. Souhaitez-vous continuer la duplication ?",
|
||||
"untitled": "Sans titre",
|
||||
"updatedAt": "Modifié le",
|
||||
"updatedCountSuccessfully": "{{count}} {{label}} mis à jour avec succès.",
|
||||
"updatedSuccessfully": "Mis à jour avec succés.",
|
||||
"updatedSuccessfully": "Mis à jour avec succès.",
|
||||
"updating": "Mise à jour",
|
||||
"uploading": "Téléchargement",
|
||||
"user": "Utilisateur",
|
||||
"users": "Utilisateurs",
|
||||
"value": "Valeur",
|
||||
"welcome": "Bienvenu(e)"
|
||||
"welcome": "Bienvenue"
|
||||
},
|
||||
"operators": {
|
||||
"contains": "contient",
|
||||
@@ -273,24 +278,24 @@
|
||||
"isLessThan": "est inférieur à",
|
||||
"isLessThanOrEqualTo": "est inférieur ou égal à",
|
||||
"isLike": "est comme",
|
||||
"isNotEqualTo": "n'est pas égal à",
|
||||
"isNotIn": "n'est pas dans",
|
||||
"isNotEqualTo": "n’est pas égal à",
|
||||
"isNotIn": "n’est pas dans",
|
||||
"near": "proche"
|
||||
},
|
||||
"upload": {
|
||||
"crop": "Récolte",
|
||||
"crop": "Recadrer",
|
||||
"cropToolDescription": "Faites glisser les coins de la zone sélectionnée, dessinez une nouvelle zone ou ajustez les valeurs ci-dessous.",
|
||||
"dragAndDrop": "Glisser-déposer un fichier",
|
||||
"dragAndDropHere": "ou glissez-déposez un fichier ici",
|
||||
"editImage": "Modifier l'image",
|
||||
"editImage": "Modifier l’image",
|
||||
"fileName": "Nom du fichier",
|
||||
"fileSize": "Taille du fichier",
|
||||
"focalPoint": "Point focal",
|
||||
"focalPointDescription": "Faites glisser le point focal directement sur l'aperçu ou ajustez les valeurs ci-dessous.",
|
||||
"focalPointDescription": "Faites glisser le point focal directement sur l’aperçu ou ajustez les valeurs ci-dessous.",
|
||||
"height": "Hauteur",
|
||||
"lessInfo": "Moins d'infos",
|
||||
"moreInfo": "Plus d'infos",
|
||||
"previewSizes": "Tailles d'aperçu",
|
||||
"lessInfo": "Moins d’infos",
|
||||
"moreInfo": "Plus d’infos",
|
||||
"previewSizes": "Tailles d’aperçu",
|
||||
"selectCollectionToBrowse": "Sélectionnez une collection à parcourir",
|
||||
"selectFile": "Sélectionnez un fichier",
|
||||
"setCropArea": "Définir la zone de recadrage",
|
||||
@@ -300,32 +305,32 @@
|
||||
"width": "Largeur"
|
||||
},
|
||||
"validation": {
|
||||
"emailAddress": "S'il vous plaît, veuillez entrer une adresse e-mail valide.",
|
||||
"enterNumber": "S'il vous plait, veuillez entrer un nombre valide.",
|
||||
"fieldHasNo": "Ce champ n'a pas de {{label}}",
|
||||
"emailAddress": "S’il vous plaît, veuillez entrer une adresse e-mail valide.",
|
||||
"enterNumber": "S’il vous plait, veuillez entrer un nombre valide.",
|
||||
"fieldHasNo": "Ce champ n’a pas de {{label}}",
|
||||
"greaterThanMax": "{{value}} est supérieur au max autorisé {{label}} de {{max}}.",
|
||||
"invalidInput": "Ce champ a une entrée invalide.",
|
||||
"invalidSelection": "Ce champ a une sélection invalide.",
|
||||
"invalidSelections": "Ce champ contient des sélections invalides suivantes :",
|
||||
"invalidSelections": "Ce champ contient les sélections invalides suivantes :",
|
||||
"lessThanMin": "{{value}} est inférieur au min autorisé {{label}} de {{min}}.",
|
||||
"limitReached": "Limite atteinte, seulement {{max}} éléments peuvent être ajoutés.",
|
||||
"longerThanMin": "Cette valeur doit être supérieure à la longueur minimale de {{minLength}} caractères.",
|
||||
"notValidDate": "\"{{value}}\" n'est pas une date valide.",
|
||||
"notValidDate": "\"{{value}}\" n’est pas une date valide.",
|
||||
"required": "Ce champ est requis.",
|
||||
"requiresAtLeast": "Ce champ doit avoir au moins {{count}} {{label}}.",
|
||||
"requiresNoMoreThan": "Ce champ ne doit pas avoir plus de {{count}} {{label}}.",
|
||||
"requiresTwoNumbers": "Ce champ doit avoir deux chiffres.",
|
||||
"shorterThanMax": "Cette valeur doit être inférieure à la longueur maximale de {{maxLength}} caractères.",
|
||||
"trueOrFalse": "Ce champ ne peut être égal qu'à vrai ou faux.",
|
||||
"validUploadID": "Ce champ n'est pas un valide identifiant de fichier."
|
||||
"trueOrFalse": "Ce champ ne peut être égal qu’à vrai ou faux.",
|
||||
"validUploadID": "Ce champ n’est pas un valide identifiant de fichier."
|
||||
},
|
||||
"version": {
|
||||
"aboutToPublishSelection": "Vous êtes sur le point de publier tous les {{label}} de la sélection. Es-tu sûr?",
|
||||
"aboutToRestore": "Vous êtes sur le point de restaurer le document {{label}} à l'état où il se trouvait le {{versionDate}}.",
|
||||
"aboutToRestoreGlobal": "Vous êtes sur le point de restaurer le ou la {{label}} global(e) à l'état où il ou elle se trouvait le {{versionDate}}.",
|
||||
"aboutToPublishSelection": "Vous êtes sur le point de publier tous les {{label}} de la sélection. Êtes-vous sûr ?",
|
||||
"aboutToRestore": "Vous êtes sur le point de restaurer le document {{label}} à l’état où il se trouvait le {{versionDate}}.",
|
||||
"aboutToRestoreGlobal": "Vous êtes sur le point de restaurer le ou la {{label}} global(e) à l’état où il ou elle se trouvait le {{versionDate}}.",
|
||||
"aboutToRevertToPublished": "Vous êtes sur le point de rétablir les modifications apportées à ce document à la version publiée. Êtes-vous sûr ?",
|
||||
"aboutToUnpublish": "Vous êtes sur le point d'annuler la publication de ce document. Êtes-vous sûr ?",
|
||||
"aboutToUnpublishSelection": "Vous êtes sur le point de dépublier tous les {{label}} de la sélection. Es-tu sûr?",
|
||||
"aboutToUnpublish": "Vous êtes sur le point d’annuler la publication de ce document. Êtes-vous sûr ?",
|
||||
"aboutToUnpublishSelection": "Vous êtes sur le point de dépublier tous les {{label}} de la sélection. Êtes-vous sûr ?",
|
||||
"autosave": "Enregistrement automatique",
|
||||
"autosavedSuccessfully": "Enregistrement automatique réussi.",
|
||||
"autosavedVersion": "Version enregistrée automatiquement",
|
||||
@@ -333,7 +338,7 @@
|
||||
"compareVersion": "Comparez cette version à :",
|
||||
"confirmPublish": "Confirmer la publication",
|
||||
"confirmRevertToSaved": "Confirmer la restauration",
|
||||
"confirmUnpublish": "Confirmer l'annulation",
|
||||
"confirmUnpublish": "Confirmer l’annulation",
|
||||
"confirmVersionRestoration": "Confirmer la restauration de la version",
|
||||
"currentDocumentStatus": "Document {{docStatus}} actuel",
|
||||
"draft": "Brouillon",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Već aktivirano",
|
||||
"alreadyLoggedIn": "Već prijavljen",
|
||||
"apiKey": "API ključ",
|
||||
"authenticated": "Autenticiran",
|
||||
"backToLogin": "Nazad na prijavu",
|
||||
"beginCreateFirstUser": "Za početak, kreiraj svog prvog korisnika.",
|
||||
"changePassword": "Promjeni lozinku",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Neovlašten, morate biti prijavljeni da biste uputili ovaj zahtjev.",
|
||||
"unknown": "Došlo je do nepoznate pogreške.",
|
||||
"unspecific": "Došlo je do pogreške.",
|
||||
"userEmailAlreadyRegistered": "Korisnik s navedenom e-poštom je već registriran.",
|
||||
"userLocked": "Ovaj korisnik je zaključan zbog previše neuspješnih pokušaja prijave.",
|
||||
"valueMustBeUnique": "Vrijednost mora biti jedinstvena.",
|
||||
"verificationTokenInvalid": "Verifikacijski token je nevaljan."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "Uspješno izbrisano {{count}} {{label}}.",
|
||||
"deletedSuccessfully": "Uspješno obrisano.",
|
||||
"deleting": "Brisanje...",
|
||||
"depth": "Dubina",
|
||||
"descending": "Silazno",
|
||||
"deselectAllRows": "Odznači sve redove",
|
||||
"document": "Dokument",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Greška",
|
||||
"errors": "Greške",
|
||||
"fallbackToDefaultLocale": "Vraćanje na zadani jezik",
|
||||
"false": "Netočno",
|
||||
"filter": "Filter",
|
||||
"filterWhere": "Filter {{label}} gdje",
|
||||
"filters": "Filteri",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} uspješno duplicirano.",
|
||||
"thisLanguage": "Hrvatski",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" uspješno obrisano.",
|
||||
"true": "Istinito",
|
||||
"unauthorized": "Neovlašteno",
|
||||
"unsavedChangesDuplicate": "Imate nespremljene promjene. Želite li nastaviti s dupliciranjem?",
|
||||
"untitled": "Bez naslova",
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
"alreadyActivated": "Már aktiválva van",
|
||||
"alreadyLoggedIn": "Már bejelentkezett",
|
||||
"apiKey": "API-kulcs",
|
||||
"authenticated": "Hitelesített",
|
||||
"backToLogin": "Vissza a bejelentkezéshez",
|
||||
"beginCreateFirstUser": "Kezdésként hozza létre az első felhasználót.",
|
||||
"changePassword": "Jelszó módosítása",
|
||||
@@ -90,6 +91,7 @@
|
||||
"unauthorized": "Jogosulatlan, a kéréshez be kell jelentkeznie.",
|
||||
"unknown": "Ismeretlen hiba történt.",
|
||||
"unspecific": "Hiba történt.",
|
||||
"userEmailAlreadyRegistered": "A megadott email címmel már regisztráltak egy felhasználót.",
|
||||
"userLocked": "Ez a felhasználó túl sok sikertelen bejelentkezési kísérlet miatt zárolva van.",
|
||||
"valueMustBeUnique": "Az értéknek egyedinek kell lennie",
|
||||
"verificationTokenInvalid": "Az ellenőrző token érvénytelen."
|
||||
@@ -177,6 +179,7 @@
|
||||
"deletedCountSuccessfully": "{{count}} {{label}} sikeresen törölve.",
|
||||
"deletedSuccessfully": "Sikeresen törölve.",
|
||||
"deleting": "Törlés...",
|
||||
"depth": "Mélység",
|
||||
"descending": "Csökkenő",
|
||||
"deselectAllRows": "Jelölje ki az összes sort",
|
||||
"document": "Dokumentum",
|
||||
@@ -195,6 +198,7 @@
|
||||
"error": "Hiba",
|
||||
"errors": "Hibák",
|
||||
"fallbackToDefaultLocale": "Visszatérés az alapértelmezett nyelvhez",
|
||||
"false": "Hamis",
|
||||
"filter": "Szűrő",
|
||||
"filterWhere": "Szűrő {{label}} ahol",
|
||||
"filters": "Szűrők",
|
||||
@@ -250,6 +254,7 @@
|
||||
"successfullyDuplicated": "{{label}} sikeresen duplikálódott.",
|
||||
"thisLanguage": "Magyar",
|
||||
"titleDeleted": "{{label}} \"{{title}}\" sikeresen törölve.",
|
||||
"true": "Igaz",
|
||||
"unauthorized": "Jogosulatlan",
|
||||
"unsavedChangesDuplicate": "Nem mentett módosításai vannak. Szeretné folytatni a duplikációt?",
|
||||
"untitled": "Névtelen",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user