Compare commits

..
Author SHA1 Message Date
Weiko 845b9ff6ba fix 2026-04-13 14:08:21 +02:00
Weiko 992ff40ac3 Fix duplicate tabs and widgets 2026-04-12 22:27:30 +02:00
4e46aa32bb i18n - translations (#19612)
Created by Github action

---------

Co-authored-by: github-actions <github-actions@twenty.com>
2026-04-12 20:33:07 +02:00
WeikoandGitHub 9e385e44f9 Fields widget draft view (#19562)
- Defer backend view creation for FIELDS widgets to prevent orphan views
when users cancel or leave before saving. Views are now created
optimistically in Jotai state (client-side UUID) and only persisted to
the database when the page layout is saved.
- Align the frontend's default field groups fallback with the backend
logic: split into General/Other groups, hide relation fields by default,
and filter invisible fields consistently across all code paths.
2026-04-12 20:26:47 +02:00
Charles BochetandGitHub 63806b24fe Export field settings types from SDK public API (#19611)
## Summary

- Follows up on #19610 which exported view/page-layout types but missed
field settings types
- Adds re-exports for `DateDisplayFormat`, `NumberDataType`, and
`FieldMetadataSettingsOnClickAction` from the SDK public API
- These enums are referenced in the return type of `defineObject` (via
field settings types like `FieldMetadataSettingsDate`,
`FieldMetadataSettingsNumber`, `FieldMetadataSettingsMultiItem`) but
were not publicly exported
- This causes TS4082 ("private name") errors for SDK consumers that have
`declaration: true` in their tsconfig, specifically when using
`defineObject` with fields that have settings (e.g. SELECT, DATE, NUMBER
fields)
2026-04-12 19:35:37 +02:00
Charles BochetandGitHub 7540bb064f Re-export missing types from SDK public API (#19610)
## Summary

- Fixes TS4082 errors for SDK consumers with `declaration: true` in
their tsconfig
- The `define*` functions (e.g. `defineView`, `definePageLayout`,
`defineLogicFunction`) return `ValidationResult<T>` where `T` references
types from `twenty-shared` that were not re-exported from the SDK's
public barrel
- TypeScript cannot generate `.d.ts` files when the return type
references "private names" — types that exist in the package but aren't
publicly exported

### Added re-exports

**Enums** (used in `ViewManifest`, `HttpRouteTriggerSettings`):
- `ViewType`, `ViewFilterOperand`, `ViewFilterGroupLogicalOperator`,
`ViewOpenRecordIn`, `ViewVisibility`
- `HTTPMethod`

**Types** (used in `PageLayoutWidgetManifest`, `LogicFunctionConfig`):
- `GridPosition`, `PageLayoutWidgetConditionalDisplay`
- `InputJsonSchema`
2026-04-12 18:33:14 +02:00
Charles BochetandGitHub ad1a4ecca0 Add isUnique support for application-defined fields (#19609)
## Summary

- Adds `isUnique?: boolean` to `RegularFieldManifest` in
`twenty-shared`, allowing SDK applications to declare unique constraints
on fields
- Updates the manifest-to-flat-field converter to read `isUnique` from
the manifest instead of hardcoding `false`
- Generates corresponding unique index metadata in
`computeApplicationManifestAllUniversalFlatEntityMaps` when a field has
`isUnique: true`, matching the behavior of the `CreateFieldInput` path
- Adds SDK-side validation rejecting `isUnique` on RELATION,
MORPH_RELATION, and FILES field types
- Adds integration test verifying manifest sync creates a unique index
for `isUnique` fields
- Adds SDK unit tests for `isUnique` validation on unsupported field
types

## Test plan

- [x] SDK unit tests: `defineField` accepts `isUnique: true` on TEXT,
rejects on RELATION and FILES
- [ ] Integration test: manifest sync with `isUnique: true` creates the
unique index in DB
- [ ] Verify `isUnique` defaults to `false` when not specified (backward
compatible)
- [ ] Verify standalone manifest fields (not nested in objects) also
generate unique indexes correctly
2026-04-12 17:03:09 +02:00
Thomas des FrancsandGitHub d9d3648baa Halftone studio v3 (glass effect) (#19598)
## Summary
- refine `/halftone` controls for material selection and slower 3D
rotation tuning
- switch the default halftone material to solid for new sessions
- include related halftone rendering, export, and glass material updates
across the website app

## Testing
- `yarn nx run twenty-website-new:typecheck`
- `yarn prettier
packages/twenty-website-new/src/app/halftone/_components/controls/AnimationsTab.tsx
--check`
- `yarn prettier
packages/twenty-website-new/src/app/halftone/_components/controls/controls-ui.tsx
packages/twenty-website-new/src/app/halftone/_components/controls/DesignTab.tsx
--check`
- `yarn prettier
packages/twenty-website-new/src/app/halftone/_lib/state.ts --check`
2026-04-12 14:40:52 +00:00
Thomas des FrancsandGitHub fda5aba9ec small fixes on pricing (#19603)
## Summary
- update pricing card styling to use a 4px border radius
- make pricing card CTAs fill the available card width
- reduce spacing below the pricing cards section
- fix pricing copy casing and punctuation, including the tailor-made
plan banner
- make engagement band headings support a configurable size and default
them to the smaller variant

## Testing
- Not run (not requested)
2026-04-12 14:39:34 +00:00
Charles BochetandGitHub 4264741281 Clear stale SDK config on uninstall and invalid client (#19608)
## Summary
- After a successful `uninstall`, clear all app registration config
(`appRegistrationId`, `appRegistrationClientId`, `appAccessToken`,
`appRefreshToken`) so the next `dev` run doesn't hit "app not found"
errors from leftover credentials
- On app re-registration (`ensureAppRegistration`), clear stale
`appAccessToken`/`appRefreshToken` that belonged to the previous
registration
- On OAuth token refresh failure, only clear config when the server
returns `invalid_client` (registration was deleted), not on transient
failures like network issues
- Extract a shared `parse-server-error` utility for consistently parsing
both GraphQL error codes (`extensions.code`/`subCode`) and OAuth error
responses (`error`/`error_description`)
2026-04-12 16:29:29 +02:00
Charles BochetandGitHub 6e259d3ded Inline twenty-shared types in SDK declarations (#19605)
## Summary
- `twenty-shared` is private and never published to npm, so SDK
consumers couldn't resolve type imports like `from
'twenty-shared/types'` in the generated `.d.ts` files
- Replace `vite.config.sdk.ts` with `rollup-plugin-dts` which compiles
`src/sdk/index.ts` directly into a self-contained `dist/sdk/index.d.ts`
with all `twenty-shared` types inlined
- The JS output from `vite.config.sdk.ts` (`dist/sdk/*.js`) was unused —
the main export already maps to `dist/index.mjs` from the node Vite
config

## Changes
- **Deleted** `vite.config.sdk.ts` — its preserved-module JS output
wasn't referenced by any `package.json` export
- **Added** `rollup.config.dts.mjs` — uses `rollup-plugin-dts` to
compile SDK types from source with `twenty-shared` inlined (~850ms)
- **Updated** `project.json` — build/dev/build:sdk targets now use
rollup instead of the removed vite config
- **Updated** `tsconfig.json` — removed `vite.config.sdk.ts` from
include

## Test plan
- [ ] Run `npx nx build twenty-sdk` and verify `dist/sdk/index.d.ts`
contains no `twenty-shared` references
- [ ] Verify `dist/index.mjs` and `dist/index.cjs` are still produced
correctly
- [ ] Verify CLI (`dist/cli.cjs`) still works
- [ ] Verify `npx nx build:sdk twenty-sdk` works standalone


Made with [Cursor](https://cursor.com)
2026-04-12 15:45:31 +02:00
Charles BochetandGitHub 3ae63f0574 Fix syncApplication failing when navigation menu item child is listed before folder in manifest (#19599)
## Summary

- Fix `syncApplication` crashing with `ENTITY_NOT_FOUND` when a
navigation menu item child (with `folderUniversalIdentifier`) appears
before its folder in the manifest's `navigationMenuItems` array
- Add a generic topological sort in
`WorkspaceEntityMigrationBuilderService.validateAndBuild()` that detects
self-referential FKs from `ALL_MANY_TO_ONE_METADATA_RELATIONS` and
ensures parents are created before children
- This also covers other self-referential entities:
`viewFilterGroup.parentViewFilterGroup`,
`fieldMetadata.relationTargetFieldMetadata`, and
`rowLevelPermissionPredicateGroup.parentRowLevelPermissionPredicateGroup`

## Root cause

The builder iterated `createdFlatEntityMaps.byUniversalIdentifier` in
insertion order (from the manifest). Validation passed because it
checked both optimistic maps and remaining-to-create maps. But the
runner processed create actions sequentially, so
`resolveUniversalRelationIdentifiersToIds` threw when the folder hadn't
been created yet.
2026-04-12 12:35:38 +02:00
Charles BochetandGitHub c8f5ecb2b6 Add APPLICATION_LOG_DRIVER=CONSOLE to twenty-app-dev container (#19600)
## Summary
- Enable application log console output in the `twenty-app-dev` Docker
image by adding `APPLICATION_LOG_DRIVER=CONSOLE` to the Dockerfile ENV
block
- Rename `APPLICATION_LOG_DRIVER_TYPE` to `APPLICATION_LOG_DRIVER` and
`ApplicationLogDriverType` to `ApplicationLogDriver` for consistency
with all other driver config variables (`EMAIL_DRIVER`, `LOGGER_DRIVER`,
`CAPTCHA_DRIVER`, etc.)
- Add `APPLICATION_LOG_DRIVER` to `.env.example`
2026-04-12 11:55:38 +02:00
136 changed files with 5211 additions and 2529 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "create-twenty-app",
"version": "1.22.0-canary.3",
"version": "1.22.0-canary.6",
"description": "Command-line interface to create Twenty application",
"main": "dist/cli.cjs",
"bin": "dist/cli.cjs",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-client-sdk",
"version": "1.22.0-canary.3",
"version": "1.22.0-canary.6",
"sideEffects": false,
"license": "AGPL-3.0",
"scripts": {
+2 -1
View File
@@ -215,7 +215,8 @@ ENV PG_DATABASE_URL=postgres://twenty:twenty@localhost:5432/default \
DISABLE_DB_MIGRATIONS=true \
DISABLE_CRON_JOBS_REGISTRATION=true \
IS_BILLING_ENABLED=false \
SIGN_IN_PREFILLED=true
SIGN_IN_PREFILLED=true \
APPLICATION_LOG_DRIVER=CONSOLE
EXPOSE 2020
VOLUME ["/data/postgres", "/app/packages/twenty-server/.local-storage"]
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} krediete"
msgid "{totalCount} selected"
msgstr "{totalCount} geselekteer"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} velde"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Algemeen"
@@ -10717,7 +10715,6 @@ msgstr "Rangskik"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost, plural, zero {لا اعتمادات} one {اعتماد وا
msgid "{totalCount} selected"
msgstr "تم تحديد {totalCount}"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} حقول"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "الرسم البياني القياسي"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "عام"
@@ -10717,7 +10715,6 @@ msgstr "تنظيم"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} crèdits"
msgid "{totalCount} selected"
msgstr "{totalCount} seleccionats"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} camps"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Gràfic de mesurador"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "General"
@@ -10717,7 +10715,6 @@ msgstr "Organitza"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} kreditů"
msgid "{totalCount} selected"
msgstr "{totalCount} vybráno"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount, plural, one {# pole} few {# pole} many {# polí} other {# polí}}"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Ukazatelový graf"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Obecné"
@@ -10717,7 +10715,6 @@ msgstr "Uspořádat"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} kreditter"
msgid "{totalCount} selected"
msgstr "{totalCount} valgt"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} felter"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Målediagram"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Generelt"
@@ -10717,7 +10715,6 @@ msgstr "Organiser"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} Credits"
msgid "{totalCount} selected"
msgstr "{totalCount} ausgewählt"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} Felder"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Allgemein"
@@ -10717,7 +10715,6 @@ msgstr "Organisieren"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} πιστώσεις"
msgid "{totalCount} selected"
msgstr "{totalCount} επιλεγμένα"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} πεδία"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Διάγραμμα δείκτη"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Γενικά"
@@ -10717,7 +10715,6 @@ msgstr "Οργάνωση"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -433,10 +433,10 @@ msgstr "{totalCost} credits"
msgid "{totalCount} selected"
msgstr "{totalCount} selected"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} fields"
msgid "{totalFieldsCount} visible fields"
msgstr "{totalFieldsCount} visible fields"
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7078,8 +7078,6 @@ msgstr "Gauge Chart"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "General"
@@ -10712,7 +10710,6 @@ msgstr "Organize"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} créditos"
msgid "{totalCount} selected"
msgstr "{totalCount} seleccionados"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} campos"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Gráfico de Medidor"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "General"
@@ -10717,7 +10715,6 @@ msgstr "Organizar"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} hyvitystä"
msgid "{totalCount} selected"
msgstr "{totalCount} valittuna"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} kenttää"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Mittarikaavio"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Yleiset"
@@ -10717,7 +10715,6 @@ msgstr ""
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} crédits"
msgid "{totalCount} selected"
msgstr "{totalCount} sélectionné(s)"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount, plural, one {# champ} other {# champs}}"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Général"
@@ -10717,7 +10715,6 @@ msgstr "Organiser"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} קרדיטים"
msgid "{totalCount} selected"
msgstr "{totalCount} נבחר"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} שדות"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "תרשים מד"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "כללי"
@@ -10717,7 +10715,6 @@ msgstr "ארגון"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} kredit"
msgid "{totalCount} selected"
msgstr "{totalCount} kiválasztva"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} mező"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Mérőműszer diagram"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Általános"
@@ -10717,7 +10715,6 @@ msgstr "Rendszerezés"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} crediti"
msgid "{totalCount} selected"
msgstr "{totalCount} selezionati"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} campi"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Grafico a Indicatore"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Generale"
@@ -10717,7 +10715,6 @@ msgstr "Organizza"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} クレジット"
msgid "{totalCount} selected"
msgstr "{totalCount} 件を選択"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} フィールド"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "ゲージチャート"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "一般"
@@ -10717,7 +10715,6 @@ msgstr "整理"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} 크레딧"
msgid "{totalCount} selected"
msgstr "{totalCount} 선택됨"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "필드 {totalFieldsCount}개"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "게이지 차트"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "일반"
@@ -10717,7 +10715,6 @@ msgstr "정리"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} credits"
msgid "{totalCount} selected"
msgstr "{totalCount} geselecteerd"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} velden"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Algemeen"
@@ -10717,7 +10715,6 @@ msgstr "Ordenen"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} kreditter"
msgid "{totalCount} selected"
msgstr "{totalCount} valgt"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} felt"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Målediagram"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Generelt"
@@ -10717,7 +10715,6 @@ msgstr "Organiser"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost, plural, one {# kredyt} few {# kredyty} many {# kredytów} ot
msgid "{totalCount} selected"
msgstr "{totalCount} wybrano"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount, plural, one{# pole} few{# pola} many{# pól} other{# pola}}"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Ogólne"
@@ -10717,7 +10715,6 @@ msgstr "Organizuj"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
@@ -433,9 +433,9 @@ msgstr ""
msgid "{totalCount} selected"
msgstr ""
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
@@ -7078,8 +7078,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr ""
@@ -10712,7 +10710,6 @@ msgstr ""
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} créditos"
msgid "{totalCount} selected"
msgstr "{totalCount} selecionado(s)"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} campos"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Gráfico de Medidor"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Geral"
@@ -10717,7 +10715,6 @@ msgstr "Organizar"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} créditos"
msgid "{totalCount} selected"
msgstr "{totalCount} selecionado(s)"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount, plural, one {# campo} other {# campos}}"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Geral"
@@ -10717,7 +10715,6 @@ msgstr "Organizar"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} credite"
msgid "{totalCount} selected"
msgstr "{totalCount} selectate"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} câmpuri"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Grafic tip ceas"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "General"
@@ -10717,7 +10715,6 @@ msgstr "Organizare"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
Binary file not shown.
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} кредита"
msgid "{totalCount} selected"
msgstr "{totalCount} изабрано"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} поља"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Опште"
@@ -10717,7 +10715,6 @@ msgstr "Организујте"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} krediter"
msgid "{totalCount} selected"
msgstr "{totalCount} valda"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} fält"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Mätardiagram"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Allmänt"
@@ -10719,7 +10717,6 @@ msgstr "Organisera"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} kredi"
msgid "{totalCount} selected"
msgstr "{totalCount} seçildi"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} alan"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Gösterge Grafiği"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Genel"
@@ -10717,7 +10715,6 @@ msgstr "Düzenle"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} кредитів"
msgid "{totalCount} selected"
msgstr "{totalCount} вибрано"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} полів"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr ""
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "Загальні"
@@ -10717,7 +10715,6 @@ msgstr "Упорядкувати"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} tín dụng"
msgid "{totalCount} selected"
msgstr "{totalCount} được chọn"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} trường"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "Biểu đồ đo lường"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "\\"
@@ -10717,7 +10715,6 @@ msgstr "Sắp xếp"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} 积分"
msgid "{totalCount} selected"
msgstr "{totalCount} 已选择"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} 个字段"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "仪表图"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "常规"
@@ -10717,7 +10715,6 @@ msgstr "整理"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
+3 -6
View File
@@ -438,10 +438,10 @@ msgstr "{totalCost} 積分"
msgid "{totalCount} selected"
msgstr "{totalCount} 已選取"
#. js-lingui-id: WwbakM
#. js-lingui-id: XdG+hI
#: src/modules/side-panel/pages/page-layout/components/record-page/SidePanelRecordPageFieldsSettings.tsx
msgid "{totalFieldsCount} fields"
msgstr "{totalFieldsCount} 個欄位"
msgid "{totalFieldsCount} visible fields"
msgstr ""
#. js-lingui-id: ITVweg
#: src/modules/object-record/object-options-dropdown/components/ObjectOptionsDropdownDefaultView.tsx
@@ -7083,8 +7083,6 @@ msgstr "儀表圖"
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/settings/admin-panel/components/SettingsAdminContent.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroupsForDisplay.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/keyboard-shortcut-menu/components/KeyboardShortcutMenuOpenContent.tsx
msgid "General"
msgstr "常規"
@@ -10717,7 +10715,6 @@ msgstr "整理"
#: src/pages/settings/admin-panel/SettingsAdminAiProviderDetail.tsx
#: src/pages/settings/admin-panel/SettingsAdmin.tsx
#: src/modules/settings/hooks/useSettingsNavigationItems.tsx
#: src/modules/page-layout/widgets/fields/hooks/useFieldsWidgetGroups.ts
#: src/modules/navigation-menu-item/edit/side-panel/components/SidePanelNewSidebarItemMainMenu.tsx
#: src/modules/navigation/components/NavigationDrawerOtherSection.tsx
#: src/modules/command-menu-item/edit/components/SidePanelCommandMenuItemEditPage.tsx
@@ -46,7 +46,7 @@ const LayoutCustomizationBarContent = () => {
ease: 'easeInOut',
}}
>
<StyledContainer>
<StyledContainer data-globally-prevent-click-outside="true">
<StyledTitle>
<IconPaint size={theme.icon.size.md} />
{t`Layout customization`}
@@ -6,6 +6,7 @@ import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/sta
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
import { useSaveNavigationMenuItemsDraft } from '@/navigation-menu-item/edit/hooks/useSaveNavigationMenuItemsDraft';
import { useCreatePendingFieldsWidgetViews } from '@/page-layout/hooks/useCreatePendingFieldsWidgetViews';
import { useSavePageLayoutWidgetsData } from '@/page-layout/hooks/useSavePageLayoutWidgetsData';
import { useUpdatePageLayoutWithTabsAndWidgets } from '@/page-layout/hooks/useUpdatePageLayoutWithTabsAndWidgets';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
@@ -38,6 +39,8 @@ export const useSaveLayoutCustomization = () => {
const { enqueueErrorSnackBar } = useSnackBar();
const { updatePageLayoutWithTabsAndWidgets } =
useUpdatePageLayoutWithTabsAndWidgets();
const { createPendingFieldsWidgetViews } =
useCreatePendingFieldsWidgetViews();
const { exitLayoutCustomizationMode } = useExitLayoutCustomizationMode();
const { savePageLayoutWidgetsData } = useSavePageLayoutWidgetsData();
@@ -104,6 +107,8 @@ export const useSaveLayoutCustomization = () => {
persistedAsDraft,
);
await createPendingFieldsWidgetViews(pageLayoutId);
if (isPageLayoutStructureDirty) {
const updateInput = convertPageLayoutDraftToUpdateInput(draft, {
shouldFilterDynamicRelationWidgets:
@@ -174,6 +179,7 @@ export const useSaveLayoutCustomization = () => {
saveDraft,
saveCommandMenuItemsDraft,
isCommandMenuItemsDirty,
createPendingFieldsWidgetViews,
updatePageLayoutWithTabsAndWidgets,
savePageLayoutWidgetsData,
exitLayoutCustomizationMode,
@@ -0,0 +1,109 @@
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { fieldsWidgetEditorModeDraftComponentState } from '@/page-layout/states/fieldsWidgetEditorModeDraftComponentState';
import { fieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/fieldsWidgetGroupsDraftComponentState';
import { fieldsWidgetUngroupedFieldsDraftComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsDraftComponentState';
import { hasInitializedFieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/hasInitializedFieldsWidgetGroupsDraftComponentState';
import { useAvailableComponentInstanceIdOrThrow } from '@/ui/utilities/state/component-state/hooks/useAvailableComponentInstanceIdOrThrow';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { v4 as uuidv4 } from 'uuid';
export const useCopyFieldsWidgetDraftState = (
pageLayoutIdFromProps: string,
) => {
const pageLayoutId = useAvailableComponentInstanceIdOrThrow(
PageLayoutComponentInstanceContext,
pageLayoutIdFromProps,
);
const fieldsWidgetGroupsDraftState = useAtomComponentStateCallbackState(
fieldsWidgetGroupsDraftComponentState,
pageLayoutId,
);
const fieldsWidgetUngroupedFieldsDraftState =
useAtomComponentStateCallbackState(
fieldsWidgetUngroupedFieldsDraftComponentState,
pageLayoutId,
);
const fieldsWidgetEditorModeDraftState = useAtomComponentStateCallbackState(
fieldsWidgetEditorModeDraftComponentState,
pageLayoutId,
);
const hasInitializedFieldsWidgetGroupsDraftState =
useAtomComponentStateCallbackState(
hasInitializedFieldsWidgetGroupsDraftComponentState,
pageLayoutId,
);
const store = useStore();
const copyFieldsWidgetDraftState = useCallback(
(sourceWidgetId: string, targetWidgetId: string) => {
const groupsDraft = store.get(fieldsWidgetGroupsDraftState);
const ungroupedFieldsDraft = store.get(
fieldsWidgetUngroupedFieldsDraftState,
);
const editorModeDraft = store.get(fieldsWidgetEditorModeDraftState);
let hasCopiedData = false;
if (sourceWidgetId in groupsDraft) {
const sourceGroups = groupsDraft[sourceWidgetId];
const clonedGroups = sourceGroups.map((group) => ({
...group,
id: uuidv4(),
fields: group.fields.map((field) => ({
...field,
viewFieldId: undefined,
})),
}));
store.set(fieldsWidgetGroupsDraftState, (prev) => ({
...prev,
[targetWidgetId]: clonedGroups,
}));
hasCopiedData = true;
}
if (sourceWidgetId in ungroupedFieldsDraft) {
const sourceUngrouped = ungroupedFieldsDraft[sourceWidgetId];
const clonedUngrouped = sourceUngrouped.map((field) => ({
...field,
viewFieldId: undefined,
}));
store.set(fieldsWidgetUngroupedFieldsDraftState, (prev) => ({
...prev,
[targetWidgetId]: clonedUngrouped,
}));
hasCopiedData = true;
}
if (sourceWidgetId in editorModeDraft) {
store.set(fieldsWidgetEditorModeDraftState, (prev) => ({
...prev,
[targetWidgetId]: editorModeDraft[sourceWidgetId],
}));
hasCopiedData = true;
}
if (hasCopiedData) {
store.set(hasInitializedFieldsWidgetGroupsDraftState, (prev) => ({
...prev,
[targetWidgetId]: true,
}));
}
},
[
fieldsWidgetEditorModeDraftState,
fieldsWidgetGroupsDraftState,
fieldsWidgetUngroupedFieldsDraftState,
hasInitializedFieldsWidgetGroupsDraftState,
store,
],
);
return { copyFieldsWidgetDraftState };
};
@@ -0,0 +1,90 @@
import { objectMetadataItemsSelector } from '@/object-metadata/states/objectMetadataItemsSelector';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { getWidgetConfigurationViewId } from '@/page-layout/utils/getWidgetConfigurationViewId';
import { usePerformViewAPIPersist } from '@/views/hooks/internal/usePerformViewAPIPersist';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { ViewType, WidgetType } from '~/generated-metadata/graphql';
export const useCreatePendingFieldsWidgetViews = () => {
const { performViewAPICreate } = usePerformViewAPIPersist();
const store = useStore();
const createPendingFieldsWidgetViews = useCallback(
async (pageLayoutId: string): Promise<void> => {
const draft = store.get(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const persisted = store.get(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const persistedWidgetIds = new Set(
persisted?.tabs.flatMap((tab) =>
tab.widgets.map((widget) => widget.id),
) ?? [],
);
const objectMetadataItems = store.get(objectMetadataItemsSelector.atom);
const newFieldsWidgets = draft.tabs
.flatMap((tab) => tab.widgets)
.filter(
(widget) =>
widget.type === WidgetType.FIELDS &&
!persistedWidgetIds.has(widget.id),
);
for (const widget of newFieldsWidgets) {
const viewId = getWidgetConfigurationViewId(widget.configuration);
if (!isDefined(viewId)) {
continue;
}
const objectMetadataId =
widget.objectMetadataId ?? draft.objectMetadataId;
if (!isDefined(objectMetadataId)) {
continue;
}
const objectMetadataItem = objectMetadataItems.find(
(item) => item.id === objectMetadataId,
);
const viewName = isDefined(objectMetadataItem)
? `${objectMetadataItem.labelSingular} Fields`
: 'Fields';
const result = await performViewAPICreate(
{
input: {
id: viewId,
name: viewName,
icon: 'IconList',
objectMetadataId,
type: ViewType.FIELDS_WIDGET,
},
},
objectMetadataId,
);
if (result.status === 'failed') {
throw new Error(
`Failed to create view for FIELDS widget ${widget.id}`,
);
}
}
},
[performViewAPICreate, store],
);
return { createPendingFieldsWidgetViews };
};
@@ -5,7 +5,6 @@ import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDr
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
import { createDefaultFieldsWidget } from '@/page-layout/utils/createDefaultFieldsWidget';
import { useCreateViewForFieldsWidget } from '@/page-layout/widgets/fields/hooks/useCreateViewForFieldsWidget';
import { useNavigatePageLayoutSidePanel } from '@/side-panel/pages/page-layout/hooks/useNavigatePageLayoutSidePanel';
import { useTargetRecord } from '@/ui/layout/contexts/useTargetRecord';
import { useAtomComponentStateCallbackState } from '@/ui/utilities/state/jotai/hooks/useAtomComponentStateCallbackState';
@@ -25,8 +24,6 @@ export const useCreateRecordPageFieldsWidget = () => {
const { currentPageLayout } = useCurrentPageLayoutOrThrow();
const { createViewForFieldsWidget } = useCreateViewForFieldsWidget();
const pageLayoutDraftState = useAtomComponentStateCallbackState(
pageLayoutDraftComponentState,
);
@@ -39,15 +36,8 @@ export const useCreateRecordPageFieldsWidget = () => {
const store = useStore();
const createRecordPageFieldsWidget = useCallback(async () => {
const viewId = await createViewForFieldsWidget({
objectMetadataId: objectMetadataItem.id,
viewName: `${objectMetadataItem.labelSingular} Fields`,
});
if (viewId === null) {
return;
}
const createRecordPageFieldsWidget = useCallback(() => {
const viewId = uuidv4();
const activeTab = currentPageLayout.tabs.find((tab) => tab.id === tabId);
const positionIndex = activeTab?.widgets.length ?? 0;
@@ -75,11 +65,9 @@ export const useCreateRecordPageFieldsWidget = () => {
resetNavigationStack: true,
});
}, [
createViewForFieldsWidget,
currentPageLayout.tabs,
navigatePageLayoutSidePanel,
objectMetadataItem.id,
objectMetadataItem.labelSingular,
pageLayoutDraftState,
pageLayoutEditingWidgetIdState,
store,
@@ -2,6 +2,7 @@ import { PageLayoutComponentInstanceContext } from '@/page-layout/states/context
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { removeWidgetFromTab } from '@/page-layout/utils/removeWidgetFromTab';
import { removeWidgetLayoutFromTab } from '@/page-layout/utils/removeWidgetLayoutFromTab';
import { useDeleteViewForFieldsWidget } from '@/page-layout/widgets/fields/hooks/useDeleteViewForFieldsWidget';
@@ -42,6 +43,11 @@ export const useDeletePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
const { deleteViewForRecordTableWidget } =
useDeleteViewForRecordTableWidget();
const pageLayoutPersistedState = useAtomComponentStateCallbackState(
pageLayoutPersistedComponentState,
pageLayoutId,
);
const store = useStore();
const deletePageLayoutWidget = useCallback(
@@ -59,7 +65,17 @@ export const useDeletePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
(widget) => widget.id === widgetId,
);
const persisted = store.get(pageLayoutPersistedState);
const persistedWidgetIds = new Set(
persisted?.tabs.flatMap((tab) =>
tab.widgets.map((widget) => widget.id),
) ?? [],
);
const isWidgetPersisted = persistedWidgetIds.has(widgetId);
if (
isWidgetPersisted &&
isDefined(widgetToDelete) &&
widgetToDelete.type === WidgetType.RECORD_TABLE &&
'viewId' in widgetToDelete.configuration &&
@@ -71,6 +87,7 @@ export const useDeletePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
}
if (
isWidgetPersisted &&
isDefined(widgetToDelete) &&
widgetToDelete.type === WidgetType.FIELDS &&
'viewId' in widgetToDelete.configuration &&
@@ -112,6 +129,7 @@ export const useDeletePageLayoutWidget = (pageLayoutIdFromProps?: string) => {
pageLayoutCurrentLayoutsState,
pageLayoutDraftState,
pageLayoutEditingWidgetIdState,
pageLayoutPersistedState,
store,
],
);
@@ -1,8 +1,10 @@
import { useCopyFieldsWidgetDraftState } from '@/page-layout/hooks/useCopyFieldsWidgetDraftState';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutTabSettingsOpenTabIdComponentState } from '@/page-layout/states/pageLayoutTabSettingsOpenTabIdComponentState';
import { type PageLayoutTab } from '@/page-layout/types/PageLayoutTab';
import { duplicateWidgetConfigurationWithNewViewId } from '@/page-layout/utils/duplicateWidgetConfigurationWithNewViewId';
import { generateDuplicatedTimestamps } from '@/page-layout/utils/generateDuplicatedTimestamps';
import { sortTabsByPosition } from '@/page-layout/utils/sortTabsByPosition';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
@@ -17,6 +19,7 @@ import { useCallback } from 'react';
import { SidePanelPages } from 'twenty-shared/types';
import { appendCopySuffix, isDefined } from 'twenty-shared/utils';
import { v4 as uuidv4 } from 'uuid';
import { WidgetConfigurationType } from '~/generated-metadata/graphql';
export const useDuplicatePageLayoutTab = ({
pageLayoutId: pageLayoutIdFromProps,
@@ -40,6 +43,9 @@ export const useDuplicatePageLayoutTab = ({
pageLayoutId,
);
const { copyFieldsWidgetDraftState } =
useCopyFieldsWidgetDraftState(pageLayoutId);
const store = useStore();
const setActiveTabId = useSetAtomComponentState(
@@ -79,6 +85,9 @@ export const useDuplicatePageLayoutTab = ({
...widget,
id: newWidgetId,
pageLayoutTabId: newTabId,
configuration: duplicateWidgetConfigurationWithNewViewId(
widget.configuration,
),
...generateDuplicatedTimestamps(),
};
});
@@ -128,6 +137,19 @@ export const useDuplicatePageLayoutTab = ({
tabs: [...prev.tabs, newTab],
});
for (const [oldWidgetId, newWidgetId] of widgetOldIdNewIdMap) {
const sourceWidget = sourceTab.widgets.find(
(widget) => widget.id === oldWidgetId,
);
if (
sourceWidget?.configuration.configurationType ===
WidgetConfigurationType.FIELDS
) {
copyFieldsWidgetDraftState(oldWidgetId, newWidgetId);
}
}
closeSidePanelMenu();
setActiveTabId(newTabId);
@@ -144,6 +166,7 @@ export const useDuplicatePageLayoutTab = ({
},
[
closeSidePanelMenu,
copyFieldsWidgetDraftState,
navigatePageLayoutSidePanel,
pageLayoutCurrentLayouts,
pageLayoutDraft,
@@ -1,9 +1,11 @@
import { useCopyFieldsWidgetDraftState } from '@/page-layout/hooks/useCopyFieldsWidgetDraftState';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { addWidgetToTab } from '@/page-layout/utils/addWidgetToTab';
import { duplicateWidgetConfigurationWithNewViewId } from '@/page-layout/utils/duplicateWidgetConfigurationWithNewViewId';
import { generateDuplicatedTimestamps } from '@/page-layout/utils/generateDuplicatedTimestamps';
import { getScrollWrapperInstanceIdFromPageLayoutId } from '@/page-layout/utils/getScrollWrapperInstanceIdFromPageLayoutId';
import { getUpdatedTabLayouts } from '@/page-layout/utils/getUpdatedTabLayouts';
@@ -15,6 +17,7 @@ import { useStore } from 'jotai';
import { useCallback } from 'react';
import { appendCopySuffix, isDefined } from 'twenty-shared/utils';
import { v4 as uuidv4 } from 'uuid';
import { WidgetConfigurationType } from '~/generated-metadata/graphql';
export const useDuplicatePageLayoutWidget = (
pageLayoutIdFromProps?: string,
@@ -39,6 +42,9 @@ export const useDuplicatePageLayoutWidget = (
pageLayoutId,
);
const { copyFieldsWidgetDraftState } =
useCopyFieldsWidgetDraftState(pageLayoutId);
const { getScrollWrapperElement } = useScrollWrapperHTMLElement(
getScrollWrapperInstanceIdFromPageLayoutId(pageLayoutId),
);
@@ -75,6 +81,9 @@ export const useDuplicatePageLayoutWidget = (
...sourceWidget,
id: newWidgetId,
title: appendCopySuffix(sourceWidget.title),
configuration: duplicateWidgetConfigurationWithNewViewId(
sourceWidget.configuration,
),
...generateDuplicatedTimestamps(),
};
@@ -115,6 +124,13 @@ export const useDuplicatePageLayoutWidget = (
tabs: addWidgetToTab(prev.tabs, sourceTab.id, clonedWidget),
}));
if (
sourceWidget.configuration.configurationType ===
WidgetConfigurationType.FIELDS
) {
copyFieldsWidgetDraftState(widgetId, newWidgetId);
}
setPageLayoutEditingWidgetId(newWidgetId);
const { scrollWrapperElement } = getScrollWrapperElement();
@@ -137,6 +153,7 @@ export const useDuplicatePageLayoutWidget = (
return newWidgetId;
},
[
copyFieldsWidgetDraftState,
pageLayoutCurrentLayoutsState,
pageLayoutDraftState,
setPageLayoutEditingWidgetId,
@@ -3,6 +3,7 @@ import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pag
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutDraggedAreaComponentState } from '@/page-layout/states/pageLayoutDraggedAreaComponentState';
import { pageLayoutEditingWidgetIdComponentState } from '@/page-layout/states/pageLayoutEditingWidgetIdComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { getWidgetConfigurationViewId } from '@/page-layout/utils/getWidgetConfigurationViewId';
import { removeWidgetFromTab } from '@/page-layout/utils/removeWidgetFromTab';
import { removeWidgetLayoutFromTab } from '@/page-layout/utils/removeWidgetLayoutFromTab';
@@ -48,6 +49,11 @@ export const useRemovePageLayoutWidgetAndPreservePosition = (
const { deleteViewForRecordTableWidget } =
useDeleteViewForRecordTableWidget();
const pageLayoutPersistedState = useAtomComponentStateCallbackState(
pageLayoutPersistedComponentState,
pageLayoutId,
);
const store = useStore();
const removePageLayoutWidgetAndPreservePosition = useCallback(
@@ -63,7 +69,16 @@ export const useRemovePageLayoutWidgetAndPreservePosition = (
(widget) => widget.id === widgetId,
);
if (isDefined(widgetToRemove)) {
const persisted = store.get(pageLayoutPersistedState);
const persistedWidgetIds = new Set(
persisted?.tabs.flatMap((tab) =>
tab.widgets.map((widget) => widget.id),
) ?? [],
);
const isWidgetPersisted = persistedWidgetIds.has(widgetId);
if (isWidgetPersisted && isDefined(widgetToRemove)) {
const viewId = getWidgetConfigurationViewId(
widgetToRemove.configuration,
);
@@ -122,6 +137,7 @@ export const useRemovePageLayoutWidgetAndPreservePosition = (
pageLayoutDraftState,
pageLayoutDraggedAreaState,
pageLayoutEditingWidgetIdState,
pageLayoutPersistedState,
store,
],
);
@@ -1,3 +1,4 @@
import { useCreatePendingFieldsWidgetViews } from '@/page-layout/hooks/useCreatePendingFieldsWidgetViews';
import { useUpdatePageLayoutWithTabsAndWidgets } from '@/page-layout/hooks/useUpdatePageLayoutWithTabsAndWidgets';
import { PageLayoutComponentInstanceContext } from '@/page-layout/states/contexts/PageLayoutComponentInstanceContext';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
@@ -41,12 +42,17 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
const { updatePageLayoutWithTabsAndWidgets } =
useUpdatePageLayoutWithTabsAndWidgets();
const { createPendingFieldsWidgetViews } =
useCreatePendingFieldsWidgetViews();
const featureFlags = useFeatureFlagsMap();
const isRecordPageLayoutEditingEnabled =
featureFlags[FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED];
const store = useStore();
const savePageLayout = useCallback(async () => {
await createPendingFieldsWidgetViews(pageLayoutId);
const pageLayoutDraft = store.get(pageLayoutDraftCallbackState);
const updateInput = convertPageLayoutDraftToUpdateInput(pageLayoutDraft, {
shouldFilterDynamicRelationWidgets: !isRecordPageLayoutEditingEnabled,
@@ -84,6 +90,7 @@ export const useSavePageLayout = (pageLayoutIdFromProps: string) => {
return result;
}, [
createPendingFieldsWidgetViews,
isRecordPageLayoutEditingEnabled,
pageLayoutCurrentLayoutsCallbackState,
pageLayoutDraftCallbackState,
@@ -0,0 +1,16 @@
import { type PageLayoutWidget } from '@/page-layout/types/PageLayoutWidget';
import { v4 as uuidv4 } from 'uuid';
import { WidgetConfigurationType } from '~/generated-metadata/graphql';
export const duplicateWidgetConfigurationWithNewViewId = (
configuration: PageLayoutWidget['configuration'],
): PageLayoutWidget['configuration'] => {
if (configuration.configurationType === WidgetConfigurationType.FIELDS) {
return {
...configuration,
viewId: uuidv4(),
};
}
return configuration;
};
@@ -368,15 +368,9 @@ export const WithViewFieldGroups: Story = {
},
};
export const WithInlineViewFields: Story = {
export const WithDefaultGroups: Story = {
render: () => {
const view = createView({
viewFields: [
createViewField('vf-name', nameField.id, 0),
createViewField('vf-employees', employeesField.id, 1),
createViewField('vf-address', addressField.id, 2),
],
});
const view = createView();
const widget = createFieldsWidget(FIELDS_VIEW_ID);
@@ -443,14 +437,12 @@ export const WithInlineViewFields: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const companyName = await canvas.findByText('Acme Corporation');
expect(companyName).toBeVisible();
const generalHeader = await canvas.findByText('General');
expect(generalHeader).toBeVisible();
const contactInfoHeader = canvas.queryByText('Contact Info');
expect(contactInfoHeader).toBeNull();
const generalHeader = canvas.queryByText('General');
expect(generalHeader).toBeNull();
const creationDateElements = await canvas.findAllByText('Creation date');
expect(creationDateElements.length).toBeGreaterThan(0);
expect(creationDateElements[0]).toBeVisible();
},
};
@@ -1,42 +0,0 @@
import { usePerformViewAPIPersist } from '@/views/hooks/internal/usePerformViewAPIPersist';
import { useCallback } from 'react';
import { v4 as uuidv4 } from 'uuid';
import { ViewType } from '~/generated-metadata/graphql';
export const useCreateViewForFieldsWidget = () => {
const { performViewAPICreate } = usePerformViewAPIPersist();
const createViewForFieldsWidget = useCallback(
async ({
objectMetadataId,
viewName,
}: {
objectMetadataId: string;
viewName: string;
}) => {
const viewId = uuidv4();
const result = await performViewAPICreate(
{
input: {
id: viewId,
name: viewName,
icon: 'IconList',
objectMetadataId,
type: ViewType.FIELDS_WIDGET,
},
},
objectMetadataId,
);
if (result.status === 'failed') {
return null;
}
return viewId;
},
[performViewAPICreate],
);
return { createViewForFieldsWidget };
};
@@ -5,6 +5,7 @@ import {
type FieldsWidgetGroup,
type FieldsWidgetGroupField,
} from '@/page-layout/widgets/fields/types/FieldsWidgetGroup';
import { mapViewFieldsToFieldsWidgetGroupFields } from '@/page-layout/widgets/fields/utils/mapViewFieldsToFieldsWidgetGroupFields';
import { useViewById } from '@/views/hooks/useViewById';
import { useMemo } from 'react';
import {
@@ -105,7 +106,7 @@ export const useFieldsWidgetEditorGroupsData = ({
const fields: FieldsWidgetGroupField[] = groupFields
.map((viewField) => {
const fieldMetadataItem = objectMetadataItem.fields.find(
(f) => f.id === viewField.fieldMetadataId,
(field) => field.id === viewField.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
@@ -136,7 +137,7 @@ export const useFieldsWidgetEditorGroupsData = ({
const lastGroup = groups[groups.length - 1];
const lastFieldPosition =
lastGroup.fields.length > 0
? Math.max(...lastGroup.fields.map((f) => f.position)) + 1
? Math.max(...lastGroup.fields.map((field) => field.position)) + 1
: 0;
const missingFields = buildMissingFields({
@@ -153,38 +154,23 @@ export const useFieldsWidgetEditorGroupsData = ({
}
if (isDefined(view) && view.viewFields.length > 0) {
let globalIndex = 0;
const existingFieldMetadataIds = new Set<string>();
const fields = mapViewFieldsToFieldsWidgetGroupFields({
viewFields: view.viewFields,
objectMetadataFields: objectMetadataItem.fields,
});
const fields = [...view.viewFields]
.sort((a, b) => a.position - b.position)
.map((viewField) => {
const fieldMetadataItem = objectMetadataItem.fields.find(
(f) => f.id === viewField.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
return null;
}
existingFieldMetadataIds.add(viewField.fieldMetadataId);
return {
fieldMetadataItem,
position: viewField.position,
isVisible: viewField.isVisible,
globalIndex: globalIndex++,
viewFieldId: viewField.id,
};
})
.filter(isDefined);
const existingFieldMetadataIds = new Set(
fields.map((field) => field.fieldMetadataItem.id),
);
const lastFieldPosition =
fields.length > 0 ? Math.max(...fields.map((f) => f.position)) + 1 : 0;
fields.length > 0
? Math.max(...fields.map((field) => field.position)) + 1
: 0;
const missingFields = buildMissingFields({
existingFieldMetadataIds,
startGlobalIndex: globalIndex,
startGlobalIndex: fields.length,
startPosition: lastFieldPosition,
});
@@ -206,6 +192,6 @@ export const useFieldsWidgetEditorGroupsData = ({
...result,
isFromView:
isDefined(view) &&
(isDefined(view.viewFieldGroups) || view.viewFields.length > 0),
(isNonEmptyArray(view.viewFieldGroups) || view.viewFields.length > 0),
};
};
@@ -1,22 +0,0 @@
import { useFieldListFieldMetadataItems } from '@/object-record/record-field-list/hooks/useFieldListFieldMetadataItems';
type UseFieldsWidgetFieldMetadataItemsProps = {
objectNameSingular: string;
};
export const useFieldsWidgetFieldMetadataItems = ({
objectNameSingular,
}: UseFieldsWidgetFieldMetadataItemsProps) => {
const { inlineFieldMetadataItems, legacyActivityTargetFieldMetadataItems } =
useFieldListFieldMetadataItems({
objectNameSingular,
excludeFieldMetadataIds: [],
excludeCreatedAtAndUpdatedAt: true,
showRelationSections: true,
});
return {
inlineFieldMetadataItems,
legacyActivityTargetFieldMetadataItems,
};
};
@@ -1,12 +1,11 @@
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useFieldsWidgetFieldMetadataItems } from '@/page-layout/widgets/fields/hooks/useFieldsWidgetFieldMetadataItems';
import { type FieldsWidgetDisplayMode } from '@/page-layout/widgets/fields/types/FieldsWidgetDisplayMode';
import {
type FieldsWidgetGroup,
type FieldsWidgetGroupField,
} from '@/page-layout/widgets/fields/types/FieldsWidgetGroup';
import { mapViewFieldsToFieldsWidgetGroupFields } from '@/page-layout/widgets/fields/utils/mapViewFieldsToFieldsWidgetGroupFields';
import { useViewById } from '@/views/hooks/useViewById';
import { useLingui } from '@lingui/react/macro';
import { useMemo } from 'react';
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
@@ -19,20 +18,17 @@ export const useFieldsWidgetGroups = ({
viewId,
objectNameSingular,
}: UseFieldsWidgetGroupsParams) => {
const { t } = useLingui();
const { view } = useViewById(viewId);
const { objectMetadataItem } = useObjectMetadataItem({
objectNameSingular,
});
const { inlineFieldMetadataItems, legacyActivityTargetFieldMetadataItems } =
useFieldsWidgetFieldMetadataItems({
objectNameSingular,
});
const groups = useMemo<FieldsWidgetGroup[]>(() => {
const { groups, displayMode } = useMemo<{
groups: FieldsWidgetGroup[];
displayMode: FieldsWidgetDisplayMode;
}>(() => {
if (!isDefined(objectMetadataItem)) {
return [];
return { groups: [], displayMode: 'inline' };
}
if (isDefined(view) && isNonEmptyArray(view.viewFieldGroups)) {
@@ -42,7 +38,7 @@ export const useFieldsWidgetGroups = ({
let globalIndex = 0;
return sortedGroups
const groups = sortedGroups
.filter((group) => group.isVisible)
.map((group) => {
const groupFields = [...(group.viewFields ?? [])].sort(
@@ -53,21 +49,19 @@ export const useFieldsWidgetGroups = ({
.filter((field) => field.isVisible)
.map((viewField) => {
const fieldMetadataItem = objectMetadataItem.fields.find(
(f) => f.id === viewField.fieldMetadataId,
(field) => field.id === viewField.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
return null;
}
const field: FieldsWidgetGroupField = {
return {
fieldMetadataItem,
position: viewField.position,
isVisible: viewField.isVisible,
globalIndex: globalIndex++,
};
return field;
})
.filter(isDefined);
@@ -80,129 +74,38 @@ export const useFieldsWidgetGroups = ({
};
})
.filter((group) => group.fields.length > 0);
return { groups, displayMode: 'grouped' };
}
if (isDefined(view) && view.viewFields.length > 0) {
let globalIndex = 0;
const allFields = mapViewFieldsToFieldsWidgetGroupFields({
viewFields: view.viewFields,
objectMetadataFields: objectMetadataItem.fields,
});
const fields: FieldsWidgetGroupField[] = [...view.viewFields]
.sort((a, b) => a.position - b.position)
.filter((viewField) => viewField.isVisible)
.map((viewField) => {
const fieldMetadataItem = objectMetadataItem.fields.find(
(f) => f.id === viewField.fieldMetadataId,
);
const visibleFields = allFields.filter((field) => field.isVisible);
if (!isDefined(fieldMetadataItem)) {
return null;
}
if (visibleFields.length === 0) {
return { groups: [], displayMode: 'inline' };
}
return {
fieldMetadataItem,
position: viewField.position,
isVisible: viewField.isVisible,
globalIndex: globalIndex++,
};
})
.filter(isDefined);
if (fields.length > 0) {
return [
return {
groups: [
{
id: `${view.id}-group-general`,
name: t`General`,
id: `${viewId}-ungrouped`,
name: '',
position: 0,
isVisible: true,
fields,
fields: visibleFields,
},
];
}
],
displayMode: 'inline',
};
}
const fieldsToDisplay = [
...inlineFieldMetadataItems,
...legacyActivityTargetFieldMetadataItems,
];
if (fieldsToDisplay.length === 0) {
return [];
}
const generalFields: Array<{
fieldMetadataItem: (typeof fieldsToDisplay)[0];
position: number;
}> = [];
const otherFields: Array<{
fieldMetadataItem: (typeof fieldsToDisplay)[0];
position: number;
}> = [];
let generalPosition = 0;
let otherPosition = 0;
fieldsToDisplay.forEach((field) => {
if (field.isCustom === true) {
otherFields.push({
fieldMetadataItem: field,
position: otherPosition++,
});
} else {
generalFields.push({
fieldMetadataItem: field,
position: generalPosition++,
});
}
});
const groups: FieldsWidgetGroup[] = [];
let globalIndex = 0;
if (generalFields.length > 0) {
groups.push({
id: `${objectNameSingular}-group-general`,
name: t`General`,
position: 0,
isVisible: true,
fields: generalFields.map((field) => ({
fieldMetadataItem: field.fieldMetadataItem,
position: field.position,
isVisible: true,
globalIndex: globalIndex++,
})),
});
}
if (otherFields.length > 0) {
groups.push({
id: `${objectNameSingular}-group-other`,
name: t`Other`,
position: 1,
isVisible: true,
fields: otherFields.map((field) => ({
fieldMetadataItem: field.fieldMetadataItem,
position: field.position,
isVisible: true,
globalIndex: globalIndex++,
})),
});
}
return groups;
}, [
objectMetadataItem,
objectNameSingular,
t,
view,
inlineFieldMetadataItems,
legacyActivityTargetFieldMetadataItems,
]);
const displayMode: FieldsWidgetDisplayMode =
isDefined(view) &&
!isNonEmptyArray(view.viewFieldGroups) &&
view.viewFields.length > 0
? 'inline'
: 'grouped';
return { groups: [], displayMode: 'inline' };
}, [objectMetadataItem, view, viewId]);
return {
groups,
@@ -4,6 +4,7 @@ import {
type FieldsWidgetGroupField,
} from '@/page-layout/widgets/fields/types/FieldsWidgetGroup';
import { getHiddenFieldsFromGroups } from '@/page-layout/widgets/fields/utils/getHiddenFieldsFromGroups';
import { mapViewFieldsToFieldsWidgetGroupFields } from '@/page-layout/widgets/fields/utils/mapViewFieldsToFieldsWidgetGroupFields';
import { useViewById } from '@/views/hooks/useViewById';
import { useMemo } from 'react';
import { isDefined, isNonEmptyArray } from 'twenty-shared/utils';
@@ -61,28 +62,10 @@ export const useFieldsWidgetHiddenFields = ({
}
if (isDefined(view) && view.viewFields.length > 0) {
let globalIndex = 0;
return [...view.viewFields]
.sort((a, b) => a.position - b.position)
.filter((viewField) => !viewField.isVisible)
.map((viewField) => {
const fieldMetadataItem = objectMetadataItem.fields.find(
(f) => f.id === viewField.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
return null;
}
return {
fieldMetadataItem,
position: viewField.position,
isVisible: false,
globalIndex: globalIndex++,
};
})
.filter(isDefined);
return mapViewFieldsToFieldsWidgetGroupFields({
viewFields: view.viewFields,
objectMetadataFields: objectMetadataItem.fields,
}).filter((field) => !field.isVisible);
}
return [];
@@ -0,0 +1,35 @@
import { type FieldMetadataItem } from '@/object-metadata/types/FieldMetadataItem';
import { type FieldsWidgetGroupField } from '@/page-layout/widgets/fields/types/FieldsWidgetGroup';
import { type ViewField } from '@/views/types/ViewField';
import { isDefined } from 'twenty-shared/utils';
export const mapViewFieldsToFieldsWidgetGroupFields = ({
viewFields,
objectMetadataFields,
}: {
viewFields: ViewField[];
objectMetadataFields: FieldMetadataItem[];
}): FieldsWidgetGroupField[] => {
let globalIndex = 0;
return [...viewFields]
.sort((a, b) => a.position - b.position)
.map((viewField) => {
const fieldMetadataItem = objectMetadataFields.find(
(field) => field.id === viewField.fieldMetadataId,
);
if (!isDefined(fieldMetadataItem)) {
return null;
}
return {
fieldMetadataItem,
position: viewField.position,
isVisible: viewField.isVisible,
globalIndex: globalIndex++,
viewFieldId: viewField.id,
};
})
.filter(isDefined);
};
@@ -15,7 +15,6 @@ import { isVerticalListPosition } from '@/page-layout/utils/isVerticalListPositi
import { removeWidgetFromTab } from '@/page-layout/utils/removeWidgetFromTab';
import { useFieldWidgetEligibleFields } from '@/page-layout/widgets/field/hooks/useFieldWidgetEligibleFields';
import { getFieldWidgetDefaultDisplayMode } from '@/page-layout/widgets/field/utils/getFieldWidgetDisplayModeConfig';
import { useCreateViewForFieldsWidget } from '@/page-layout/widgets/fields/hooks/useCreateViewForFieldsWidget';
import { useDeleteViewForFieldsWidget } from '@/page-layout/widgets/fields/hooks/useDeleteViewForFieldsWidget';
import { useDeleteViewForRecordTableWidget } from '@/page-layout/widgets/record-table/hooks/useDeleteViewForRecordTableWidget';
import { SidePanelGroup } from '@/side-panel/components/SidePanelGroup';
@@ -92,8 +91,6 @@ export const SidePanelPageLayoutRecordPageWidgetTypeSelect = () => {
const { insertCreatedWidgetAtContext } =
useInsertCreatedWidgetAtContext(pageLayoutId);
const { createViewForFieldsWidget } = useCreateViewForFieldsWidget();
const { deleteViewForFieldsWidget } = useDeleteViewForFieldsWidget();
const { deleteViewForRecordTableWidget } =
@@ -187,21 +184,14 @@ export const SidePanelPageLayoutRecordPageWidgetTypeSelect = () => {
}),
);
const handleCreateFieldsWidget = useCallback(async () => {
const handleCreateFieldsWidget = useCallback(() => {
if (!isDefined(tabId)) {
return;
}
const replacePositionIndex = getExistingWidgetPositionIndex();
const viewId = await createViewForFieldsWidget({
objectMetadataId: objectMetadataItem.id,
viewName: `${objectMetadataItem.labelSingular} Fields`,
});
if (viewId === null) {
return;
}
const viewId = uuidv4();
removeExistingWidgetIfReplacing();
@@ -233,12 +223,10 @@ export const SidePanelPageLayoutRecordPageWidgetTypeSelect = () => {
resetNavigationStack: true,
});
}, [
createViewForFieldsWidget,
getExistingWidgetPositionIndex,
insertCreatedWidgetAtContext,
navigatePageLayoutSidePanel,
objectMetadataItem.id,
objectMetadataItem.labelSingular,
pageLayoutDraftState,
removeExistingWidgetIfReplacing,
setPageLayoutEditingWidgetId,
@@ -108,7 +108,7 @@ export const SidePanelRecordPageFieldsSettings = () => {
Icon={IconLayoutSidebarRight}
hasSubMenu
onClick={handleNavigateToLayout}
description={t`${totalFieldsCount} fields`}
description={t`${totalFieldsCount} visible fields`}
contextualTextPosition="right"
/>
</SelectableListItem>
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "twenty-sdk",
"version": "1.22.0-canary.3",
"version": "1.22.0-canary.6",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/sdk/index.d.ts",
@@ -83,6 +83,7 @@
"@types/node": "^24.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"rollup-plugin-dts": "^6.4.1",
"ts-morph": "^25.0.0",
"tsx": "^4.7.0",
"twenty-shared": "workspace:*",
+4 -3
View File
@@ -15,7 +15,8 @@
"cwd": "{projectRoot}",
"commands": [
"npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts && npx vite build -c vite.config.sdk.ts",
"tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist"
"tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist",
"npx rimraf 'dist/sdk/**/*.d.ts' 'dist/sdk/**/*.d.ts.map' && npx rollup -c rollup.config.sdk-dts.mjs"
],
"parallel": false
}
@@ -25,7 +26,7 @@
"dependsOn": ["^build"],
"options": {
"cwd": "packages/twenty-sdk",
"command": "npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts && npx vite build -c vite.config.sdk.ts && tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist && npx vite build -c vite.config.node.ts --watch & npx vite build -c vite.config.browser.ts --watch & npx vite build -c vite.config.sdk.ts --watch"
"command": "npx rimraf dist && npx vite build -c vite.config.node.ts && npx vite build -c vite.config.browser.ts && npx vite build -c vite.config.sdk.ts && tsgo -p tsconfig.lib.json --declaration --emitDeclarationOnly --noEmit false --outDir dist --rootDir src && npx tsc-alias -p tsconfig.lib.json --outDir dist && npx rimraf 'dist/sdk/**/*.d.ts' 'dist/sdk/**/*.d.ts.map' && npx rollup -c rollup.config.sdk-dts.mjs && npx vite build -c vite.config.node.ts --watch & npx vite build -c vite.config.browser.ts --watch & npx vite build -c vite.config.sdk.ts --watch"
}
},
"start": {
@@ -93,7 +94,7 @@
"outputs": ["{projectRoot}/dist/sdk"],
"options": {
"cwd": "{projectRoot}",
"command": "npx vite build -c vite.config.sdk.ts"
"command": "npx vite build -c vite.config.sdk.ts && npx rollup -c rollup.config.sdk-dts.mjs"
}
}
}
@@ -0,0 +1,26 @@
import dts from 'rollup-plugin-dts';
// Generates a self-contained SDK declaration file directly from source,
// inlining types from twenty-shared so npm consumers don't need it.
export default {
input: 'src/sdk/index.ts',
output: {
file: 'dist/sdk/index.d.ts',
format: 'es',
},
external: (id) => {
if (id === 'twenty-shared' || id.startsWith('twenty-shared/')) {
return false;
}
if (id.startsWith('@/')) {
return false;
}
return !id.startsWith('.') && !id.startsWith('/');
},
plugins: [
dts({
tsconfig: './tsconfig.lib.json',
respectExternal: true,
}),
],
};
@@ -16,6 +16,7 @@ const innerAppUninstall = async (
ConfigService.setActiveRemote(options.remote);
}
const configService = new ConfigService();
const apiService = new ApiService();
const manifest = await readManifestFromFile(options.appPath);
@@ -48,6 +49,13 @@ const innerAppUninstall = async (
};
}
await configService.setConfig({
appRegistrationId: undefined,
appRegistrationClientId: undefined,
appAccessToken: undefined,
appRefreshToken: undefined,
});
return { success: true, data: undefined };
};
@@ -1,4 +1,5 @@
import { type ConfigService } from '@/cli/utilities/config/config-service';
import { isOAuthInvalidClientError } from '@/cli/utilities/error/parse-server-error';
import { exchangeCredentialsForTokens } from './exchange-credentials-for-tokens';
@@ -50,6 +51,23 @@ export const ensureAppAccessTokenIsValidOrRefresh = async (
return data.access_token;
}
try {
const body = await response.json();
if (isOAuthInvalidClientError(body)) {
await configService.setConfig({
appRegistrationId: undefined,
appRegistrationClientId: undefined,
appAccessToken: undefined,
appRefreshToken: undefined,
});
return undefined;
}
} catch {
// Non-JSON error response (e.g. proxy 502) — fall through to credential exchange
}
}
if (credentials) {
@@ -1,19 +1,6 @@
import { type ApiService } from '@/cli/utilities/api/api-service';
import { type ConfigService } from '@/cli/utilities/config/config-service';
import { isDefined, isPlainObject } from 'twenty-shared/utils';
const isAlreadyClaimedError = (error: unknown): boolean => {
if (!isPlainObject(error)) {
return false;
}
const { extensions } = error as { extensions?: { subCode?: string } };
return (
isDefined(extensions) &&
extensions.subCode === 'UNIVERSAL_IDENTIFIER_ALREADY_CLAIMED'
);
};
import { hasGraphQLErrorSubCode } from '@/cli/utilities/error/parse-server-error';
export const ensureAppRegistration = async (
apiService: ApiService,
@@ -35,6 +22,8 @@ export const ensureAppRegistration = async (
await configService.setConfig({
appRegistrationId: applicationRegistration.id,
appRegistrationClientId: applicationRegistration.oAuthClientId,
appAccessToken: undefined,
appRefreshToken: undefined,
});
return {
@@ -44,7 +33,12 @@ export const ensureAppRegistration = async (
};
}
if (!isAlreadyClaimedError(createResult.error)) {
const isAlreadyClaimed = hasGraphQLErrorSubCode(
createResult.error,
'UNIVERSAL_IDENTIFIER_ALREADY_CLAIMED',
);
if (!isAlreadyClaimed) {
const errorDetail =
createResult.error instanceof Error
? createResult.error.message
@@ -70,6 +64,8 @@ export const ensureAppRegistration = async (
await configService.setConfig({
appRegistrationId: registration.id,
appRegistrationClientId: registration.oAuthClientId,
appAccessToken: undefined,
appRefreshToken: undefined,
});
const rotateResult =
@@ -0,0 +1,55 @@
import { isPlainObject } from 'twenty-shared/utils';
type GraphQLErrorEntry = {
message?: string;
extensions?: {
code?: string;
subCode?: string;
};
};
type OAuthErrorBody = {
error?: string;
error_description?: string;
};
const asGraphQLErrorEntry = (value: unknown): GraphQLErrorEntry | undefined => {
if (!isPlainObject(value)) {
return undefined;
}
return value as GraphQLErrorEntry;
};
export const getGraphQLErrorCode = (error: unknown): string | undefined => {
return asGraphQLErrorEntry(error)?.extensions?.code;
};
export const getGraphQLErrorSubCode = (error: unknown): string | undefined => {
return asGraphQLErrorEntry(error)?.extensions?.subCode;
};
export const hasGraphQLErrorSubCode = (
error: unknown,
subCode: string,
): boolean => {
return getGraphQLErrorSubCode(error) === subCode;
};
export const isGraphQLNotFoundError = (error: unknown): boolean => {
return getGraphQLErrorCode(error) === 'NOT_FOUND';
};
export const getOAuthError = (body: unknown): string | undefined => {
if (!isPlainObject(body)) {
return undefined;
}
const { error } = body as OAuthErrorBody;
return typeof error === 'string' ? error : undefined;
};
export const isOAuthInvalidClientError = (body: unknown): boolean => {
return getOAuthError(body) === 'invalid_client';
};
@@ -175,5 +175,58 @@ describe('defineField', () => {
'Field "Status" is a SELECT/MULTI_SELECT type and must have options',
);
});
it('should accept isUnique on a TEXT field', () => {
const config: FieldManifest = {
...validConfig,
isUnique: true,
};
const result = defineField(config);
expect(result.success).toBe(true);
expect(result.config?.isUnique).toBe(true);
});
it('should return error when isUnique is set on a RELATION field', () => {
const config = {
objectUniversalIdentifier: '20202020-b374-4779-a561-80086cb2e17f',
universalIdentifier: '550e8400-e29b-41d4-a716-446655440001',
type: FieldMetadataType.RELATION,
name: 'company',
label: 'Company',
isUnique: true,
relationTargetFieldMetadataUniversalIdentifier:
'550e8400-e29b-41d4-a716-446655440002',
relationTargetObjectMetadataUniversalIdentifier:
'550e8400-e29b-41d4-a716-446655440003',
universalSettings: { relationType: 'ONE_TO_MANY' },
};
const result = defineField(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain(
`Field "Company" of type ${FieldMetadataType.RELATION} cannot be unique`,
);
});
it('should return error when isUnique is set on a FILES field', () => {
const config = {
objectUniversalIdentifier: '20202020-b374-4779-a561-80086cb2e17f',
universalIdentifier: '550e8400-e29b-41d4-a716-446655440001',
type: FieldMetadataType.FILES,
name: 'attachments',
label: 'Attachments',
isUnique: true,
};
const result = defineField(config as any);
expect(result.success).toBe(false);
expect(result.errors).toContain(
`Field "Attachments" of type ${FieldMetadataType.FILES} cannot be unique`,
);
});
});
});
@@ -35,6 +35,17 @@ export const validateFields = (
`Field "${field.label}" is a SELECT/MULTI_SELECT type and must have options`,
);
}
if (
field.isUnique === true &&
(field.type === FieldMetadataType.RELATION ||
field.type === FieldMetadataType.MORPH_RELATION ||
field.type === FieldMetadataType.FILES)
) {
errors.push(
`Field "${field.label}" of type ${field.type} cannot be unique`,
);
}
}
return errors;
+15 -1
View File
@@ -1,10 +1,23 @@
export {
AggregateOperations,
DateDisplayFormat,
FieldMetadataSettingsOnClickAction,
HTTPMethod,
NavigationMenuItemType,
NumberDataType,
ObjectRecordGroupByDateGranularity,
PageLayoutTabLayoutMode,
ViewFilterGroupLogicalOperator,
ViewFilterOperand,
ViewOpenRecordIn,
ViewType,
ViewVisibility,
} from 'twenty-shared/types';
export type {
GridPosition,
PageLayoutWidgetConditionalDisplay,
PageLayoutWidgetUniversalConfiguration,
} from 'twenty-shared/types';
export type { PageLayoutWidgetUniversalConfiguration } from 'twenty-shared/types';
export type { ApplicationConfig } from './application/application-config';
export { defineApplication } from './application/define-application';
export type {
@@ -47,6 +60,7 @@ export type {
LogicFunctionConfig,
LogicFunctionHandler,
} from './logic-functions/logic-function-config';
export type { InputJsonSchema } from 'twenty-shared/logic-function';
export type { CronPayload } from './logic-functions/triggers/cron-payload-type';
export type {
DatabaseEventPayload,
+1
View File
@@ -48,6 +48,7 @@ FRONTEND_URL=http://localhost:3001
# SENTRY_ENVIRONMENT=main
# SENTRY_DSN=https://xxx@xxx.ingest.sentry.io/xxx
# SENTRY_FRONT_DSN=https://xxx@xxx.ingest.sentry.io/xxx
# APPLICATION_LOG_DRIVER=CONSOLE
# LOG_LEVELS=error,warn
# SERVER_URL=http://localhost:3000
# WORKSPACE_INACTIVE_DAYS_BEFORE_NOTIFICATION=7
@@ -1,14 +1,14 @@
import { type TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
import { type OPTIONS_TYPE } from 'src/engine/core-modules/application-logs/application-logs.module-definition';
import { ApplicationLogDriverType } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver-type.enum';
import { ApplicationLogDriver } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver.enum';
export const applicationLogsModuleFactory = async (
twentyConfigService: TwentyConfigService,
): Promise<typeof OPTIONS_TYPE> => {
const driverType = twentyConfigService.get('APPLICATION_LOG_DRIVER_TYPE');
const driverType = twentyConfigService.get('APPLICATION_LOG_DRIVER');
return {
type: driverType as ApplicationLogDriverType,
type: driverType as ApplicationLogDriver,
};
};
@@ -12,7 +12,7 @@ import { ApplicationLogsService } from 'src/engine/core-modules/application-logs
import { ClickHouseApplicationLogDriver } from 'src/engine/core-modules/application-logs/drivers/clickhouse.driver';
import { ConsoleApplicationLogDriver } from 'src/engine/core-modules/application-logs/drivers/console.driver';
import { DisabledApplicationLogDriver } from 'src/engine/core-modules/application-logs/drivers/disabled.driver';
import { ApplicationLogDriverType } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver-type.enum';
import { ApplicationLogDriver } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver.enum';
@Global()
@Module({
@@ -64,13 +64,13 @@ export class ApplicationLogsModule extends ConfigurableModuleClass {
}
private static createDriver(
type: ApplicationLogDriverType,
type: ApplicationLogDriver,
clickHouseService?: ClickHouseService,
) {
switch (type) {
case ApplicationLogDriverType.CONSOLE:
case ApplicationLogDriver.CONSOLE:
return new ConsoleApplicationLogDriver();
case ApplicationLogDriverType.CLICKHOUSE:
case ApplicationLogDriver.CLICKHOUSE:
if (!clickHouseService) {
throw new Error(
'ClickHouseService is required for the ClickHouse application log driver',
@@ -78,7 +78,7 @@ export class ApplicationLogsModule extends ConfigurableModuleClass {
}
return new ClickHouseApplicationLogDriver(clickHouseService);
case ApplicationLogDriverType.DISABLED:
case ApplicationLogDriver.DISABLED:
default:
return new DisabledApplicationLogDriver();
}
@@ -1,4 +1,4 @@
export enum ApplicationLogDriverType {
export enum ApplicationLogDriver {
DISABLED = 'DISABLED',
CONSOLE = 'CONSOLE',
CLICKHOUSE = 'CLICKHOUSE',
@@ -1,5 +1,5 @@
import { type ApplicationLogDriverType } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver-type.enum';
import { type ApplicationLogDriver } from 'src/engine/core-modules/application-logs/interfaces/application-log-driver.enum';
export type ApplicationLogsModuleOptions = {
type: ApplicationLogDriverType;
type: ApplicationLogDriver;
};

Some files were not shown because too many files have changed in this diff Show More