908aefe7c148763a9f9f350981aed22ef906ab48
12 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
908aefe7c1 |
feat: replace hardcoded AI model constants with JSON seed catalog (#18818)
## Summary - Replaces per-provider TypeScript constant files (`openai-models.const.ts`, `anthropic-models.const.ts`, etc.) with a single `ai-providers.json` catalog as the source of truth - Adds runtime model discovery via AI SDK for self-hosted providers, with `models.dev` enrichment for pricing/capabilities - Introduces composite model IDs (`provider/modelId`) for canonical, conflict-free identification - Simplifies provider configuration: API keys are injected from environment variables (e.g., `OPENAI_API_KEY`) - Adds admin panel UI for provider management (add/remove/test), model discovery, recommended model configuration, and default fast/smart model selection per workspace - Removes deprecated config variables (`AI_DISABLED_MODEL_IDS`, `AUTO_ENABLE_NEW_AI_MODELS`, etc.) - Adds database migration for composite model ID format ## Test plan - [ ] Server typecheck passes - [ ] Frontend typecheck passes - [ ] Server unit tests pass - [ ] Frontend unit tests pass - [ ] CI pipeline green - [ ] Admin panel AI tab loads correctly - [ ] Provider discovery works for configured providers - [ ] Model recommendation toggles persist - [ ] Default fast/smart model selection works Made with [Cursor](https://cursor.com) |
||
|
|
b456f79167 |
Reduce leak between gql schema (#17878)
## Reduce type leakage between GraphQL schemas ### Why Twenty runs two separate GraphQL schemas: **core** and **metadata**. NestJS's `@nestjs/graphql` uses a global `TypeMetadataStorage` that accumulates all decorated types across all modules. When each schema is built, every registered type leaks into both schemas regardless of which module it belongs to. This means the core schema's generated TypeScript (`generated/graphql.ts`) contained ~2,700 lines of types that only belong to the metadata schema (and vice versa). This creates confusion about type ownership, inflates generated code, and makes it harder to reason about which API surface each schema actually exposes. ### How **1. Patch `@nestjs/graphql` to support schema-scoped type resolution** - **(Already done)** Added a `resolverSchemaScope` option to `GqlModuleOptions`, allowing each schema to declare a scope (e.g. `'metadata'`) - `ResolversExplorerService` now filters resolvers by a `RESOLVER_SCHEMA_SCOPE` metadata key, so each schema only sees its own resolvers - `GraphQLSchemaFactory` now performs a **reachability walk** (`computeReachableTypes`) starting from scoped resolver return types and arguments, only including types that are transitively referenced — handling unions, interfaces, and prototype chains - Type definition storage and orphaned reference registry are cleared between schema builds to prevent cross-contamination **2. Register `ClientConfig` as orphaned type in metadata schema** Since `ClientConfig` is needed in the metadata schema but not directly returned by a resolver, it's explicitly declared via `buildSchemaOptions.orphanedTypes`. **3. Regenerate frontend types and fix imports** - `generated/graphql.ts` shrank by ~2,700 lines (types moved to where they belong) - `generated-metadata/graphql.ts` gained types like `ClientConfig` that were previously missing - ~500 frontend files updated to import from the correct generated file |
||
|
|
b27a97f2c5 |
feat: enforce @/ alias for imports and fix all relative parent imports (#16787)
## Summary This PR enforces the use of `@/` alias for imports instead of relative parent imports (`../`). ## Changes ### ESLint Configuration - Added `no-restricted-imports` pattern in `eslint.config.react.mjs` to block `../*` imports with the message "Relative parent imports are not allowed. Use @/ alias instead." - Removed the non-working `import/no-relative-parent-imports` rule (doesn't work properly in ESLint flat config) ### VS Code Settings - Added `javascript.preferences.importModuleSpecifier: non-relative` to `.vscode/settings.json` (TypeScript setting was already there) ### Code Fixes - Fixed **941 relative parent imports** across **706 files** in `packages/twenty-front` - All `../` imports converted to use `@/` alias ## Why - Consistent import style across the codebase - Easier to move files without breaking imports - Better IDE support for auto-imports - Clearer understanding of where imports come from |
||
|
|
1088f7bbab |
feat: add lingui/no-unlocalized-strings ESLint rule and fix translations (#16610)
## Summary
This PR adds the `lingui/no-unlocalized-strings` ESLint rule to detect
untranslated strings and fixes translation issues across multiple
components.
## Changes
### ESLint Configuration (`eslint.config.react.mjs`)
- Added comprehensive `ignore` patterns for non-translatable strings
(CSS values, HTML attributes, technical identifiers)
- Added `ignoreNames` for props that don't need translation (className,
data-*, aria-*, etc.)
- Added `ignoreFunctions` for console methods, URL APIs, and other
non-user-facing functions
- Disabled rule for debug files, storybook, and test files
### Components Fixed (~19 files)
- Object record components (field inputs, pickers, merge dialogs)
- Settings components (accounts, admin panel)
- Serverless function components
- Record table and title cell components
## Status
🚧 **Work in Progress** - ~124 files remaining to fix
This PR is being submitted as draft to allow progressive fixing of
remaining translation issues.
## Testing
- Run `npx eslint "src/**/*.tsx"` in `packages/twenty-front` to check
remaining issues
|
||
|
|
da3c3d2b9d |
[GroupBy] Add views filters to groupBy query (#14762)
Closes https://github.com/twentyhq/core-team-issues/issues/1560 If viewId is defined in a groupBy query, we want to apply all filters of the view to the query. This required to move a lot of code from twenty-front to twenty-shared to convert the filters as stored in the db into graphql filters, applying the right combinations between filters etc., which was previously only done in the FE. This PR does not handle any field filters, it will be done in a later pr |
||
|
|
464a480043 |
Continue ESLINT9 Migration (#13795)
Might already fix #13793 |
||
|
|
de802b1447 |
Removed all v2 in naming of state management logic (#13675)
This PR removes any V2 naming in state management logic and some minor utils and hooks. It has a lot of changes but nearly all of them were made by the rename functionality of vscode which is deterministic, so it shouldn't introduce any regression. QA has been made on this PR on the main features of the app without any noticeable issue. Also renamed some other v2 naming related items : - TextInputV2 => TextInput - TextInput => SettingsTextInput - ObjectFilterDropdownFilterSelectMenuItemV2 => ObjectFilterDropdownFilterSelectMenuItem - useInitDraftValueV2 => useInitDraftValue - useOpenRecordTableCellV2 => useOpenRecordTableCell |
||
|
|
c6e5bab4e9 |
Replace hotkey scopes by focus stack (Part 4 - Inputs) (#12933)
# Replace hotkey scopes by focus stack (Part 4 - Inputs) This PR is the 4th part of a refactoring aiming to deprecate the hotkey scopes api in favor of the new focus stack api which is more robust. Part 1: https://github.com/twentyhq/twenty/pull/12673 Part 2: https://github.com/twentyhq/twenty/pull/12798 Part 3: https://github.com/twentyhq/twenty/pull/12910 In this part, I refactored all inputs in the app so that each input has a unique id which can be used to track the focused element. |
||
|
|
2d5312276c |
Improve FE error handling (#12864)
This PR aims at improving readability in sentry and user experience with runtime errors. **GraphQL errors (and ApolloError)** 1. In sentry we have a lot of "Object captured as exception with keys: extensions, message" errors (2k over the last 90d), on which we have zero information. This is because in apollo-factory we were passing on GraphQL errors to sentry directly why sentry expects the structure of a JS Error. We are now changing that, rebuilding an Error object and attempting to help grouping by creating a fingerPrint based on error code and truncated operationName (same as we do in the back for 500 graphql errors). 2. In sentry we have a lot of ApolloError, who actually correspond to errors that should not be logged in sentry (Forbidden errors such as "Email is not verified"), or errors that are already tracked by back-end (Postgres errors such as "column xxx does not exist"). This is because ApolloErrors become unhandled rejections errors if they are not caught and automatically sent to sentry through the basic config. To change that we are now filtering out ApolloErrors created from GraphQL Errors before sending error to sentry: <img width="524" alt="image" src="https://github.com/user-attachments/assets/02974829-26d9-4a9e-8c4c-cfe70155e4ab" /> **Runtime errors** 4. Runtime errors were all caught by sentry with the name "Error", making them not easy to differentiate on sentry (they were not grouped together but all appeared in the list as "Error"). We are replacing the "Error" name with the error message, or the error code if present. We are introducing a CustomError class that allows errors whose message contain dynamic text (an id for instance) to be identified on sentry with a common code. _(TODO: if this approach is validated then I have yet to replace Error with dynamic error messages with CustomError)_ 5. Runtime error messages contain technical details that do not mean anything to users (for instance, "Invalid folder ID: ${droppableId}", "ObjectMetadataItem not found", etc.). Let's replace them with "Please refresh the page." to users and keep the message error for sentry and our dev experience (they will still show in the console as uncaught errors). |
||
|
|
cbc0d06a2f |
Replace hotkey scopes by focus stack (Part 1 - Dropdowns and Side Panel) (#12673)
This PR is the first part of a refactoring aiming to deprecate the hotkey scopes api in favor of the new focus stack api which is more robust. The refactored components in this PR are the dropdowns and the side panel/command menu. - Replaced `useScopedHotkeys` by `useHotkeysOnFocusedElement` for all dropdown components, selectable lists and the command menu - Introduced `focusId` for all dropdowns and created a common hotkey scope `DropdownHotkeyScope` for backward compatibility - Replaced `setHotkeyScopeAndMemorizePreviousScope` occurrences with `usePushFocusItemToFocusStack` and `goBackToPreviousHotkeyScope` with `removeFocusItemFromFocusStack` Note: Test that the shorcuts and arrow key navigation still work properly when interacting with dropdowns and the command menu. Bugs that I have spotted during the QA but which are already present on main: - Icon picker select with arrow keys doesn’t work inside dropdowns - Some dropdowns are not selectable with arrow keys (no selectable list) - Dropdowns in dropdowns don’t reset the hotkey scope correctly when closing - The table click outside is not triggered after closing a table cell and clicking outside of the table |
||
|
|
01b40e173b |
Refactored dropdown content and fixed all dropdown width bugs (#12334)
This PR refactors all the dropdown content wrapping mechanism across the entire app. It refactors the internals of the `Dropdown` component and introduces a new generic `DropdownContent` component that is a generic wrapper used for each dropdown. ## Why this PR ? Because we’ve been experiencing continuous regressions for months on the dropdown content width, with weird scrolling behaviors in some and not in others, and every time a solution was found for a particular set of dropdowns, it broke another set of dropdowns, which wasn’t noticed because doing the QA of all dropdowns of the app is very difficult for fixing an apparently small bug. ## Don’t we already have a `DropdownMenu` component ? Indeed, this new `DropdownContent` is almost like `DropdownMenu` and took inspiration from it but `DropdownContent` acts as a generic content container that sets the width of the whole dropdown, whether we have a menu or not. ## Why don’t we put it directly in Dropdown internals ? Because the Dropdown component is using a complex logic with floating-ui middleware to compute its position and size, and for this logic to work correctly, it cannot be responsible for the “wanted” width of its content, because the children components, which the dropdown is not aware of, can request different widths after the dropdown has been mounted. A good example with multiple use cases inside the same dropdown can be found in `AdvancedFilterDropdownFilterInput` Thus, it is the responsibility of the content of the dropdown to determine the width it wants to have. ## What is the difference with DropdownMenuItemsContainer ? We can have multiple `DropdownMenuItemsContainer` in a dropdown, alongside other components like `DropdownMenuSeparator` or `DropdownMenuHeader`, and each of those components behaves differently regarding to its width, paddings, etc. Therefore it is logical that the `DropdownMenuItemsContainer` cannot be responsible for the whole dropdown content width, and trying to do so has been the cause of many regressions for months. Now `DropdownMenuItemsContainer` is taking a width of `auto` by default, which is the best to adapt to a parent which has a defined width. ## How do I set the width of my dropdown now ? By passing a pixel width to the props `widthInPixels` of `DropdownContent`, which only accepts numbers to avoid any confusion with `auto` , `100%` or `160px` and other specific width variables. The `dropdownWidth` props has been removed from `<Dropdown>` to avoid any confusion. Also the `DropdownMenuItemsContainer` is now using `auto` as its default width to fill the available space inside `DropdownContent` . It is highly recommended to use the enum `GenericDropdownContentWidt` to define your width. ## Where to use this new `DropdownContent` component ? There are two main use cases. If the dropdown content is defined directly inline in the Dropdown props, then it is recommended to use it here too. On the other hand if the dropdown content is abstracted in another component, it’s recommended to use this new component alongside the others components like `DropdownMenuItemsContainer`. A good rule of thumb is to place `DropdownContent` where `DropdownMenuItemsContainer`, `DropdownMenuSearchInput`, etc. are placed. ## What if I have a custom width ? Just define a constant like `ICON_PICKER_DROPDOWN_CONTENT_WIDTH` and use it with the props `widthInPixels` . Otherwise there’s a `GenericDropdownContentWidth` enum. The default value being `GenericDropdownContentWidth.Medium` (or 200px), which most dropdowns use. ## QA Component | Comment -- | -- AttachmentDropdown | Fixed overflowing (thanks to DropdownContent) RecordIndexActionMenuDropdown | CommandMenuActionMenuDropdown | SupportDropdown | Fixed overflowing (thanks to DropdownContent) MessageThreadSubscribersDropdownButton | Removed because unused FavoriteFolderNavigationDrawerItemDropdown | Set width at Narrow FavoriteFolderPicker | ViewPickerOptionDropdown | PageFavoriteFolderDropdown | Removed because unused AdvancedFilterAddFilterRuleSelect | AdvancedFilterAddFilterRuleSelect | AdvancedFilterFieldSelectMenu | AdvancedFilterRecordFilterGroupOptionsDropdown | AdvancedFilterRecordFilterOperanceSelect | Set width at Narrow AdvancedFilterLogicalOperatorDropdown | Set width at Narrow AdvancedFilterRecordFilterOptionsDropdown | AdvancedFilterRootRecordFilterGroup | Fixed broken horizontal scrolling behavior AdvancedFilterSubFieldSelectMenu | AdvancedFilterDropdownFilterInput | ObjectFilterDropdownBooleanSelect | ObjectFilterDropdownCountrySelect | Fixed broken menu items container ObjectFilterDropdownCurrencySelect | Set width to Large ObjectFilterDropdownFilterInput | ObjectFilterDropdownOperandDropdown | Fixed width that was not fixed ObjectFilterDropdownFilterInput | Fixed width that wasn’t the same for EditableFilterChip ObjectFilterDropdownOperandSelect | Refactored ObjectOptionsDropdownRecordGroupFieldsContent | Added missing separator ObjectOptionDropdownFieldsContent | ObjectOptionsDropdownHiddenFieldsContent | ObjectOptionsDropdownLayoutContent | ObjectOptionsDropdownLayoutOpenInContent | ObjectOptionsDropdownMenuContent | ObjectOptionsDropdownRecordGroupFieldsContent | ObjectOptionsDropdownRecordGroupsContent | ObjectOptionsDropdownRecordGroupSortContent | ObjectOptionsDropdownHiddenRecordGroupsContent | Removed unnecessary DropdownMenuItemsContainer RecordBoardColumnHeaderAggregateDropdown | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownFieldsContent | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownMenuContent | Fixed overflowing (thanks to DropdownContent) RecordBoardColumnHeaderAggregateDropdownOptionsContent | Fixed overflowing (thanks to DropdownContent) MultiItemFieldInput | Fixed overflowing (thanks to DropdownContent) MultiItemFieldMenuItem | MultipleRecordPicker | Fixed overflowing (thanks to DropdownContent) SingleRecordPicker | RecordTableColumnAggregateDropdownSubmenuContent | RecordTableColumnAggregateFooterMenuContent | RecordTableColumnHeadDropdownMenu | Fixed overflowing (thanks to DropdownContent) RecordTableHeaderPlusButtonContent | MultipleSelectDropdown | Broken width fixed ObjectSortDropdownButton | RecordDetailRelationRecordsListItem | ConfigVariableDatabaseInput | ConfigVariableOptionsDropdownContent | SettingsObjectFieldActiveActionDropdown | Fixed overflowing (thanks to DropdownContent) SettingsObjectFieldDisabledActionDropdown | Set width at Narrow SettingsObjectSummaryCard | Removed because unused SettingsDataModelFieldSelectFormOptionRow | SettingsDataModelNewFieldBreadcrumbDropdown | SettingsObjectInactiveMenuDropDown | SettingsRoleAssignementWorkspaceMemberPickerDropdown | SettingsRolePermissionObjectLevelObjectPickerDropdownContent | SettingsSecurityApprovedAccessDomainRowDropdownMenu | Couldn’t test SettingsSecuritySSORowDropdownMenu | Couldn’t test SettingsAccountsRowDropdownMenu | Fixed overflowing (thanks to DropdownContent) SettingsIntegrationDatabaseConnectionSummaryCard | Couldn’t test SettingsServerlessFunctionTablEnvironmentVariableTableRow | Deactivated scope MatchColumnSelectFieldSelectDropdownContent | Removed now unnecessary width on DropdownMenuItemsContainer MatchColumnSelectSubFieldSelectDropdownContent | SubMatchingSelectInput | CurrencyPickerDropdownSelect | IconPicker | Fixed overflowing (thanks to DropdownContent) PhoneCountryPickerDropdownSelect | Select | Refactored to drilldown wanted width of content, in this case it’s intended ExpandedListDropdown | ShowPageAddButton | Removed because unused MultiWorkspaceDropdownDefaultComponent | MultiWorkspaceDropdownThemesComponent | MultiWorkspaceDropdownWorkspacesListComponent | AdvancedFilterDropdownButton | EditableFilterChip | EditableFilterDropdownButton | UpdateViewButtonGroup | ViewBarFilterDropdown | ViewBarFilterDropdownFieldSelectMenu | ViewPickerContentCreateMode | ViewPickerContentEditMode | ViewPickerListContent | WorkflowEditTriggerDatabaseEventForm | WorkflowVariablesDropdownFieldItems | WorkflowVariablesDropdownObjectItems | WorkflowVariablesDropdownWorkflowStepItems | CommandMenuContextChipGroups | RecordBoardColumnDropdownMenu | MultiSelectInput | SelectInput | CustomSlashMenu | DropdownMenu | Removed and replaced by DropdownContent OverlayContainer and around | <!-- notionvc: 1e23bdb8-2dda-4f8d-a64d-ecc829a768a2 --> ## Miscellaneous Side notes : - The `Select` component is now wrapping the `DropdownContent` because it computes a dynamic width. - The advanced filter dropdown has been fixed, it was broken when resizing the window horizontally, we couldn’t scroll. This specific edge case was taken into account when refactoring the whole dropdown content system - As discussed with Nitin, data-select-disable will probably be removed entirely, so I let it as is, because right now it is not used by the refactored d&d selection. - Duplicate separators under DropdownMenuHeader have been removed. Fixes : https://github.com/twentyhq/twenty/issues/12327 Fixes : https://github.com/twentyhq/core-team-issues/issues/951 |
||
|
|
e957b1acd6 |
Twenty config admin panel integration (#11755)
closes https://github.com/twentyhq/core-team-issues/issues/761 closes https://github.com/twentyhq/core-team-issues/issues/762 --------- Co-authored-by: Félix Malfait <felix@twenty.com> |