Commit Graph

14214 Commits

Author SHA1 Message Date
Said Akhrarov
cd546b3125 feat(ui): add support for disabling join field row types (#12738)
### What?
This PR adds a new `admin.disableRowTypes` config to `'join'` fields
which hides the `"Type"` column from the relationship table.

### Why?
While the collection type column _can be_ helpful for providing
information, it's not always necessary and can sometimes be redundant
when the field only has a singular relationTo. Hiding it can be helpful
by removing visual noise and providing editors the data directly.

### How?
By threading `admin.disableRowTypes` directly to the `getTableState`
function of the `RelationshipTable` component.

**With row types** (via `admin.disableRowTypes: false | undefined` OR
default for polymorphic):

![image](https://github.com/user-attachments/assets/22b55477-cf56-4b0e-a845-e6f2b39efe3b)

**Without row types** (default for monomorphic):

![image](https://github.com/user-attachments/assets/3a2bb0ba-2d5e-4299-8689-249b2d3fefe2)
2025-10-03 11:10:10 +01:00
Sasha
4b6b0c51fb fix: update packages list for pnpm payload info (#14030)
Updates the packages list for `pnpm payload info` that's used in the
Environment Info issue template section.
2025-10-03 13:07:09 +03:00
Paul
e4f90a24fb fix(templates): ecommerce wrong links in readme and docs and issue with missing graphql dependency (#14045)
Fixes some links in readme and docs and missing graphql dependency for
npm users.
2025-10-02 22:24:35 +00:00
Jarrod Flesch
bffb9ef8b9 fix(ui): saving empty code editor throw error (#14019)
Fixes https://github.com/payloadcms/payload/issues/14006

When attempting to save an empty code editor an error would throw
because `value` was undefined.
2025-10-02 14:31:45 -07:00
Riley Langbein
ece5a95f64 fix(next): prevent locale upsert when not authenticated (#13621)
### What?

When `?locale=` is present in an admin panel URL and that admin panel
URL is visited in an unauthenticated browser, a runtime error is thrown.

### Why?

`upsertPreferences` relies on `req.user` to successfully create a new
`payload-preferences` document. When an unauthenticated user visits a
URL with a `locale` parameter, `upsertPreferences` is called but
`req.user` is not available.

### How?

Prevent `upsertPreferences` from being called when `req.user` is not
available.

Fixes #13581

Co-authored-by: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com>
2025-10-02 13:26:01 -07:00
Paul
d826159fc0 fix(ui): add support back for custom live preview components (#14037)
Fixes https://github.com/payloadcms/payload/issues/13308

Adds support for a custom live preview component back, we previously
supported this and it was allowed via the config types but it wasn't
being rendered.

Now we export the `useLivePreviewContext` hook and the original
`LivePreviewWindow` component too so that end users can wrap the live
preview functionality with anything custom that they may need
2025-10-02 15:09:15 -04:00
Valur Sverrisson
7088d25787 fix(translations): fixes to Icelandic translations (#14038)
### What?
Add various missing translations for Icelandic language and update some
of the current ones to be more human readable.

### Why?
To make it more useable in Icelandic
2025-10-02 16:54:57 +00:00
Jacob Fletcher
2be6bb3c3b feat(ui): live preview conditions (#14012)
Supports live preview conditions. This is essentially access control for
live preview, where you may want to restrict who can use it based on
certain criteria, such as the current user or document data.

To do this, simply return null or undefined from your live preview url
functions:

```ts
url: ({ req }) => (req.user?.role === 'admin' ? '/hello-world' : null)
```

This is also useful for pages which derive their URL from document data,
e.g. a slug field, do not attempt to render the live preview window
until the URL is fully formatted.

For example, if you have a page in your front-end with the URL structure
of `/posts/[slug]`, the slug field is required before the page can
properly load. However, if the slug is not a required field, or when
drafts and/or autosave is enabled, the slug field might not yet have
data, leading to `/posts/undefined` or similar.

```ts
url: ({ data }) => data?.slug ? `/${data.slug}` : null
```

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211513433305000
2025-10-02 10:24:11 -04:00
Patrik
537f58b4bc feat: adds disableGroupBy to fields admin props (#14017)
### What?

Adds a new `disableGroupBy` admin config property for fields to control
their visibility in the list view GroupBy dropdown.

### Why

Previously, the GroupByBuilder was incorrectly using `disableListFilter`
to determine which fields to show in the group-by dropdown.

### How

- Added new `disableGroupBy` property to the field admin config types.
- Updated `GroupByBuilder` to filter fields based on `disableGroupBy`
instead of `disableListFilter`

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211511898438807
2025-10-02 06:22:32 -07:00
Sasha
ef57d24200 fix(drizzle): generate DB schema syntax is deprecated (#14031) 2025-10-02 06:12:49 +03:00
Sasha
9d6cae0445 feat: allow findDistinct on fields nested to relationships and on virtual fields (#14026)
This adds support for using `findDistinct`
https://github.com/payloadcms/payload/pull/13102 on fields:
* Nested to a relationship, for example `category.title`
* Virtual fields that are linked to relationships, for example
`categoryTitle`


```tsx
const Category: CollectionConfig = {
  slug: 'categories',
  fields: [
    {
      name: 'title',
      type: 'text',
    },
  ],
}

const Posts: CollectionConfig = {
  slug: 'posts',
  fields: [
    {
      name: 'category',
      type: 'relationship',
      relationTo: 'categories',
    },
    {
      name: 'categoryTitle',
      type: 'text',
      virtual: 'category.title',
    },
  ],
}

// Supported now
const relationResult = await payload.findDistinct({ collection: 'posts', field: 'category.title' })
// Supported now
const virtualResult = await payload.findDistinct({ collection: 'posts', field: 'categoryTitle' })
```
2025-10-02 05:36:30 +03:00
Sasha
95bdffd11f fix(db-postgres): querying multiple hasMany text or number fields (#14028)
Fixes https://github.com/payloadcms/payload/issues/14023
2025-10-02 02:30:04 +00:00
Sasha
1e654c0a95 fix(db-mongodb): localized blocks with fallback and versions (#13974)
Fixes https://github.com/payloadcms/payload/issues/13663
The issue appears to be reproducible only with 3 or more locales
2025-10-01 21:43:28 -04:00
Marcus Forsberg
4b193dae53 fix(translations): fixes to Swedish translation (#13994)
### What?

Normalizes terminology for trash to Papperskorg and fixes some other
inconsistencies and errors
2025-10-02 00:55:34 +00:00
Alessio Gravili
810da546b6 fix(richtext-lexical): field.admin overrides were ignored in RenderLexical helper (#14024)
We forgot to spread field.admin, thus you were not able to override this
when using `RenderLexical`.
2025-10-01 23:39:44 +00:00
Aurimar Lopes
a938ad6289 fix(templates): correct typo in footer text (#14021)
### What?
Fixed a typo in the ecommerce template footer component.  
Changed "Crafted by Prayload" → "Crafted by Payload" in
`templates/ecommerce/src/components/Footer/index.tsx`.

### Why?
Ensures correct branding and improves professionalism of the template.

### How?
Updated the footer text string directly.

Before:
  Crafted by Prayload  
After:
  Crafted by Payload
2025-10-01 16:20:24 -07:00
Alessio Gravili
9bcb7b0d9d feat: bundle types (#14020)
This PR updates the build process to generate a single
`dist/index.bundled.d.ts` file that bundles all `payload` package types.

Having one bundled declaration file makes it easy to load types into the
Monaco editor (e.g. for the new Code block), enabling full type
completion for the `payload` package.

## Example

```ts
 BlocksFeature({
        blocks: [
          CodeBlock({
            slug: 'PayloadCode',
            languages: {
              ts: 'TypeScript',
            },
            typescript: {
              fetchTypes: [
                {
                  filePath: 'file:///node_modules/payload/index.d.ts',
                  url: 'https://unpkg.com/payload@3.59.0-internal.e247081/dist/index.bundled.d.ts', // <= download bundled .d.ts
                },
              ],
              paths: {
                payload: ['file:///node_modules/payload/index.d.ts'],
              },
              typeRoots: ['node_modules/@types', 'node_modules/payload'],
            },
          }),
        ],
      }),
```

<img width="1506" height="866" alt="Screenshot 2025-10-01 at 12 38
54@2x"
src="https://github.com/user-attachments/assets/135b9b69-058a-42b9-afa0-daa328f64f38"
/>




---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211524241290884
2025-10-01 22:05:37 +00:00
Sasha
5d86d5c486 fix: autosave: true doesn't work on payload.update with where (#14001)
Fixes https://github.com/payloadcms/payload/issues/13952
2025-10-01 21:30:53 +03:00
Jarrod Flesch
d017499b86 fix(plugin-multi-tenant): rm chalk dep (#14003)
Fixes https://github.com/payloadcms/payload/issues/13957

Removes chalk usage and replaces it with a simplified chalk helper since
the usage is so low here.
2025-10-01 14:29:58 -04:00
Jarrod Flesch
2ce6e130c9 fix(storage-uploadthing): hide key field from filters and columns (#14004)
Fixes https://github.com/payloadcms/payload/issues/13992

Hides the key field from filters and columns for the uploadthing plugin.
2025-10-01 14:28:40 -04:00
Sasha
48e9576dec fix(graphql): error querying hasMany relationships when some document was deleted (#14002)
When you have a `hasMany: true` relationship field with at least 1 ID
that references nothing (because the actual document was deleted and
since MongoDB doesn't have foreign constraints - the relationship field
still includes that "dead" ID) graphql querying of that field fails.
This PR fixes it.

The same applies if you don't have access to some document for all DBs
2025-10-01 13:28:17 -04:00
Patrik
accd95ec8a fix(ui): array fields not respecting width styles in row layouts (#13986)
### What?

This PR applies `mergeFieldStyles` to the `ArrayFieldComponent`
component, ensuring that custom admin styles such as `width` are
correctly respected when Array fields are placed inside row layouts.

### Why?

Previously, Array fields did not inherit or apply their `admin.width`
(or other merged field styles). For example, when placing two array
fields side by side inside a row with `width: '50%'`, the widths were
ignored, causing layout issues.

### How?

- Imported and used `mergeFieldStyles` within `ArrayFieldComponent`.
- Applied the merged styles to the root `<div>` via the `style` prop,
consistent with how other field components (like `TextField`) handle
styles.

Fixes #13973 


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211511898438801
2025-10-01 10:15:30 -07:00
Alessio Gravili
54b6f15392 fix(richtext-lexical): slash menu arrows keys not respected when block nearby (#14015)
This PR fixes an issue where the Decorator plugin intercepted arrow
keys, preventing navigation while the slash menu was open. By increasing
the command priority of the slash menu plugin, arrow keys now work
correctly when the menu is active.

## Before


https://github.com/user-attachments/assets/0bffb174-6943-4ef5-9f4a-0d6ea87ce199

## After


https://github.com/user-attachments/assets/99323fb1-dfc7-4b52-8902-efbf7f83ba4d


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211524241290880
2025-10-01 16:38:37 +00:00
Patrik
10e5042adb docs: adds comprehensive virtual field configuration documentation (#13942)
### What?

Updated the existing Virtual Fields section to focus on Join field types
and added a new "Virtual Field Configuration" section covering how to
make any field virtual.

### Why?

The existing virtual fields documentation was lacking depth and clarity.

### How?

- Refined existing h3 "Virtual Fields" section to focus specifically on
Join field types
- Added new h2 "Virtual Field Configuration" section with detailed
coverage of:
    - Boolean virtual fields with hooks for computed values
    - String path virtual fields for relationship resolution
    - Virtual path syntax and requirements
    - Common use cases with practical examples
2025-10-01 11:39:22 -04:00
Sasha
1510e125ab fix(db-postgres): joins count with hasMany relationships (#14008)
Fixes https://github.com/payloadcms/payload/issues/13950
2025-10-01 18:38:54 +03:00
Sasha
267ea9ea36 examples: fix revalidation hook in localization example (#14014)
Fixes https://github.com/payloadcms/payload/issues/13884
2025-10-01 18:38:35 +03:00
Sasha
a5c8b5bf74 fix(sdk): incorrect fetch initialization on cloudflare (#14009)
Fixes
```
TypeError: Illegal invocation: function called with incorrect this reference.
```
error on Cloudflare -
https://github.com/payloadcms/payload/pull/9463#discussion_r2230376819
2025-10-01 15:36:46 +00:00
Elliot DeNolf
209b1f151b chore(eslint): set reportUnusedDisableDirectives to error (#14011)
Sets `reportUnusedDisableDirectives: 'error'` in our eslint config. This
will error when `// eslint-disable-*` directives are unused. It will
also auto-fix if possible.
2025-10-01 10:56:12 -04:00
Jan Beck
de352a6761 fix(plugin-search): handle trashed documents in search plugin sync (#13836)
### What?

Prevents "not found" error when trashing search-enabled documents in
localized site.

### Why?

**See issue https://github.com/payloadcms/payload/issues/13835 for
details and reproduction of bug.**

When a document is soft-deleted (has `deletedAt` timestamp), the search
plugin's `afterChange` hook tries to sync the document but fails because
`payload.findByID()` excludes trashed documents by default.

**Original buggy code** in
`packages/plugin-search/src/utilities/syncDocAsSearchIndex.ts` at lines
46-51:

```typescript
docToSyncWith = await payload.findByID({
  id,
  collection,
  locale: syncLocale,
  req,
  // MISSING: trash parameter!
})
```

### How?

Added detection for trashed documents and include `trash: true`
parameter:

```typescript
// Check if document is trashed (has deletedAt field)
const isTrashDocument = doc && 'deletedAt' in doc && doc.deletedAt

docToSyncWith = await payload.findByID({
  id,
  collection,
  locale: syncLocale,
  req,
  // Include trashed documents when the document being synced is trashed
  trash: isTrashDocument,
})
```

### Test Coverage Added

- **Enabled trash functionality** in Posts collection for plugin-search
tests
- **Added comprehensive e2e test case** in
`test/plugin-search/int.spec.ts` that verifies:
  1. Creates a published post and verifies search document creation
  2. Soft deletes the post (moves to trash)
  3. Verifies search document is properly synced after trash operation
  4. Cleans up by permanently deleting the trashed document

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Patrik Kozak <35232443+PatrikKozak@users.noreply.github.com>
2025-10-01 06:10:40 -07:00
Tobias Odendahl
7eacd396b1 feat(db-mongodb,drizzle): add atomic array operations for relationship fields (#13891)
### What?
This PR adds atomic array operations ($append and $remove) for
relationship fields with `hasMany: true` across all database adapters.
These operations allow developers to add or remove specific items from
relationship arrays without replacing the entire array.

New API:
```
// Append relationships (prevents duplicates)
await payload.db.updateOne({
  collection: 'posts',
  id: 'post123',
  data: {
    categories: { $append: ['featured', 'trending'] }
  }
})

// Remove specific relationships
await payload.db.updateOne({
  collection: 'posts', 
  id: 'post123',
  data: {
    tags: { $remove: ['draft', 'private'] }
  }
})

// Works with polymorphic relationships
await payload.db.updateOne({
  collection: 'posts',
  id: 'post123', 
  data: {
    relatedItems: {
      $append: [
        { relationTo: 'categories', value: 'category-id' },
        { relationTo: 'tags', value: 'tag-id' }
      ]
    }
  }
})
```

### Why?
Currently, updating relationship arrays requires replacing the entire
array which requires fetching existing data before updates. Requiring
more implementation effort and potential for errors when using the API,
in particular for bulk updates.

### How?

#### Cross-Adapter Features:
- Polymorphic relationships: Full support for relationTo:
['collection1', 'collection2']
- Localized relationships: Proper locale handling when fields are
localized
- Duplicate prevention: Ensures `$append` doesn't create duplicates
- Order preservation: Appends to end of array maintaining order
- Bulk operations: Works with `updateMany` for bulk updates

#### MongoDB Implementation:
- Converts `$append` to native `$addToSet` (prevents duplicates in
contrast to `$push`)
- Converts `$remove` to native `$pull` (targeted removal)

#### Drizzle Implementation (Postgres/SQLite):
- Uses optimized batch `INSERT` with duplicate checking for `$append`
- Uses targeted `DELETE` queries for `$remove`
- Implements timestamp-based ordering for performance
- Handles locale columns conditionally based on schema

### Limitations
The current implementation is only on database-adapter level and not
(yet) for the local API. Implementation in the localAPI will be done
separately.
2025-09-30 13:58:09 -04:00
Paul
7601835438 chore: update d1 cloudflare template package versions (#13977)
Bumps from internal until script is updated
2025-09-30 15:14:10 +01:00
Elliot DeNolf
5b64e12c65 chore(release): v3.58.0 [skip ci] 2025-09-30 09:22:02 -04:00
Elliot DeNolf
2e1fb575d1 chore(sdk): add README (#13975)
Adds README
2025-09-30 09:01:25 -04:00
Sasha
f9743b44ee ci: add @payloadcms/sdk to publish list (#13964)
Follow up for https://github.com/payloadcms/payload/pull/9463
2025-09-30 08:45:06 -04:00
Paul
d7888df30e chore: update templates and docs links (#13971)
Updates the templates and docs links for d1 and ecommerce post PR merge
to `/main`
2025-09-30 12:22:28 +00:00
Paul
ef4874b9a0 feat: ecommerce plugin and template (#8297)
This PR adds an ecommerce plugin package with both a Payload plugin and
React UI utilities for the frontend. It also adds a new ecommerce
template and new ecommerce test suite.

It also makes a change to the `cpa` package to accept a `--version` flag
to install a specific version of Payload defaulting to the latest.
2025-09-29 20:05:16 -04:00
Sasha
92a5f075b6 feat: add Payload SDK package (#9463)
Adds Payload SDK package, which can be used to query Payload REST API in
a fully type safe way. Has support for all necessary operations,
including auth, type safe `select`, `populate`, `joins` properties and
simplified file uploading.

Its interface is _very_ similar to the Local API, can't even notice the
difference:
Example:
```ts
import { PayloadSDK } from '@payloadcms/sdk'
import type { Config } from './payload-types'

// Pass your config from generated types as generic
const sdk = new PayloadSDK<Config>({
  baseURL: 'https://example.com/api',
})

// Find operation
const posts = await sdk.find({
  collection: 'posts',
  draft: true,
  limit: 10,
  locale: 'en',
  page: 1,
  where: { _status: { equals: 'published' } },
})

// Find by ID operation
const posts = await sdk.findByID({
  id,
  collection: 'posts',
  draft: true,
  locale: 'en',
})

// Auth login operation
const result = await sdk.login({
  collection: 'users',
  data: {
    email: 'dev@payloadcms.com',
    password: '12345',
  },
})

// Create operation
const result = await sdk.create({
  collection: 'posts',
  data: { text: 'text' },
})

// Create operation with a file
// `file` can be either a Blob | File object or a string URL
const result = await sdk.create({ collection: 'media', file, data: {} })

// Count operation
const result = await sdk.count({ collection: 'posts', where: { id: { equals: post.id } } })

// Update (by ID) operation
const result = await sdk.update({
  collection: 'posts',
  id: post.id,
  data: {
    text: 'updated-text',
  },
})

// Update (bulk) operation
const result = await sdk.update({
  collection: 'posts',
  where: {
    id: {
      equals: post.id,
    },
  },
  data: { text: 'updated-text-bulk' },
})

// Delete (by ID) operation
const result = await sdk.delete({ id: post.id, collection: 'posts' })

// Delete (bulk) operation
const result = await sdk.delete({ where: { id: { equals: post.id } }, collection: 'posts' })

// Find Global operation
const result = await sdk.findGlobal({ slug: 'global' })

// Update Global operation
const result = await sdk.updateGlobal({ slug: 'global', data: { text: 'some-updated-global' } })

// Auth Login operation
const result = await sdk.login({
  collection: 'users',
  data: { email: 'dev@payloadcms.com', password: '123456' },
})

// Auth Me operation
const result = await sdk.me(
  { collection: 'users' },
  {
    headers: {
      Authorization: `JWT  ${user.token}`,
    },
  },
)

// Auth Refresh Token operation
const result = await sdk.refreshToken(
  { collection: 'users' },
  { headers: { Authorization: `JWT ${user.token}` } },
)

// Auth Forgot Password operation
const result = await sdk.forgotPassword({
  collection: 'users',
  data: { email: user.email },
})

// Auth Reset Password operation
const result = await sdk.resetPassword({
  collection: 'users',
  data: { password: '1234567', token: resetPasswordToken },
})

// Find Versions operation
const result = await sdk.findVersions({
  collection: 'posts',
  where: { parent: { equals: post.id } },
})

// Find Version by ID operation
const result = await sdk.findVersionByID({ collection: 'posts', id: version.id })

// Restore Version operation
const result = await sdk.restoreVersion({
  collection: 'posts',
  id,
})

// Find Global Versions operation
const result = await sdk.findGlobalVersions({
  slug: 'global',
})

// Find Global Version by ID operation
const result = await sdk.findGlobalVersionByID({ id: version.id, slug: 'global' })

// Restore Global Version operation
const result = await sdk.restoreGlobalVersion({
  slug: 'global',
  id
})
```



Every operation has optional 3rd parameter which is used to add
additional data to the RequestInit object (like headers):
```ts
await sdk.me({
  collection: "users"
}, {
  // RequestInit object
  headers: {
    Authorization: `JWT ${token}`
  }
})
``` 

To query custom endpoints, you can use the `request` method, which is
used internally for all other methods:
```ts
await sdk.request({
  method: 'POST',
  path: '/send-data',
  json: {
    id: 1,
  },
})
```

Custom `fetch` implementation and `baseInit` for shared `RequestInit`
properties:
```ts
const sdk = new PayloadSDK<Config>({
  baseInit: { credentials: 'include' },
  baseURL: 'https://example.com/api',
  fetch: async (url, init) => {
    console.log('before req')
    const response = await fetch(url, init)
    console.log('after req')
    return response
  },
})
```
2025-09-29 17:01:01 -04:00
Sasha
99043eeaeb feat: adds new Cloudflare D1 SQLite adapter, R2 storage adapter and Cloudflare template (#12537)
This feat adds support for
- [D1 Cloudflare SQLite](https://developers.cloudflare.com/d1/)
- R2 storage directly (previously it was via S3 SDK)
- Cloudflare 1-click deploy template

---------

Co-authored-by: Paul Popus <paul@payloadcms.com>
2025-09-29 16:58:18 -04:00
Patrik
9248fc41e8 fix(ui): query preset where field not displaying array values (#13961)
### What?

Query preset WhereField component was not displaying array values
correctly. When using relationship fields with operators like "is in"
that accept multiple values, the array values were not being formatted
and displayed properly in the query preset modal.

### Why?

The original `renderCondition` function only handled single values and
date objects, but did not include logic for arrays. This caused
relationship fields with multiple selected values to either not display
correctly or throw errors when viewed in query preset modals.

### How?

- Added proper array detection with `Array.isArray()` in the
`renderCondition` function
- Created a reusable `formatValue` helper function that handles single
values, objects (dates), and arrays consistently
- For arrays, format each value and join with commas:
`operatorValue.map(formatValue).join(', ')`
- Enhanced `addListFilter` test helper to accept both single values
(`string`) and arrays (`string[]`) for relationship field testing


---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211334352350024
2025-09-29 13:10:24 -07:00
Alessio Gravili
6a2e8149f1 fix(richtext-lexical): editor re-mounting on save due to json key order not being preserved in postgres (#13962)
Fixes https://github.com/payloadcms/payload/issues/13904

When using Postgres, saving a document can cause the editor to re-mount.
If the document includes a blocks field, this leads to the editor
incorrectly resetting its value to the previous state. The issue occurs
because the editor is re-mounted without recalculating the initial
Lexical state.

This re-mounting behavior is caused by how Postgres handles JSON
storage:
- With `jsonb` columns, unlike `json` columns, object key order is not
guaranteed.
- As a result, saving and reloading the Lexical editor state shifts key
order, causing `JSON.stringify()` comparisons to fail.

## Solution

To fix this, we now compare the incoming and previous editor state in a
way that ignores key order. Specifically, we switched from
`JSON.stringify()` to `dequal` for deep equality checks.

## Notes

- This code only runs when external changes occur (e.g. after saving a
document or when custom code modifies form fields).
- Because this check is infrequent, performance impact is negligible.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211488746010087
2025-09-29 21:07:34 +01:00
Alessio Gravili
41aa201f7b fix: ensure blocks filterOptions are awaited (#13960)
Fixes #13956
2025-09-29 17:48:30 +00:00
Elliot DeNolf
6d995ffb91 ci: add cron to activity notifications [skip ci] (#13959)
Set GitHub activity notifications to run on Monday mornings
2025-09-29 11:10:47 -04:00
Elliot DeNolf
2514e4ddc3 ci: proper path for activity notifications [skip ci] 2025-09-29 11:02:25 -04:00
Elliot DeNolf
ba33f2f0a7 ci: activity-notifications debug inputs 2025-09-29 10:59:28 -04:00
Elliot DeNolf
f7b1b7b839 chore: rename activity notifications workflow 2025-09-29 10:57:15 -04:00
Elliot DeNolf
4562df735d ci: github activity slack notifications (#13955)
Slack notifications for popular and new issues
2025-09-29 10:55:43 -04:00
Jacob Fletcher
3c4f8a3508 fix(next): static live preview url corrupt after save (#13949)
As of #13631, statically defined live preview URLs become corrupt after
the first save.

For example, if you define your URL as a string like this:

```ts
import type { CollectionConfig } from 'payload'

const MyCollection: CollectionConfig = {
  // ...
  admin: {
    livePreview: {
      url: '/hello-world'
    }
  }
}
```

On initial load, the iframe's src will evaluate to `.../hello-world` as
expected, but from the first save onward, the url becomes
`.../undefined`.

This is because for statically defined URLs, the `livePreviewURL`
property does not exist on the response. Despite this, we set it into
state as undefined. This is true for both collections and globals.

Initially reported on Discord here:
https://discord.com/channels/967097582721572934/967097582721572937/1421166976113442847

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211478736160152
2025-09-26 21:23:04 +00:00
Patrik
ae34b6d6d1 fix(ui): move collection description below title in document view (#13946)
### What?

Moved description rendering from DocumentFields to DocumentHeader
component.

### List view

#### Before 
<img width="1315" height="696" alt="Screenshot 2025-09-26 at 10 12
14 AM"
src="https://github.com/user-attachments/assets/9c102f4b-ed71-4e3d-85d6-87464e6c8568"
/>

#### After
<img width="1647" height="762" alt="Screenshot 2025-09-26 at 1 24 12 PM"
src="https://github.com/user-attachments/assets/1c2f4eae-5bf8-43ad-af65-23f333b01ba8"
/>


### Document View

#### Before
<img width="1321" height="673" alt="Screenshot 2025-09-26 at 10 57
01 AM"
src="https://github.com/user-attachments/assets/3c6c7218-a8f6-4e52-af27-f0c4ffa0a6ef"
/>

#### After
<img width="1645" height="682" alt="Screenshot 2025-09-26 at 1 24 29 PM"
src="https://github.com/user-attachments/assets/1ac774c7-8820-4d41-afef-c60044383474"
/>


### Document Drawer

<img width="1631" height="631" alt="Screenshot 2025-09-26 at 1 24 49 PM"
src="https://github.com/user-attachments/assets/42285d23-a37d-4419-9644-f9c27358f2bf"
/>

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211478222789332
2025-09-26 12:15:45 -07:00
Jacob Fletcher
17520439e5 fix: sanitize collection labels to inherit defaults when only a partial config is provided (#13944)
When only a partial `labels` config is defined on a collection, the
collection defaults do not apply as expected. This leads to undefined
`singular` or `plural` properties that render as either empty or
untranslated strings on the front-end.

For example:

```ts
import type { CollectionConfig } from 'payload'

export MyCollection: CollectionConfig = {
  // ... 
  labels: {
    plural: 'Pages', // Notice that `singular` is excluded here
  },
}
```

This renders empty or untranslated strings throughout the admin panel,
here are a couple examples:

<img width="326" height="211" alt="Screenshot 2025-09-26 at 10 27 40 AM"
src="https://github.com/user-attachments/assets/3872c4dd-0dac-4c1c-b417-61ddd042bbb8"
/>

<img width="330" height="267" alt="Screenshot 2025-09-26 at 10 27 51 AM"
src="https://github.com/user-attachments/assets/78772405-b5f3-45fa-9bf0-bc078f1ba976"
/>

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211478736160147
2025-09-26 15:24:11 +00:00
Jarrod Flesch
2cc34d1a4a fix(plugin-multi-tenant): properly localize labels (#13943)
Fixes https://github.com/payloadcms/payload/issues/13940

Use `getTranslation` for translating localized collection labels.
2025-09-26 07:48:11 -07:00