## Summary
Today the SDK lets apps declare `filters` on a view but not `sorts`, so
any view installed via an app manifest can never have a default
ordering. This PR adds declarative view sorts end-to-end: SDK manifest
type, `defineView` validation, CLI scaffold, and the application
install/sync pipeline that converts the manifest into the universal flat
entity used by workspace migrations. The persistence layer
(`ViewSortEntity`, resolvers, action handlers, builders…) already
existed server-side; the missing piece was the manifest → universal-flat
converter and the relation wiring on `view`.
## Changes
**`twenty-shared`**
- Add `ViewSortDirection` enum (`ASC` | `DESC`) and re-export it from
`twenty-shared/types`.
- Add `ViewSortManifest` type and an optional `sorts?:
ViewSortManifest[]` on `ViewManifest`, exported from
`twenty-shared/application`.
**`twenty-sdk`**
- Validate `sorts` entries in `defineView` (`universalIdentifier`,
`fieldMetadataUniversalIdentifier`, `direction` ∈ `ASC`/`DESC`).
- Add a commented `// sorts: [ ... ]` example to the CLI view scaffold
template + matching snapshot assertion.
**`twenty-server`**
- Re-export `ViewSortDirection` from `twenty-shared/types` in
`view-sort/enums/view-sort-direction.ts` (single source of truth,
backward compatible for existing imports).
- New converter `fromViewSortManifestToUniversalFlatViewSort` (+ unit
tests for `ASC` and `DESC`).
- Wire the converter into
`computeApplicationManifestAllUniversalFlatEntityMaps` so
`viewManifest.sorts` are added to `flatViewSortMaps`, mirroring how
filters are processed.
- Replace the `// @ts-expect-error TODO migrate viewSort to v2 /
viewSorts: null` placeholder in `ALL_ONE_TO_MANY_METADATA_RELATIONS`
with the proper relation (`viewSortIds` /
`viewSortUniversalIdentifiers`).
- Update affected snapshots (`get-metadata-related-metadata-names`,
`all-universal-flat-entity-foreign-key-aggregator-properties`).
## Example usage
\`\`\`ts
defineView({
name: 'All issues',
objectUniversalIdentifier: 'issue',
sorts: [
{
universalIdentifier: 'all-issues__sort-created-at',
fieldMetadataUniversalIdentifier: 'createdAt',
direction: 'DESC',
},
],
});
\`\`\`
56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
import {
|
|
ViewFilterGroupLogicalOperator,
|
|
ViewOpenRecordIn,
|
|
ViewSortDirection,
|
|
ViewType,
|
|
ViewVisibility,
|
|
} from 'twenty-shared/types';
|
|
|
|
import { type ViewFilterGroupEntity } from 'src/engine/metadata-modules/view-filter-group/entities/view-filter-group.entity';
|
|
import { type ViewSortEntity } from 'src/engine/metadata-modules/view-sort/entities/view-sort.entity';
|
|
import { type ViewEntity } from 'src/engine/metadata-modules/view/entities/view.entity';
|
|
|
|
export const createViewData = (overrides: Partial<ViewEntity> = {}) => ({
|
|
name: 'Test View',
|
|
icon: 'IconTable',
|
|
type: ViewType.TABLE,
|
|
key: null,
|
|
position: 0,
|
|
isCompact: false,
|
|
openRecordIn: ViewOpenRecordIn.SIDE_PANEL,
|
|
visibility: ViewVisibility.WORKSPACE,
|
|
...overrides,
|
|
});
|
|
|
|
export const createViewSortData = (
|
|
viewId: string,
|
|
overrides: Partial<ViewSortEntity> = {},
|
|
) => ({
|
|
viewId,
|
|
direction: ViewSortDirection.ASC,
|
|
...overrides,
|
|
});
|
|
|
|
export const updateViewSortData = (
|
|
overrides: Partial<ViewSortEntity> = {},
|
|
) => ({
|
|
direction: ViewSortDirection.DESC,
|
|
...overrides,
|
|
});
|
|
|
|
export const createViewFilterGroupData = (
|
|
viewId: string,
|
|
overrides: Partial<ViewFilterGroupEntity> = {},
|
|
) => ({
|
|
viewId,
|
|
logicalOperator: ViewFilterGroupLogicalOperator.AND,
|
|
...overrides,
|
|
});
|
|
|
|
export const updateViewFilterGroupData = (
|
|
overrides: Partial<ViewFilterGroupEntity> = {},
|
|
) => ({
|
|
logicalOperator: ViewFilterGroupLogicalOperator.OR,
|
|
...overrides,
|
|
});
|