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
@@ -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}`;
}