feat: fix junction toggle persistence and add type-safe documentation paths (#17421)

## Summary

- **Fix junction relation toggle not being saved**: The form schema
wasn't tracking the `settings` field, so changes to
`junctionTargetFieldId` weren't marked as dirty
- **Add type-safe documentation paths**: Generate TypeScript constants
from `base-structure.json` to prevent broken documentation links
- **Create many-to-many relations documentation**: Step-by-step guide
for building many-to-many relations using junction objects
- **Update `getDocumentationUrl`**: Now uses shared constants from
`twenty-shared` for base URL, default path, and supported languages

## Key Changes

### Junction Toggle Fix
- Added `settings` field to the form schema in
`SettingsDataModelFieldRelationForm.tsx`
- Fixed the toggle to properly merge settings when updating
`junctionTargetFieldId`

### Type-Safe Documentation Paths
- New constants in `twenty-shared/constants`:
- `DOCUMENTATION_PATHS` - All 161 documentation paths as typed constants
  - `DOCUMENTATION_SUPPORTED_LANGUAGES` - 14 supported languages
  - `DOCUMENTATION_BASE_URL` / `DOCUMENTATION_DEFAULT_PATH`
- Generator script: `yarn docs:generate-paths`
- CI integration: Added to `docs-i18n-pull.yaml` workflow

### Documentation
- New article:
`/user-guide/data-model/how-tos/create-many-to-many-relations`
- Updated `/user-guide/data-model/capabilities/relation-fields.mdx` with
Lab warning and link

## Test plan
- [ ] Verify junction toggle saves correctly when enabled/disabled
- [ ] Verify documentation link opens correct localized page
- [ ] Verify `yarn docs:generate-paths` regenerates paths correctly
This commit is contained in:
Félix Malfait
2026-01-25 13:29:20 +01:00
committed by GitHub
parent 3b512164d1
commit 161689be18
22 changed files with 736 additions and 50 deletions
+4 -1
View File
@@ -107,10 +107,13 @@ jobs:
- name: Regenerate docs.json
run: yarn docs:generate
- name: Regenerate documentation paths constants
run: yarn docs:generate-paths
- name: Commit artifacts to pull request branch
if: github.event_name == 'pull_request'
run: |
git add packages/twenty-docs/docs.json packages/twenty-docs/navigation/navigation.template.json
git add packages/twenty-docs/docs.json packages/twenty-docs/navigation/navigation.template.json packages/twenty-shared/src/constants/DocumentationPaths.ts
if git diff --staged --quiet --exit-code; then
echo "No navigation/doc changes to commit."
exit 0
+1
View File
@@ -210,6 +210,7 @@
"scripts": {
"docs:generate": "tsx packages/twenty-docs/scripts/generate-docs-json.ts",
"docs:generate-navigation-template": "tsx packages/twenty-docs/scripts/generate-navigation-template.ts",
"docs:generate-paths": "tsx packages/twenty-docs/scripts/generate-documentation-paths.ts",
"start": "npx concurrently --kill-others 'npx nx run-many -t start -p twenty-server twenty-front' 'npx wait-on tcp:3000 && npx nx run twenty-server:worker'"
},
"workspaces": {
+14
View File
@@ -80,6 +80,7 @@
"user-guide/data-model/how-tos/create-custom-objects",
"user-guide/data-model/how-tos/create-custom-fields",
"user-guide/data-model/how-tos/create-relation-fields",
"user-guide/data-model/how-tos/create-many-to-many-relations",
"user-guide/data-model/how-tos/customize-your-data-model",
"user-guide/data-model/how-tos/data-model-faq"
]
@@ -524,6 +525,7 @@
"l/fr/user-guide/data-model/how-tos/create-custom-objects",
"l/fr/user-guide/data-model/how-tos/create-custom-fields",
"l/fr/user-guide/data-model/how-tos/create-relation-fields",
"l/fr/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/fr/user-guide/data-model/how-tos/customize-your-data-model",
"l/fr/user-guide/data-model/how-tos/data-model-faq"
]
@@ -968,6 +970,7 @@
"l/ar/user-guide/data-model/how-tos/create-custom-objects",
"l/ar/user-guide/data-model/how-tos/create-custom-fields",
"l/ar/user-guide/data-model/how-tos/create-relation-fields",
"l/ar/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/ar/user-guide/data-model/how-tos/customize-your-data-model",
"l/ar/user-guide/data-model/how-tos/data-model-faq"
]
@@ -1412,6 +1415,7 @@
"l/cs/user-guide/data-model/how-tos/create-custom-objects",
"l/cs/user-guide/data-model/how-tos/create-custom-fields",
"l/cs/user-guide/data-model/how-tos/create-relation-fields",
"l/cs/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/cs/user-guide/data-model/how-tos/customize-your-data-model",
"l/cs/user-guide/data-model/how-tos/data-model-faq"
]
@@ -1856,6 +1860,7 @@
"l/de/user-guide/data-model/how-tos/create-custom-objects",
"l/de/user-guide/data-model/how-tos/create-custom-fields",
"l/de/user-guide/data-model/how-tos/create-relation-fields",
"l/de/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/de/user-guide/data-model/how-tos/customize-your-data-model",
"l/de/user-guide/data-model/how-tos/data-model-faq"
]
@@ -2300,6 +2305,7 @@
"l/es/user-guide/data-model/how-tos/create-custom-objects",
"l/es/user-guide/data-model/how-tos/create-custom-fields",
"l/es/user-guide/data-model/how-tos/create-relation-fields",
"l/es/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/es/user-guide/data-model/how-tos/customize-your-data-model",
"l/es/user-guide/data-model/how-tos/data-model-faq"
]
@@ -2744,6 +2750,7 @@
"l/it/user-guide/data-model/how-tos/create-custom-objects",
"l/it/user-guide/data-model/how-tos/create-custom-fields",
"l/it/user-guide/data-model/how-tos/create-relation-fields",
"l/it/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/it/user-guide/data-model/how-tos/customize-your-data-model",
"l/it/user-guide/data-model/how-tos/data-model-faq"
]
@@ -3188,6 +3195,7 @@
"l/ja/user-guide/data-model/how-tos/create-custom-objects",
"l/ja/user-guide/data-model/how-tos/create-custom-fields",
"l/ja/user-guide/data-model/how-tos/create-relation-fields",
"l/ja/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/ja/user-guide/data-model/how-tos/customize-your-data-model",
"l/ja/user-guide/data-model/how-tos/data-model-faq"
]
@@ -3632,6 +3640,7 @@
"l/ko/user-guide/data-model/how-tos/create-custom-objects",
"l/ko/user-guide/data-model/how-tos/create-custom-fields",
"l/ko/user-guide/data-model/how-tos/create-relation-fields",
"l/ko/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/ko/user-guide/data-model/how-tos/customize-your-data-model",
"l/ko/user-guide/data-model/how-tos/data-model-faq"
]
@@ -4076,6 +4085,7 @@
"l/pt/user-guide/data-model/how-tos/create-custom-objects",
"l/pt/user-guide/data-model/how-tos/create-custom-fields",
"l/pt/user-guide/data-model/how-tos/create-relation-fields",
"l/pt/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/pt/user-guide/data-model/how-tos/customize-your-data-model",
"l/pt/user-guide/data-model/how-tos/data-model-faq"
]
@@ -4520,6 +4530,7 @@
"l/ro/user-guide/data-model/how-tos/create-custom-objects",
"l/ro/user-guide/data-model/how-tos/create-custom-fields",
"l/ro/user-guide/data-model/how-tos/create-relation-fields",
"l/ro/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/ro/user-guide/data-model/how-tos/customize-your-data-model",
"l/ro/user-guide/data-model/how-tos/data-model-faq"
]
@@ -4964,6 +4975,7 @@
"l/ru/user-guide/data-model/how-tos/create-custom-objects",
"l/ru/user-guide/data-model/how-tos/create-custom-fields",
"l/ru/user-guide/data-model/how-tos/create-relation-fields",
"l/ru/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/ru/user-guide/data-model/how-tos/customize-your-data-model",
"l/ru/user-guide/data-model/how-tos/data-model-faq"
]
@@ -5408,6 +5420,7 @@
"l/tr/user-guide/data-model/how-tos/create-custom-objects",
"l/tr/user-guide/data-model/how-tos/create-custom-fields",
"l/tr/user-guide/data-model/how-tos/create-relation-fields",
"l/tr/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/tr/user-guide/data-model/how-tos/customize-your-data-model",
"l/tr/user-guide/data-model/how-tos/data-model-faq"
]
@@ -5852,6 +5865,7 @@
"l/zh/user-guide/data-model/how-tos/create-custom-objects",
"l/zh/user-guide/data-model/how-tos/create-custom-fields",
"l/zh/user-guide/data-model/how-tos/create-relation-fields",
"l/zh/user-guide/data-model/how-tos/create-many-to-many-relations",
"l/zh/user-guide/data-model/how-tos/customize-your-data-model",
"l/zh/user-guide/data-model/how-tos/data-model-faq"
]
@@ -52,6 +52,7 @@
"user-guide/data-model/how-tos/create-custom-objects",
"user-guide/data-model/how-tos/create-custom-fields",
"user-guide/data-model/how-tos/create-relation-fields",
"user-guide/data-model/how-tos/create-many-to-many-relations",
"user-guide/data-model/how-tos/customize-your-data-model",
"user-guide/data-model/how-tos/data-model-faq"
]
@@ -1,21 +1,7 @@
export const DEFAULT_LANGUAGE = 'en' as const;
export const SUPPORTED_LANGUAGES = [
DEFAULT_LANGUAGE,
'fr',
'ar',
'cs',
'de',
'es',
'it',
'ja',
'ko',
'pt',
'ro',
'ru',
'tr',
'zh',
] as const;
export type SupportedLanguage = (typeof SUPPORTED_LANGUAGES)[number];
// Re-export from twenty-shared (source of truth)
// Using relative path because these scripts run via tsx from workspace root
export { DOCUMENTATION_DEFAULT_LANGUAGE as DEFAULT_LANGUAGE } from '../../twenty-shared/src/constants/DocumentationDefaultLanguage';
export {
DOCUMENTATION_SUPPORTED_LANGUAGES as SUPPORTED_LANGUAGES,
type DocumentationSupportedLanguage as SupportedLanguage,
} from '../../twenty-shared/src/constants/DocumentationSupportedLanguages';
+3
View File
@@ -13,6 +13,9 @@
"dependencies": {
"mintlify": "latest"
},
"devDependencies": {
"twenty-shared": "workspace:*"
},
"engines": {
"node": "^24.5.0",
"npm": "please-use-yarn",
@@ -0,0 +1,101 @@
import fs from 'fs';
import path from 'path';
type BasePage = string | BaseGroup;
type BaseGroup = {
key: string;
label: string;
icon?: string;
pages: BasePage[];
};
type BaseTab = {
key: string;
label: string;
groups: BaseGroup[];
};
type BaseStructure = {
tabs: BaseTab[];
};
const baseStructurePath = path.resolve(
__dirname,
'../navigation/base-structure.json',
);
const outputDir = path.resolve(__dirname, '../../twenty-shared/src/constants');
const baseStructure: BaseStructure = JSON.parse(
fs.readFileSync(baseStructurePath, 'utf8'),
);
const extractPaths = (pages: BasePage[]): string[] => {
const paths: string[] = [];
for (const page of pages) {
if (typeof page === 'string') {
paths.push(page);
} else {
paths.push(...extractPaths(page.pages));
}
}
return paths;
};
const pathToConstantName = (docPath: string): string => {
return docPath.replace(/^\//, '').replace(/[/-]/g, '_').toUpperCase();
};
const allPaths: string[] = [];
for (const tab of baseStructure.tabs) {
for (const group of tab.groups) {
allPaths.push(...extractPaths(group.pages));
}
}
const sortedPaths = [...allPaths].sort();
const AUTO_GENERATED_HEADER = `/*
* _____ _
*|_ _|_ _____ _ __ | |_ _ _
* | | \\ \\ /\\ / / _ \\ '_ \\| __| | | | Auto-generated file
* | | \\ V V / __/ | | | |_| |_| | DO NOT EDIT - changes will be overwritten
* |_| \\_/\\_/ \\___|_| |_|\\__|\\__, | Generated by: yarn docs:generate-paths
* |___/
*
* This file is generated from packages/twenty-docs/navigation/base-structure.json
* To add new documentation pages, add them to base-structure.json and run:
* yarn docs:generate-paths
*/
`;
// Generate DocumentationPaths.ts
const pathEntries = sortedPaths
.map((docPath) => {
const constName = pathToConstantName(docPath);
const value = `'/${docPath}'`;
// Check if line would be too long (80 char limit)
if (` ${constName}: ${value},`.length > 80) {
return ` ${constName}:\n ${value},`;
}
return ` ${constName}: ${value},`;
})
.join('\n');
const pathsContent = `${AUTO_GENERATED_HEADER}export const DOCUMENTATION_PATHS = {
${pathEntries}
} as const;
export type DocumentationPath =
(typeof DOCUMENTATION_PATHS)[keyof typeof DOCUMENTATION_PATHS];
`;
fs.writeFileSync(path.join(outputDir, 'DocumentationPaths.ts'), pathsContent);
console.log(`Generated documentation constants to ${outputDir}`);
console.log(`Total paths: ${sortedPaths.length}`);
@@ -37,12 +37,16 @@ Many records in Object A can be linked to many records in Object B.
**Example:** Many People can be linked to many Projects, and vice versa.
<Warning>
**Many-to-Many is not yet supported.**
Many-to-many relations use a **junction object** pattern: an intermediate object that connects both sides. With the junction relation feature, Twenty displays the final linked records directly, hiding the intermediate object from the UI.
This relation type is planned for H1 2026. As a workaround, create an intermediate "junction" object (e.g., "Project Assignments") that has Many-to-One relations to both objects.
<img src="/images/user-guide/fields/junction-relation-diagram.png" style={{width:'100%'}}/>
<Warning>
**Lab Feature**: Junction relations must be enabled at **Settings → Updates → Lab** before use.
</Warning>
See [How to Create Many-to-Many Relations](/user-guide/data-model/how-tos/create-many-to-many-relations) for a complete step-by-step guide.
## Creating a Relation Field
1. Go to **Settings → Data Model**
@@ -0,0 +1,175 @@
---
title: Create Many-to-Many Relations
description: Connect records where many items on both sides can be linked together using junction objects.
---
Many-to-many relations let you connect multiple records on both sides. For example: many People can work on many Projects, and each Project can have many People.
<Warning>
**Lab Feature**: Junction relations are currently in the Lab. Enable them at **Settings → Updates → Lab** before following this guide.
</Warning>
<Note>
This feature also requires **Advanced mode** to be enabled (toggle at the bottom right of Settings).
</Note>
## When to Use Many-to-Many
Use many-to-many when both sides of a relationship can have multiple connections:
| Relationship | Example |
|-------------|---------|
| People ↔ Projects | A person works on multiple projects; a project has multiple team members |
| Companies ↔ Tags | A company can have multiple tags; a tag can apply to multiple companies |
| Products ↔ Orders | A product can be in multiple orders; an order contains multiple products |
## How It Works
Twenty uses a **junction object** pattern for many-to-many relations. A junction object sits between two objects and holds the connections:
```
People ←→ Project Assignments ←→ Projects
```
The **Project Assignments** object (junction) has:
- A relation to People (many-to-one)
- A relation to Projects (many-to-one)
When you enable the junction relation toggle, Twenty displays linked records directly instead of showing the intermediate junction records.
## Prerequisites
1. **Enable Junction Relations in Lab**: Go to **Settings → Updates → Lab** and enable **Junction Relations**
2. **Enable Advanced mode**: Toggle on **Advanced mode** at the bottom right of the Settings sidebar
3. Plan your data model:
- Which two objects are you connecting?
- What should the junction object be called?
## Step 1: Create the Junction Object
First, create the intermediate object that will hold the connections.
1. Go to **Settings → Data Model**
2. Click **+ New object**
3. Name it descriptively (e.g., "Project Assignment", "Team Member", "Product Order")
4. Click **Save**
<Tip>
**Naming convention**: Use a name that describes the relationship, like "Project Assignment" or "Team Membership". This makes the data model easier to understand.
</Tip>
## Step 2: Create Relations from the Junction Object
Add relation fields from the junction object to both objects you want to connect.
### First Relation (Junction → Object A)
1. Select your junction object in **Settings → Data Model**
2. Click **+ Add Field**
3. Choose **Relation** as the field type
4. Select the first object (e.g., "People")
5. Set the relation type to **Many-to-One** (many assignments can link to one person)
6. Name the fields:
- Field on junction: e.g., "Person"
- Field on People: e.g., "Project Assignments"
7. Click **Save**
### Second Relation (Junction → Object B)
1. Still on the junction object, click **+ Add Field**
2. Choose **Relation** as the field type
3. Select the second object (e.g., "Projects")
4. Set the relation type to **Many-to-One**
5. Name the fields:
- Field on junction: e.g., "Project"
- Field on Projects: e.g., "Team Members"
6. Click **Save**
## Step 3: Configure the Junction Relation Display
Now configure the source objects to display linked records directly, skipping the intermediate junction object.
1. Go to **Settings → Data Model**
2. Select the first object (e.g., "People")
3. Find the relation field pointing to the junction object (e.g., "Project Assignments")
4. Click to edit the field
5. Enable **"This is a relation to a Junction Object"**
6. Select the **Target relation** (e.g., "Project" — the field on the junction that points to the other side)
7. Click **Save**
{/* TODO: Add image
<img src="/images/user-guide/fields/junction-relation-toggle.png" style={{width:'100%'}}/>
*/}
Repeat for the other object:
1. Select "Projects" in Data Model
2. Edit the "Team Members" relation field
3. Enable the junction toggle
4. Select "Person" as the target relation
5. Save
## Result
After configuration:
- On a **Person** record, the "Project Assignments" field displays **Projects** directly (not assignment records)
- On a **Project** record, the "Team Members" field displays **People** directly
The junction object still exists and stores the connections, but the UI presents a cleaner many-to-many view.
## Example: People ↔ Projects
Here's a complete walkthrough:
### Create the Junction Object
- Name: **Project Assignment**
- Description: "Links people to projects they work on"
### Add Relations
1. **Project Assignment → People**
- Type: Many-to-One
- Field on Assignment: "Person"
- Field on People: "Project Assignments"
2. **Project Assignment → Projects**
- Type: Many-to-One
- Field on Assignment: "Project"
- Field on Projects: "Team Members"
### Configure Junction Display
1. On **People** object:
- Edit "Project Assignments" field
- Enable junction toggle
- Target: "Project"
2. On **Projects** object:
- Edit "Team Members" field
- Enable junction toggle
- Target: "Person"
### Use It
- Open a Person record → See their Projects directly
- Open a Project record → See team members directly
- Create new connections from either side
## Adding Extra Data to Connections
Since the junction object is a real object, you can add custom fields to store information about the relationship:
- **Role**: "Developer", "Designer", "Manager"
- **Start Date**: When they joined the project
- **Hours Allocated**: Weekly hours on this project
To access this data, navigate to the junction object directly or query it via the API.
## Limitations
- **CSV Import/Export**: Importing many-to-many relations directly is not supported. Import records to the junction object instead.
- **Filters**: Filtering by many-to-many relations may have limited options.
## Related
- [Relation Fields](/user-guide/data-model/capabilities/relation-fields) — relation types explained
- [Create Custom Objects](/user-guide/data-model/how-tos/create-custom-objects) — how to create objects
- [Create Relation Fields](/user-guide/data-model/how-tos/create-relation-fields) — basic relation setup
@@ -32,4 +32,10 @@ export const StyledSettingsOptionCardTitle = styled.div`
export const StyledSettingsOptionCardDescription = styled.div`
color: ${({ theme }) => theme.font.color.tertiary};
font-size: ${({ theme }) => theme.font.size.sm};
a {
position: relative;
z-index: 1;
pointer-events: auto;
}
`;
@@ -41,7 +41,7 @@ const StyledSettingsOptionCardToggleCover = styled.span`
type SettingsOptionCardContentToggleProps = {
Icon?: IconComponent;
title: React.ReactNode;
description?: string;
description?: React.ReactNode;
divider?: boolean;
disabled?: boolean;
advancedMode?: boolean;
@@ -55,6 +55,12 @@ export const settingsDataModelFieldMorphRelationFormSchema = z.object({
),
targetFieldLabel: z.string().min(1),
iconOnDestination: z.string().min(1),
settings: z
.object({
junctionTargetFieldId: z.string().optional(),
})
.catchall(z.unknown())
.optional(),
});
export type SettingsDataModelFieldMorphRelationFormValues = z.infer<
@@ -1,16 +1,19 @@
import { Trans, useLingui } from '@lingui/react/macro';
import { useFormContext } from 'react-hook-form';
import { useRecoilValue } from 'recoil';
import { DOCUMENTATION_PATHS } from 'twenty-shared/constants';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconLink } from 'twenty-ui/display';
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
import { useObjectMetadataItems } from '@/object-metadata/hooks/useObjectMetadataItems';
import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsOptions/SettingsOptionCardContentSelect';
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
import { getDocumentationUrl } from '@/support/utils/getDocumentationUrl';
import { Select } from '@/ui/input/components/Select';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { useLingui } from '@lingui/react/macro';
import { useRecoilValue } from 'recoil';
import { FieldMetadataType } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import { IconLink } from 'twenty-ui/display';
import { RelationType } from '~/generated-metadata/graphql';
import { type SettingsDataModelFieldEditFormValues } from '~/pages/settings/data-model/SettingsObjectFieldEdit';
@@ -26,6 +29,12 @@ export const SettingsDataModelFieldRelationJunctionForm = ({
useFormContext<SettingsDataModelFieldEditFormValues>();
const isAdvancedModeEnabled = useRecoilValue(isAdvancedModeEnabledState);
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const documentationUrl = getDocumentationUrl({
locale: currentWorkspaceMember?.locale,
path: DOCUMENTATION_PATHS.USER_GUIDE_DATA_MODEL_HOW_TOS_CREATE_MANY_TO_MANY_RELATIONS,
});
const { objectMetadataItem: sourceObjectMetadataItem } =
useObjectMetadataItem({ objectNameSingular });
@@ -34,7 +43,8 @@ export const SettingsDataModelFieldRelationJunctionForm = ({
const relationType = watch('relationType') ?? RelationType.ONE_TO_MANY;
const targetObjectIds = watch('morphRelationObjectMetadataIds') ?? [];
const junctionTargetFieldId = watch('settings.junctionTargetFieldId');
const currentSettings = watch('settings');
const junctionTargetFieldId = currentSettings?.junctionTargetFieldId;
// Only applies to ONE_TO_MANY with single target
if (
@@ -112,23 +122,40 @@ export const SettingsDataModelFieldRelationJunctionForm = ({
const handleJunctionToggle = (checked: boolean) => {
if (checked && junctionFieldOptions.length > 0) {
setValue(
'settings.junctionTargetFieldId',
junctionFieldOptions[0].value,
'settings',
{
...currentSettings,
junctionTargetFieldId: junctionFieldOptions[0].value,
},
{
shouldDirty: true,
},
);
} else {
setValue('settings.junctionTargetFieldId', undefined, {
shouldDirty: true,
});
setValue(
'settings',
{
...currentSettings,
junctionTargetFieldId: undefined,
},
{
shouldDirty: true,
},
);
}
};
const handleSelectionChange = (selectedValue: string) => {
setValue('settings.junctionTargetFieldId', selectedValue, {
shouldDirty: true,
});
setValue(
'settings',
{
...currentSettings,
junctionTargetFieldId: selectedValue,
},
{
shouldDirty: true,
},
);
};
return (
@@ -136,7 +163,19 @@ export const SettingsDataModelFieldRelationJunctionForm = ({
<SettingsOptionCardContentToggle
Icon={IconLink}
title={t`This is a relation to a Junction Object`}
description={t`Will show linked records directly instead of intermediate junction record`}
description={
<Trans>
Build many-to-many relations.{' '}
<a
href={documentationUrl}
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: 'underline', color: 'inherit' }}
>
Learn more
</a>
</Trans>
}
checked={isJunctionConfigEnabled}
onChange={handleJunctionToggle}
divider={isJunctionConfigEnabled}
@@ -1,15 +1,17 @@
const DOCUMENTATION_BASE_URL = 'https://docs.twenty.com';
const DOCUMENTATION_PATH = '/user-guide/introduction';
// Locales that have documentation translations available
const SUPPORTED_DOC_LOCALES = ['fr', 'pt', 'de', 'es', 'it', 'ja', 'ko', 'zh'];
import {
DOCUMENTATION_BASE_URL,
DOCUMENTATION_DEFAULT_LANGUAGE,
DOCUMENTATION_DEFAULT_PATH,
DOCUMENTATION_SUPPORTED_LANGUAGES,
type DocumentationPath,
} from 'twenty-shared/constants';
export const getDocumentationUrl = ({
locale,
path = DOCUMENTATION_PATH,
path = DOCUMENTATION_DEFAULT_PATH,
}: {
locale?: string | null;
path?: string;
path?: DocumentationPath | string;
}): string => {
if (!locale) {
return `${DOCUMENTATION_BASE_URL}${path}`;
@@ -18,7 +20,16 @@ export const getDocumentationUrl = ({
// Extract language code from locale (e.g., 'fr' from 'fr-FR')
const langCode = locale.split('-')[0].toLowerCase();
if (SUPPORTED_DOC_LOCALES.includes(langCode)) {
// English content is served at root path (no /l/en/ prefix)
if (langCode === DOCUMENTATION_DEFAULT_LANGUAGE) {
return `${DOCUMENTATION_BASE_URL}${path}`;
}
const isSupported = DOCUMENTATION_SUPPORTED_LANGUAGES.includes(
langCode as (typeof DOCUMENTATION_SUPPORTED_LANGUAGES)[number],
);
if (isSupported) {
return `${DOCUMENTATION_BASE_URL}/l/${langCode}${path}`;
}
@@ -0,0 +1 @@
export const DOCUMENTATION_BASE_URL = 'https://docs.twenty.com';
@@ -0,0 +1 @@
export const DOCUMENTATION_DEFAULT_LANGUAGE = 'en' as const;
@@ -0,0 +1,4 @@
import { DOCUMENTATION_PATHS } from './DocumentationPaths';
export const DOCUMENTATION_DEFAULT_PATH =
DOCUMENTATION_PATHS.USER_GUIDE_INTRODUCTION;
@@ -0,0 +1,298 @@
/*
* _____ _
*|_ _|_ _____ _ __ | |_ _ _
* | | \ \ /\ / / _ \ '_ \| __| | | | Auto-generated file
* | | \ V V / __/ | | | |_| |_| | DO NOT EDIT - changes will be overwritten
* |_| \_/\_/ \___|_| |_|\__|\__, | Generated by: yarn docs:generate-paths
* |___/
*
* This file is generated from packages/twenty-docs/navigation/base-structure.json
* To add new documentation pages, add them to base-structure.json and run:
* yarn docs:generate-paths
*/
export const DOCUMENTATION_PATHS = {
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_BEST_PRACTICES_SERVER:
'/developers/contribute/capabilities/backend-development/best-practices-server',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_CUSTOM_OBJECTS:
'/developers/contribute/capabilities/backend-development/custom-objects',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_FEATURE_FLAGS:
'/developers/contribute/capabilities/backend-development/feature-flags',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_FOLDER_ARCHITECTURE_SERVER:
'/developers/contribute/capabilities/backend-development/folder-architecture-server',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_QUEUE:
'/developers/contribute/capabilities/backend-development/queue',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_SERVER_COMMANDS:
'/developers/contribute/capabilities/backend-development/server-commands',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BACKEND_DEVELOPMENT_ZAPIER:
'/developers/contribute/capabilities/backend-development/zapier',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_BUG_AND_REQUESTS:
'/developers/contribute/capabilities/bug-and-requests',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_BEST_PRACTICES_FRONT:
'/developers/contribute/capabilities/frontend-development/best-practices-front',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_FOLDER_ARCHITECTURE_FRONT:
'/developers/contribute/capabilities/frontend-development/folder-architecture-front',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_FRONTEND_COMMANDS:
'/developers/contribute/capabilities/frontend-development/frontend-commands',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_HOTKEYS:
'/developers/contribute/capabilities/frontend-development/hotkeys',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_STORYBOOK:
'/developers/contribute/capabilities/frontend-development/storybook',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_STYLE_GUIDE:
'/developers/contribute/capabilities/frontend-development/style-guide',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_FRONTEND_DEVELOPMENT_WORK_WITH_FIGMA:
'/developers/contribute/capabilities/frontend-development/work-with-figma',
DEVELOPERS_CONTRIBUTE_CAPABILITIES_LOCAL_SETUP:
'/developers/contribute/capabilities/local-setup',
DEVELOPERS_CONTRIBUTE_CONTRIBUTE: '/developers/contribute/contribute',
DEVELOPERS_EXTEND_CAPABILITIES_APIS: '/developers/extend/capabilities/apis',
DEVELOPERS_EXTEND_CAPABILITIES_APPS: '/developers/extend/capabilities/apps',
DEVELOPERS_EXTEND_CAPABILITIES_WEBHOOKS:
'/developers/extend/capabilities/webhooks',
DEVELOPERS_EXTEND_EXTEND: '/developers/extend/extend',
DEVELOPERS_INTRODUCTION: '/developers/introduction',
DEVELOPERS_SELF_HOST_CAPABILITIES_CLOUD_PROVIDERS:
'/developers/self-host/capabilities/cloud-providers',
DEVELOPERS_SELF_HOST_CAPABILITIES_DOCKER_COMPOSE:
'/developers/self-host/capabilities/docker-compose',
DEVELOPERS_SELF_HOST_CAPABILITIES_SETUP:
'/developers/self-host/capabilities/setup',
DEVELOPERS_SELF_HOST_CAPABILITIES_TROUBLESHOOTING:
'/developers/self-host/capabilities/troubleshooting',
DEVELOPERS_SELF_HOST_CAPABILITIES_UPGRADE_GUIDE:
'/developers/self-host/capabilities/upgrade-guide',
DEVELOPERS_SELF_HOST_SELF_HOST: '/developers/self-host/self-host',
TWENTY_UI_DISPLAY_APP_TOOLTIP: '/twenty-ui/display/app-tooltip',
TWENTY_UI_DISPLAY_CHECKMARK: '/twenty-ui/display/checkmark',
TWENTY_UI_DISPLAY_CHIP: '/twenty-ui/display/chip',
TWENTY_UI_DISPLAY_ICONS: '/twenty-ui/display/icons',
TWENTY_UI_DISPLAY_SOON_PILL: '/twenty-ui/display/soon-pill',
TWENTY_UI_DISPLAY_TAG: '/twenty-ui/display/tag',
TWENTY_UI_INPUT_BLOCK_EDITOR: '/twenty-ui/input/block-editor',
TWENTY_UI_INPUT_BUTTONS: '/twenty-ui/input/buttons',
TWENTY_UI_INPUT_CHECKBOX: '/twenty-ui/input/checkbox',
TWENTY_UI_INPUT_COLOR_SCHEME: '/twenty-ui/input/color-scheme',
TWENTY_UI_INPUT_ICON_PICKER: '/twenty-ui/input/icon-picker',
TWENTY_UI_INPUT_IMAGE_INPUT: '/twenty-ui/input/image-input',
TWENTY_UI_INPUT_RADIO: '/twenty-ui/input/radio',
TWENTY_UI_INPUT_SELECT: '/twenty-ui/input/select',
TWENTY_UI_INPUT_TEXT: '/twenty-ui/input/text',
TWENTY_UI_INPUT_TOGGLE: '/twenty-ui/input/toggle',
TWENTY_UI_INTRODUCTION: '/twenty-ui/introduction',
TWENTY_UI_NAVIGATION: '/twenty-ui/navigation',
TWENTY_UI_NAVIGATION_BREADCRUMB: '/twenty-ui/navigation/breadcrumb',
TWENTY_UI_NAVIGATION_LINKS: '/twenty-ui/navigation/links',
TWENTY_UI_NAVIGATION_MENU_ITEM: '/twenty-ui/navigation/menu-item',
TWENTY_UI_NAVIGATION_NAVIGATION_BAR: '/twenty-ui/navigation/navigation-bar',
TWENTY_UI_NAVIGATION_STEP_BAR: '/twenty-ui/navigation/step-bar',
TWENTY_UI_PROGRESS_BAR: '/twenty-ui/progress-bar',
USER_GUIDE_AI_CAPABILITIES_AI_AGENTS: '/user-guide/ai/capabilities/ai-agents',
USER_GUIDE_AI_CAPABILITIES_AI_CHATBOT:
'/user-guide/ai/capabilities/ai-chatbot',
USER_GUIDE_AI_CAPABILITIES_PERMISSIONS_ACCESS_CONTROL:
'/user-guide/ai/capabilities/permissions-access-control',
USER_GUIDE_AI_HOW_TOS_AI_FAQ: '/user-guide/ai/how-tos/ai-faq',
USER_GUIDE_AI_OVERVIEW: '/user-guide/ai/overview',
USER_GUIDE_BILLING_CAPABILITIES_PRICING_PLANS:
'/user-guide/billing/capabilities/pricing-plans',
USER_GUIDE_BILLING_CAPABILITIES_WORKFLOW_CREDITS:
'/user-guide/billing/capabilities/workflow-credits',
USER_GUIDE_BILLING_HOW_TOS_BILLING_FAQ:
'/user-guide/billing/how-tos/billing-faq',
USER_GUIDE_BILLING_OVERVIEW: '/user-guide/billing/overview',
USER_GUIDE_CALENDAR_EMAILS_CAPABILITIES_CALENDAR:
'/user-guide/calendar-emails/capabilities/calendar',
USER_GUIDE_CALENDAR_EMAILS_CAPABILITIES_MAILBOX:
'/user-guide/calendar-emails/capabilities/mailbox',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_CAN_I_BOOK_MEETINGS_FROM_TWENTY:
'/user-guide/calendar-emails/how-tos/can-i-book-meetings-from-twenty',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_CAN_I_SEND_EMAILS_FROM_TWENTY:
'/user-guide/calendar-emails/how-tos/can-i-send-emails-from-twenty',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_CAN_I_TRACK_EMAIL_ACTIVITY_ON_ALL_OBJECTS:
'/user-guide/calendar-emails/how-tos/can-i-track-email-activity-on-all-objects',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_CONNECT_SEVERAL_MAILBOXES_PER_USER:
'/user-guide/calendar-emails/how-tos/connect-several-mailboxes-per-user',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_I_DONT_SEE_EMAILS_ON_RECORDS:
'/user-guide/calendar-emails/how-tos/i-dont-see-emails-on-records',
USER_GUIDE_CALENDAR_EMAILS_HOW_TOS_LIMIT_EMAILS_IMPORTED:
'/user-guide/calendar-emails/how-tos/limit-emails-imported',
USER_GUIDE_CALENDAR_EMAILS_OVERVIEW: '/user-guide/calendar-emails/overview',
USER_GUIDE_DASHBOARDS_CAPABILITIES_CHART_SETTINGS:
'/user-guide/dashboards/capabilities/chart-settings',
USER_GUIDE_DASHBOARDS_CAPABILITIES_DASHBOARDS:
'/user-guide/dashboards/capabilities/dashboards',
USER_GUIDE_DASHBOARDS_CAPABILITIES_WIDGETS:
'/user-guide/dashboards/capabilities/widgets',
USER_GUIDE_DASHBOARDS_HOW_TOS_DASHBOARDS_FAQ:
'/user-guide/dashboards/how-tos/dashboards-faq',
USER_GUIDE_DASHBOARDS_HOW_TOS_WIDGET_FAQ:
'/user-guide/dashboards/how-tos/widget-faq',
USER_GUIDE_DASHBOARDS_OVERVIEW: '/user-guide/dashboards/overview',
USER_GUIDE_DATA_MIGRATION_CAPABILITIES_ERROR_HANDLING:
'/user-guide/data-migration/capabilities/error-handling',
USER_GUIDE_DATA_MIGRATION_CAPABILITIES_FIELD_MAPPING:
'/user-guide/data-migration/capabilities/field-mapping',
USER_GUIDE_DATA_MIGRATION_CAPABILITIES_FILE_FORMATS:
'/user-guide/data-migration/capabilities/file-formats',
USER_GUIDE_DATA_MIGRATION_CAPABILITIES_IMPORT_RELATIONS:
'/user-guide/data-migration/capabilities/import-relations',
USER_GUIDE_DATA_MIGRATION_CAPABILITIES_UNIQUENESS_CONSTRAINTS:
'/user-guide/data-migration/capabilities/uniqueness-constraints',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_EXPORT_YOUR_DATA:
'/user-guide/data-migration/how-tos/export-your-data',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_FIX_IMPORT_ERRORS:
'/user-guide/data-migration/how-tos/fix-import-errors',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_IMPORT_COMPANIES_VIA_CSV:
'/user-guide/data-migration/how-tos/import-companies-via-csv',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_IMPORT_CONTACTS_VIA_CSV:
'/user-guide/data-migration/how-tos/import-contacts-via-csv',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_IMPORT_DATA_VIA_API:
'/user-guide/data-migration/how-tos/import-data-via-api',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_IMPORT_RELATIONS_BETWEEN_OBJECTS_VIA_CSV:
'/user-guide/data-migration/how-tos/import-relations-between-objects-via-csv',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_MIGRATING_FROM_OTHER_CRMS:
'/user-guide/data-migration/how-tos/migrating-from-other-crms',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_MIGRATING_FROM_SELF_HOSTED_TO_CLOUD:
'/user-guide/data-migration/how-tos/migrating-from-self-hosted-to-cloud',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_PREPARE_YOUR_CSV_FILES:
'/user-guide/data-migration/how-tos/prepare-your-csv-files',
USER_GUIDE_DATA_MIGRATION_HOW_TOS_UPDATE_EXISTING_RECORDS_VIA_IMPORT:
'/user-guide/data-migration/how-tos/update-existing-records-via-import',
USER_GUIDE_DATA_MIGRATION_OVERVIEW: '/user-guide/data-migration/overview',
USER_GUIDE_DATA_MODEL_CAPABILITIES_FIELDS:
'/user-guide/data-model/capabilities/fields',
USER_GUIDE_DATA_MODEL_CAPABILITIES_OBJECTS:
'/user-guide/data-model/capabilities/objects',
USER_GUIDE_DATA_MODEL_CAPABILITIES_RELATION_FIELDS:
'/user-guide/data-model/capabilities/relation-fields',
USER_GUIDE_DATA_MODEL_HOW_TOS_CREATE_CUSTOM_FIELDS:
'/user-guide/data-model/how-tos/create-custom-fields',
USER_GUIDE_DATA_MODEL_HOW_TOS_CREATE_CUSTOM_OBJECTS:
'/user-guide/data-model/how-tos/create-custom-objects',
USER_GUIDE_DATA_MODEL_HOW_TOS_CREATE_MANY_TO_MANY_RELATIONS:
'/user-guide/data-model/how-tos/create-many-to-many-relations',
USER_GUIDE_DATA_MODEL_HOW_TOS_CREATE_RELATION_FIELDS:
'/user-guide/data-model/how-tos/create-relation-fields',
USER_GUIDE_DATA_MODEL_HOW_TOS_CUSTOMIZE_YOUR_DATA_MODEL:
'/user-guide/data-model/how-tos/customize-your-data-model',
USER_GUIDE_DATA_MODEL_HOW_TOS_DATA_MODEL_FAQ:
'/user-guide/data-model/how-tos/data-model-faq',
USER_GUIDE_DATA_MODEL_OVERVIEW: '/user-guide/data-model/overview',
USER_GUIDE_GETTING_STARTED_CAPABILITIES_GLOSSARY:
'/user-guide/getting-started/capabilities/glossary',
USER_GUIDE_GETTING_STARTED_CAPABILITIES_IMPLEMENTATION_SERVICES:
'/user-guide/getting-started/capabilities/implementation-services',
USER_GUIDE_GETTING_STARTED_CAPABILITIES_WHAT_IS_TWENTY:
'/user-guide/getting-started/capabilities/what-is-twenty',
USER_GUIDE_GETTING_STARTED_HOW_TOS_CONFIGURE_YOUR_WORKSPACE:
'/user-guide/getting-started/how-tos/configure-your-workspace',
USER_GUIDE_GETTING_STARTED_HOW_TOS_CREATE_WORKSPACE:
'/user-guide/getting-started/how-tos/create-workspace',
USER_GUIDE_GETTING_STARTED_HOW_TOS_NAVIGATE_AROUND_TWENTY:
'/user-guide/getting-started/how-tos/navigate-around-twenty',
USER_GUIDE_INTRODUCTION: '/user-guide/introduction',
USER_GUIDE_PERMISSIONS_ACCESS_CAPABILITIES_PERMISSIONS:
'/user-guide/permissions-access/capabilities/permissions',
USER_GUIDE_PERMISSIONS_ACCESS_CAPABILITIES_SSO_CONFIGURATION:
'/user-guide/permissions-access/capabilities/sso-configuration',
USER_GUIDE_PERMISSIONS_ACCESS_HOW_TOS_PERMISSIONS_FAQ:
'/user-guide/permissions-access/how-tos/permissions-faq',
USER_GUIDE_PERMISSIONS_ACCESS_OVERVIEW:
'/user-guide/permissions-access/overview',
USER_GUIDE_SETTINGS_CAPABILITIES_DOMAINS_SETTINGS:
'/user-guide/settings/capabilities/domains-settings',
USER_GUIDE_SETTINGS_CAPABILITIES_EXPERIENCE_SETTINGS:
'/user-guide/settings/capabilities/experience-settings',
USER_GUIDE_SETTINGS_CAPABILITIES_MEMBER_MANAGEMENT:
'/user-guide/settings/capabilities/member-management',
USER_GUIDE_SETTINGS_CAPABILITIES_PROFILE_SETTINGS:
'/user-guide/settings/capabilities/profile-settings',
USER_GUIDE_SETTINGS_CAPABILITIES_UPDATES_SETTINGS:
'/user-guide/settings/capabilities/updates-settings',
USER_GUIDE_SETTINGS_CAPABILITIES_WORKSPACE_SETTINGS:
'/user-guide/settings/capabilities/workspace-settings',
USER_GUIDE_SETTINGS_HOW_TOS_SETTINGS_FAQ:
'/user-guide/settings/how-tos/settings-faq',
USER_GUIDE_SETTINGS_OVERVIEW: '/user-guide/settings/overview',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_CALENDAR_VIEW:
'/user-guide/views-pipelines/capabilities/calendar-view',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_FIELDS_AND_COLUMNS:
'/user-guide/views-pipelines/capabilities/fields-and-columns',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_FILTERS_AND_SORTING:
'/user-guide/views-pipelines/capabilities/filters-and-sorting',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_KANBAN_VIEWS:
'/user-guide/views-pipelines/capabilities/kanban-views',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_TABLE_VIEWS:
'/user-guide/views-pipelines/capabilities/table-views',
USER_GUIDE_VIEWS_PIPELINES_CAPABILITIES_VIEW_SETTINGS:
'/user-guide/views-pipelines/capabilities/view-settings',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_CREATE_A_CALENDAR_VIEW_FOR_TASKS_DUE:
'/user-guide/views-pipelines/how-tos/create-a-calendar-view-for-tasks-due',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_CREATE_A_KANBAN_VIEW_FOR_PROJECTS:
'/user-guide/views-pipelines/how-tos/create-a-kanban-view-for-projects',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_CREATE_A_TABLE_VIEW_WITH_GROUPING:
'/user-guide/views-pipelines/how-tos/create-a-table-view-with-grouping',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_RESTRICT_ACCESS_TO_YOUR_VIEW:
'/user-guide/views-pipelines/how-tos/restrict-access-to-your-view',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_SET_UP_A_SALES_PIPELINE:
'/user-guide/views-pipelines/how-tos/set-up-a-sales-pipeline',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_SHOW_EXPECTED_AMOUNT_IN_PIPELINE:
'/user-guide/views-pipelines/how-tos/show-expected-amount-in-pipeline',
USER_GUIDE_VIEWS_PIPELINES_HOW_TOS_TRACK_TIME_IN_STAGE:
'/user-guide/views-pipelines/how-tos/track-time-in-stage',
USER_GUIDE_VIEWS_PIPELINES_OVERVIEW: '/user-guide/views-pipelines/overview',
USER_GUIDE_WORKFLOWS_CAPABILITIES_SEND_EMAILS_FROM_WORKFLOWS:
'/user-guide/workflows/capabilities/send-emails-from-workflows',
USER_GUIDE_WORKFLOWS_CAPABILITIES_USE_BRANCHES_IN_WORKFLOWS:
'/user-guide/workflows/capabilities/use-branches-in-workflows',
USER_GUIDE_WORKFLOWS_CAPABILITIES_USE_ITERATOR:
'/user-guide/workflows/capabilities/use-iterator',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_ACTIONS:
'/user-guide/workflows/capabilities/workflow-actions',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_BRANCHES:
'/user-guide/workflows/capabilities/workflow-branches',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_CREDITS:
'/user-guide/workflows/capabilities/workflow-credits',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_RUNS:
'/user-guide/workflows/capabilities/workflow-runs',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_TRIGGERS:
'/user-guide/workflows/capabilities/workflow-triggers',
USER_GUIDE_WORKFLOWS_CAPABILITIES_WORKFLOW_VERSIONS:
'/user-guide/workflows/capabilities/workflow-versions',
USER_GUIDE_WORKFLOWS_HOW_TOS_ADVANCED_CONFIGURATIONS_HANDLE_ARRAYS_IN_CODE_ACTIONS:
'/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions',
USER_GUIDE_WORKFLOWS_HOW_TOS_CONNECT_TO_OTHER_TOOLS_BRING_PRODUCT_DATA_IN_TWENTY:
'/user-guide/workflows/how-tos/connect-to-other-tools/bring-product-data-in-twenty',
USER_GUIDE_WORKFLOWS_HOW_TOS_CONNECT_TO_OTHER_TOOLS_BRING_TYPEFORM_SUBMISSIONS_IN_TWENTY:
'/user-guide/workflows/how-tos/connect-to-other-tools/bring-typeform-submissions-in-twenty',
USER_GUIDE_WORKFLOWS_HOW_TOS_CONNECT_TO_OTHER_TOOLS_GENERATE_PDF_FROM_TWENTY:
'/user-guide/workflows/how-tos/connect-to-other-tools/generate-pdf-from-twenty',
USER_GUIDE_WORKFLOWS_HOW_TOS_CONNECT_TO_OTHER_TOOLS_GENERATE_QUOTE_OR_INVOICE_FROM_TWENTY:
'/user-guide/workflows/how-tos/connect-to-other-tools/generate-quote-or-invoice-from-twenty',
USER_GUIDE_WORKFLOWS_HOW_TOS_CONNECT_TO_OTHER_TOOLS_SET_UP_A_WEBHOOK_TRIGGER:
'/user-guide/workflows/how-tos/connect-to-other-tools/set-up-a-webhook-trigger',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_CLOSED_WON_AUTOMATIONS:
'/user-guide/workflows/how-tos/crm-automations/closed-won-automations',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_DETECT_STALE_OPPORTUNITIES:
'/user-guide/workflows/how-tos/crm-automations/detect-stale-opportunities',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_DISPLAY_NUMBER_OF_EMAILS_RECEIVED:
'/user-guide/workflows/how-tos/crm-automations/display-number-of-emails-received',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_DISPLAY_RELATED_RECORD_DATA:
'/user-guide/workflows/how-tos/crm-automations/display-related-record-data',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_FORMULA_FIELDS:
'/user-guide/workflows/how-tos/crm-automations/formula-fields',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_NOTIFY_TEAMMATES_OF_NOTE_TO_REVIEW:
'/user-guide/workflows/how-tos/crm-automations/notify-teammates-of-note-to-review',
USER_GUIDE_WORKFLOWS_HOW_TOS_CRM_AUTOMATIONS_SEND_EMAIL_ALERTS_WITH_TASKS_DUE:
'/user-guide/workflows/how-tos/crm-automations/send-email-alerts-with-tasks-due',
USER_GUIDE_WORKFLOWS_HOW_TOS_NEED_MORE_HELP_PROFESSIONAL_SERVICES:
'/user-guide/workflows/how-tos/need-more-help/professional-services',
USER_GUIDE_WORKFLOWS_HOW_TOS_NEED_MORE_HELP_WORKFLOW_TROUBLESHOOTING:
'/user-guide/workflows/how-tos/need-more-help/workflow-troubleshooting',
USER_GUIDE_WORKFLOWS_HOW_TOS_NEED_MORE_HELP_WORKFLOWS_FAQ:
'/user-guide/workflows/how-tos/need-more-help/workflows-faq',
USER_GUIDE_WORKFLOWS_OVERVIEW: '/user-guide/workflows/overview',
} as const;
export type DocumentationPath =
(typeof DOCUMENTATION_PATHS)[keyof typeof DOCUMENTATION_PATHS];
@@ -0,0 +1,21 @@
import { DOCUMENTATION_DEFAULT_LANGUAGE } from './DocumentationDefaultLanguage';
export const DOCUMENTATION_SUPPORTED_LANGUAGES = [
DOCUMENTATION_DEFAULT_LANGUAGE,
'fr',
'ar',
'cs',
'de',
'es',
'it',
'ja',
'ko',
'pt',
'ro',
'ru',
'tr',
'zh',
] as const;
export type DocumentationSupportedLanguage =
(typeof DOCUMENTATION_SUPPORTED_LANGUAGES)[number];
@@ -14,6 +14,13 @@ export { CURRENCY_CODE_LABELS } from './CurrencyCodeLabels';
export { DATE_TYPE_FORMAT } from './DateTypeFormat';
export { DEFAULT_NUMBER_OF_GROUPS_LIMIT } from './DefaultNumberOfGroupsLimit';
export { DEFAULT_RELATIVE_DATE_FILTER_VALUE } from './DefaultRelativeDateFilterValue';
export { DOCUMENTATION_BASE_URL } from './DocumentationBaseUrl';
export { DOCUMENTATION_DEFAULT_LANGUAGE } from './DocumentationDefaultLanguage';
export { DOCUMENTATION_DEFAULT_PATH } from './DocumentationDefaultPath';
export type { DocumentationPath } from './DocumentationPaths';
export { DOCUMENTATION_PATHS } from './DocumentationPaths';
export type { DocumentationSupportedLanguage } from './DocumentationSupportedLanguages';
export { DOCUMENTATION_SUPPORTED_LANGUAGES } from './DocumentationSupportedLanguages';
export { FIELD_FOR_TOTAL_COUNT_AGGREGATE_OPERATION } from './FieldForTotalCountAggregateOperation';
export { MAX_OPTIONS_TO_DISPLAY } from './FieldMetadataMaxOptionsToDisplay';
export { FIELD_RESTRICTED_ADDITIONAL_PERMISSIONS_REQUIRED } from './FieldRestrictedAdditionalPermissionsRequired';
@@ -1,4 +1,7 @@
import { APP_LOCALES, type AppLocale } from '@/translations/constants/AppLocales';
import {
APP_LOCALES,
type AppLocale,
} from '@/translations/constants/AppLocales';
export const isValidLocale = (value: string | null): value is AppLocale =>
value !== null && value in APP_LOCALES;
+1
View File
@@ -57322,6 +57322,7 @@ __metadata:
resolution: "twenty-docs@workspace:packages/twenty-docs"
dependencies:
mintlify: "npm:latest"
twenty-shared: "workspace:*"
languageName: unknown
linkType: soft