Merge branch 'main' into claude/billing-events-analytics-cnHak

This commit is contained in:
Félix Malfait
2026-03-17 17:06:35 +01:00
committed by GitHub
214 changed files with 2931 additions and 1189 deletions
+1 -1
View File
@@ -100,7 +100,7 @@ jobs:
create-twenty-app --version
mkdir -p /tmp/e2e-test-workspace
cd /tmp/e2e-test-workspace
create-twenty-app test-app --exhaustive --display-name "Test App" --description "E2E test app"
create-twenty-app test-app --exhaustive --display-name "Test App" --description "E2E test app" --skip-local-instance
- name: Install scaffolded app dependencies
run: |
+6
View File
@@ -27,6 +27,10 @@ const program = new Command(packageJson.name)
'--description <description>',
'Application description (skips prompt)',
)
.option(
'--skip-local-instance',
'Skip the local Twenty instance setup prompt',
)
.helpOption('-h, --help', 'Display this help message.')
.action(
async (
@@ -37,6 +41,7 @@ const program = new Command(packageJson.name)
name?: string;
displayName?: string;
description?: string;
skipLocalInstance?: boolean;
},
) => {
const modeFlags = [options?.exhaustive, options?.minimal].filter(Boolean);
@@ -72,6 +77,7 @@ const program = new Command(packageJson.name)
name: options?.name,
displayName: options?.displayName,
description: options?.description,
skipLocalInstance: options?.skipLocalInstance,
});
},
);
@@ -1,11 +1,16 @@
import { copyBaseApplicationProject } from '@/utils/app-template';
import { convertToLabel } from '@/utils/convert-to-label';
import { install } from '@/utils/install';
import {
type LocalInstanceResult,
setupLocalInstance,
} from '@/utils/setup-local-instance';
import { tryGitInit } from '@/utils/try-git-init';
import chalk from 'chalk';
import * as fs from 'fs-extra';
import inquirer from 'inquirer';
import kebabCase from 'lodash.kebabcase';
import { execSync } from 'node:child_process';
import * as path from 'path';
import { isDefined } from 'twenty-shared/utils';
@@ -22,6 +27,7 @@ type CreateAppOptions = {
name?: string;
displayName?: string;
description?: string;
skipLocalInstance?: boolean;
};
export class CreateAppCommand {
@@ -52,7 +58,29 @@ export class CreateAppCommand {
await tryGitInit(appDirectory);
this.logSuccess(appDirectory);
let localResult: LocalInstanceResult = { running: false };
if (!options.skipLocalInstance) {
const { needsLocalInstance } = await inquirer.prompt([
{
type: 'confirm',
name: 'needsLocalInstance',
message:
'Do you need a local instance of Twenty? Recommended if you not have one already.',
default: true,
},
]);
if (needsLocalInstance) {
localResult = await setupLocalInstance();
}
if (isDefined(localResult.apiKey)) {
this.runAuthLogin(appDirectory, localResult.apiKey);
}
}
this.logSuccess(appDirectory, localResult);
} catch (error) {
console.error(
chalk.red('Initialization failed:'),
@@ -179,20 +207,49 @@ export class CreateAppCommand {
console.log('');
}
private logSuccess(appDirectory: string): void {
private runAuthLogin(appDirectory: string, apiKey: string): void {
try {
execSync(
`yarn twenty auth:login --api-key "${apiKey}" --api-url http://localhost:3000`,
{ cwd: appDirectory, stdio: 'inherit' },
);
console.log(chalk.green('✅ Authenticated with local Twenty instance.'));
} catch {
console.log(
chalk.yellow(
'⚠️ Auto auth:login failed. Run `yarn twenty auth:login` manually.',
),
);
}
}
private logSuccess(
appDirectory: string,
localResult: LocalInstanceResult,
): void {
const dirName = appDirectory.split('/').reverse()[0] ?? '';
console.log(chalk.green('✅ Application created!'));
console.log('');
console.log(chalk.blue('Next steps:'));
console.log(chalk.gray(` cd ${dirName}`));
console.log(
chalk.gray(
' yarn twenty remote add --local # Authenticate with Twenty',
),
);
console.log(
chalk.gray(' yarn twenty dev # Start dev mode'),
);
if (localResult.apiKey) {
console.log(chalk.gray(' yarn twenty app:dev # Start dev mode'));
} else if (localResult.running) {
console.log(
chalk.gray(
' yarn twenty remote add --local # Authenticate with Twenty',
),
);
console.log(chalk.gray(' yarn twenty app:dev # Start dev mode'));
} else {
console.log(
chalk.gray(
' yarn twenty remote add --local # Authenticate with Twenty',
),
);
console.log(chalk.gray(' yarn twenty app:dev # Start dev mode'));
}
}
}
@@ -0,0 +1,194 @@
import chalk from 'chalk';
import inquirer from 'inquirer';
import { execSync } from 'node:child_process';
import { isDefined } from 'twenty-shared/utils';
const INSTALL_SCRIPT_URL =
'https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/scripts/install.sh';
const SERVER_CONTAINER = 'twenty-server-1';
const DB_CONTAINER = 'twenty-db-1';
const isDockerAvailable = (): boolean => {
try {
execSync('docker compose version', { stdio: 'ignore' });
return true;
} catch {
return false;
}
};
const isDockerRunning = (): boolean => {
try {
execSync('docker info', { stdio: 'ignore' });
return true;
} catch {
return false;
}
};
const isTwentyServerRunning = async (): Promise<boolean> => {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3000);
const response = await fetch('http://localhost:3000/healthz', {
signal: controller.signal,
});
clearTimeout(timeoutId);
const body = await response.json();
return body.status === 'ok';
} catch {
return false;
}
};
const getActiveWorkspaceId = (): string | null => {
try {
const result = execSync(
`docker exec ${DB_CONTAINER} psql -U postgres -d default -t -c "SELECT id FROM core.workspace WHERE \\"activationStatus\\" = 'ACTIVE' LIMIT 1"`,
{ encoding: 'utf-8' },
).trim();
return result || null;
} catch {
return null;
}
};
const generateApiKeyToken = (workspaceId: string): string | null => {
try {
const output = execSync(
`docker exec -e NODE_ENV=development ${SERVER_CONTAINER} yarn command:prod workspace:generate-api-key -w ${workspaceId}`,
{ encoding: 'utf-8' },
);
const TOKEN_PREFIX = 'TOKEN:';
const tokenLine = output
.trim()
.split('\n')
.find((line) => line.includes(TOKEN_PREFIX));
if (!tokenLine) {
return null;
}
const tokenStartIndex =
tokenLine.indexOf(TOKEN_PREFIX) + TOKEN_PREFIX.length;
return tokenLine.slice(tokenStartIndex).trim();
} catch {
return null;
}
};
export type LocalInstanceResult = {
running: boolean;
apiKey?: string;
};
export const setupLocalInstance = async (): Promise<LocalInstanceResult> => {
console.log('');
console.log(chalk.blue('🐳 Setting up local Twenty instance...'));
if (await isTwentyServerRunning()) {
console.log(
chalk.green('✅ Twenty server is already running on localhost:3000.'),
);
} else {
if (!isDockerAvailable()) {
console.log(
chalk.yellow(
'⚠️ Docker Compose is not installed. Please install Docker first.',
),
);
console.log(chalk.gray(' See https://docs.docker.com/get-docker/'));
return { running: false };
}
if (!isDockerRunning()) {
console.log(
chalk.yellow(
'⚠️ Docker is not running. Please start Docker and try again.',
),
);
return { running: false };
}
try {
execSync(`bash <(curl -sL ${INSTALL_SCRIPT_URL})`, {
stdio: 'inherit',
shell: '/bin/bash',
});
} catch {
console.log(
chalk.yellow('⚠️ Local instance setup did not complete successfully.'),
);
return { running: false };
}
}
console.log('');
console.log(
chalk.blue(
'👉 Please create your workspace in the browser before continuing.',
),
);
const { workspaceCreated } = await inquirer.prompt([
{
type: 'confirm',
name: 'workspaceCreated',
message: 'Have you finished creating your workspace?',
default: true,
},
]);
if (!workspaceCreated) {
console.log(
chalk.yellow(
'⚠️ Skipping API key generation. Run `yarn twenty remote add --local` manually after creating your workspace.',
),
);
return { running: true };
}
console.log(chalk.blue('🔑 Generating API key for your workspace...'));
const workspaceId = getActiveWorkspaceId();
if (!isDefined(workspaceId)) {
console.log(
chalk.yellow(
'⚠️ No active workspace found. Make sure you completed the signup flow, then run `yarn twenty auth:login` manually.',
),
);
return { running: true };
}
const apiKey = generateApiKeyToken(workspaceId);
if (!isDefined(apiKey)) {
console.log(
chalk.yellow(
'⚠️ Could not generate API key. Run `yarn twenty auth:login` manually.',
),
);
return { running: true };
}
console.log(chalk.green('✅ API key generated for your workspace.'));
return { running: true, apiKey };
};
File diff suppressed because one or more lines are too long
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kan nie skandeer nie? Kopieer die"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Kanselleer"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Kanselleer uitgawe"
@@ -6187,9 +6185,9 @@ msgstr "Kon nie prent verwyder nie"
msgid "Failed to retry jobs. Please try again later."
msgstr "Kon nie werke herprobeer nie. Probeer asseblief later weer."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Uitleg"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Saterdag"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Stoor"
@@ -11597,11 +11594,6 @@ msgstr "Stoor as nuwe aansig"
msgid "Save Dashboard"
msgstr "Stoor Paneelbord"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Sommige"
msgid "Some folders"
msgstr "Sommige vouers"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "هل لا يمكنك المسح؟ انسخ الـ"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "إلغاء"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "إلغاء التعديل"
@@ -6187,9 +6185,9 @@ msgstr "فشل في إزالة الصورة"
msgid "Failed to retry jobs. Please try again later."
msgstr "فشل في إعادة محاولة الوظائف. يُرجى المحاولة مرة أخرى لاحقًا."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "التخطيط"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "السبت"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "حفظ"
@@ -11597,11 +11594,6 @@ msgstr "احفظ كعرض جديد"
msgid "Save Dashboard"
msgstr "حفظ لوحة القيادة"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "بعض"
msgid "Some folders"
msgstr "بعض المجلدات"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "No pots escanejar? Copia la"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Cancel·la"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Cancel·la l'edició"
@@ -6187,9 +6185,9 @@ msgstr "No s'ha pogut eliminar la imatge"
msgid "Failed to retry jobs. Please try again later."
msgstr "Error en reintentar feines. Torneu-ho a provar més tard."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Disseny"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Dissabte"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Desa"
@@ -11597,11 +11594,6 @@ msgstr "Desa com a nova vista"
msgid "Save Dashboard"
msgstr "Desa el quadre de comandament"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Alguns"
msgid "Some folders"
msgstr "Algunes carpetes"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Nemůžete skenovat? Zkopírujte"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Storno"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Zrušit edici"
@@ -6187,9 +6185,9 @@ msgstr "Nepodařilo se odstranit obrázek"
msgid "Failed to retry jobs. Please try again later."
msgstr "Nepodařilo se znovu spustit úlohy. Prosím, zkuste to znovu později."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Rozvržení"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sobota"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Uložit"
@@ -11597,11 +11594,6 @@ msgstr "Uložit jako nové zobrazení"
msgid "Save Dashboard"
msgstr "Uložit panel"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Některé"
msgid "Some folders"
msgstr "Některé složky"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kan ikke scanne? Kopier"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Annuller"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Annuller udgave"
@@ -6187,9 +6185,9 @@ msgstr "Kunne ikke fjerne billede"
msgid "Failed to retry jobs. Please try again later."
msgstr "Kunne ikke forsøge jobs igen. Prøv venligst igen senere."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Layout"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Lørdag"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Gem"
@@ -11597,11 +11594,6 @@ msgstr "Gem som ny visning"
msgid "Save Dashboard"
msgstr "Gem dashboard"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Nogle"
msgid "Some folders"
msgstr "Nogle mapper"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kann nicht gescannt werden? Kopieren Sie das"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Abbrechen"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Edition abbrechen"
@@ -6187,9 +6185,9 @@ msgstr "Bild konnte nicht entfernt werden"
msgid "Failed to retry jobs. Please try again later."
msgstr "Aufgaben konnten nicht erneut versucht werden. Bitte versuchen Sie es später noch einmal."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Layout"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Samstag"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Speichern"
@@ -11597,11 +11594,6 @@ msgstr "Als neue Ansicht speichern"
msgid "Save Dashboard"
msgstr "Dashboard speichern"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Einige"
msgid "Some folders"
msgstr "Einige Ordner"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Δεν μπορείτε να σαρώσετε; Αντιγράψτε το
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Ακύρωση"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Ακύρωση Έκδοσης"
@@ -6187,9 +6185,9 @@ msgstr "Αποτυχία αφαίρεσης εικόνας"
msgid "Failed to retry jobs. Please try again later."
msgstr "Αποτυχία επαναποστολής εργασιών. Παρακαλώ δοκιμάστε ξανά αργότερα."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Διάταξη"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Σάββατο"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Αποθήκευση"
@@ -11597,11 +11594,6 @@ msgstr "Αποθήκευση ως νέα προβολή"
msgid "Save Dashboard"
msgstr "Αποθήκευση πίνακα ελέγχου"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12642,6 +12634,11 @@ msgstr "Μερικά"
msgid "Some folders"
msgstr "Ορισμένοι φάκελοι"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+10 -13
View File
@@ -2554,13 +2554,11 @@ msgstr "Can't scan? Copy the"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Cancel"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Cancel Edition"
@@ -6182,10 +6180,10 @@ msgstr "Failed to remove picture"
msgid "Failed to retry jobs. Please try again later."
msgstr "Failed to retry jobs. Please try again later."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
msgstr "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr "Failed to save layout customization"
#. js-lingui-id: y3HIOa
#: src/pages/settings/ai/SettingsAgentForm.tsx
@@ -8016,7 +8014,7 @@ msgid "Layout"
msgstr "Layout"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr "Layout customization"
@@ -11577,7 +11575,6 @@ msgstr "Saturday"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Save"
@@ -11592,11 +11589,6 @@ msgstr "Save as new view"
msgid "Save Dashboard"
msgstr "Save Dashboard"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr "Save Page Layout"
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12635,6 +12627,11 @@ msgstr "Some"
msgid "Some folders"
msgstr "Some folders"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr "Some layout changes could not be saved"
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "¿No puedes escanear? Copia el"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Cancelar"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Cancelar edición"
@@ -6187,9 +6185,9 @@ msgstr "No se pudo eliminar la imagen"
msgid "Failed to retry jobs. Please try again later."
msgstr "No se pudieron reintentar los trabajos. Por favor, inténtelo de nuevo más tarde."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Diseño"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sábado"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Guardar"
@@ -11597,11 +11594,6 @@ msgstr "Guardar como nueva vista"
msgid "Save Dashboard"
msgstr "Guardar Tablero"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Alguno"
msgid "Some folders"
msgstr "Algunas carpetas"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Et voi skannata? Kopioi"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Peruuta"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Peruuta muokkaus"
@@ -6187,9 +6185,9 @@ msgstr "Kuvan poistaminen epäonnistui"
msgid "Failed to retry jobs. Please try again later."
msgstr "Epäonnistui uusien tehtävien yrittämisessä. Yritä myöhemmin uudelleen."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Ulkoasu"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Lauantai"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Tallenna"
@@ -11597,11 +11594,6 @@ msgstr "Tallenna uutena näkymänä"
msgid "Save Dashboard"
msgstr "Tallenna hallintapaneeli"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Jotkut"
msgid "Some folders"
msgstr "Jotkin kansiot"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Impossible de scanner ? Copiez le"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Annuler"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Annuler l'édition"
@@ -6187,9 +6185,9 @@ msgstr "Échec de la suppression de l'image"
msgid "Failed to retry jobs. Please try again later."
msgstr "Échec du réessai des tâches. Veuillez réessayer plus tard."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Disposition"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Samedi"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Enregistrer"
@@ -11597,11 +11594,6 @@ msgstr "Enregistrer comme nouvelle vue"
msgid "Save Dashboard"
msgstr "Enregistrer le tableau de bord"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Certains"
msgid "Some folders"
msgstr "Certains dossiers"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
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
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "לא ניתן לסרוק? העתק את"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "בטל"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "בטל עריכה"
@@ -6187,9 +6185,9 @@ msgstr "הסרת התמונה נכשלה"
msgid "Failed to retry jobs. Please try again later."
msgstr "נכשל בהפעלה מחדש של עבודות. נא לנסות שוב מאוחר יותר."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "פריסה"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "יום שבת"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "שמור"
@@ -11597,11 +11594,6 @@ msgstr "שמור כתצוגה חדשה"
msgid "Save Dashboard"
msgstr "שמור לוח בקרה"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "חלק"
msgid "Some folders"
msgstr "כמה תיקיות"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Nem tud beolvasni? Másolja a"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Mégse"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Szerkesztés törlése"
@@ -6187,9 +6185,9 @@ msgstr "Nem sikerült eltávolítani a képet"
msgid "Failed to retry jobs. Please try again later."
msgstr "Nem sikerült újraindítani az állásokat. Kérjük, próbálja meg újra később."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Elrendezés"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Szombat"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Mentés"
@@ -11597,11 +11594,6 @@ msgstr "Mentés új nézetként"
msgid "Save Dashboard"
msgstr "Irányítópult mentése"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Néhány"
msgid "Some folders"
msgstr "Néhány mappa"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Non puoi scansionare? Copia il"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Annulla"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Annulla Edizione"
@@ -6187,9 +6185,9 @@ msgstr "Impossibile rimuovere l'immagine"
msgid "Failed to retry jobs. Please try again later."
msgstr "Impossibile riprovare i lavori. Per favore riprova più tardi."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Disposizione"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sabato"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Salva"
@@ -11597,11 +11594,6 @@ msgstr "Salva come nuova vista"
msgid "Save Dashboard"
msgstr "Salva Cruscotto"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Alcuni"
msgid "Some folders"
msgstr "Alcune cartelle"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "スキャンできませんか? コピーしてください"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "キャンセル"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "編集をキャンセル"
@@ -6187,9 +6185,9 @@ msgstr "画像を削除できませんでした"
msgid "Failed to retry jobs. Please try again later."
msgstr "ジョブの再試行に失敗しました。後でもう一度お試しください。"
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "レイアウト"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "土曜日"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "保存"
@@ -11597,11 +11594,6 @@ msgstr "新しいビューとして保存"
msgid "Save Dashboard"
msgstr "ダッシュボードを保存"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "一部"
msgid "Some folders"
msgstr "いくつかのフォルダー"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "스캔이 불가능합니까?"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "취소"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "판 취소"
@@ -6187,9 +6185,9 @@ msgstr "사진 제거 실패"
msgid "Failed to retry jobs. Please try again later."
msgstr "작업 재시도 실패. 나중에 다시 시도하세요."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "레이아웃"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "토요일"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "저장"
@@ -11597,11 +11594,6 @@ msgstr "새 보기로 저장"
msgid "Save Dashboard"
msgstr "대시보드 저장"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "일부"
msgid "Some folders"
msgstr "일부 폴더"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kan je niet scannen? Kopieer de"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Annuleren"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Annuleer Bewerken"
@@ -6187,9 +6185,9 @@ msgstr "Het verwijderen van de afbeelding is mislukt"
msgid "Failed to retry jobs. Please try again later."
msgstr "Fout bij het opnieuw proberen van taken. Probeer het later opnieuw."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Lay-out"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Zaterdag"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Opslaan"
@@ -11597,11 +11594,6 @@ msgstr "Opslaan als nieuwe weergave"
msgid "Save Dashboard"
msgstr "Dashboard opslaan"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Enkele"
msgid "Some folders"
msgstr "Sommige mappen"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kan ikke skanne? Kopier "
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Avbryt"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Avbryt redigering"
@@ -6187,9 +6185,9 @@ msgstr "Kunne ikke fjerne bilde"
msgid "Failed to retry jobs. Please try again later."
msgstr "Mislyktes å prøve jobber på nytt. Vennligst prøv igjen senere."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Oppsett"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Lørdag"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Lagre"
@@ -11597,11 +11594,6 @@ msgstr "Lagre som ny visning"
msgid "Save Dashboard"
msgstr "Lagre dashbord"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Noen"
msgid "Some folders"
msgstr "Noen mapper"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Nie można zeskanować? Skopiuj"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Anuluj"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Anuluj edycję"
@@ -6187,9 +6185,9 @@ msgstr "Nie udało się usunąć zdjęcia"
msgid "Failed to retry jobs. Please try again later."
msgstr "Nie udało się ponowić zadań. Proszę spróbować ponownie później."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Układ"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sobota"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Zapisz"
@@ -11597,11 +11594,6 @@ msgstr "Zapisz jako nowy widok"
msgid "Save Dashboard"
msgstr "Zapisz Dashboard"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Niektóre"
msgid "Some folders"
msgstr "Niektóre foldery"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2554,13 +2554,11 @@ msgstr ""
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr ""
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr ""
@@ -6182,9 +6180,9 @@ msgstr ""
msgid "Failed to retry jobs. Please try again later."
msgstr ""
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8016,7 +8014,7 @@ msgid "Layout"
msgstr ""
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11577,7 +11575,6 @@ msgstr ""
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr ""
@@ -11592,11 +11589,6 @@ msgstr ""
msgid "Save Dashboard"
msgstr ""
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12635,6 +12627,11 @@ msgstr ""
msgid "Some folders"
msgstr ""
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Não consegue escanear? Copie o"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Cancelar"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Cancelar Edição"
@@ -6187,9 +6185,9 @@ msgstr "Falha ao remover imagem"
msgid "Failed to retry jobs. Please try again later."
msgstr "Falha ao repetir trabalhos. Por favor, tente novamente mais tarde."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Disposição"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sábado"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Salvar"
@@ -11597,11 +11594,6 @@ msgstr "Salvar como nova visualização"
msgid "Save Dashboard"
msgstr "Salvar Painel"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Alguns"
msgid "Some folders"
msgstr "Algumas pastas"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Não consegue escanear? Copie o"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Cancelar"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Cancelar Edição"
@@ -6187,9 +6185,9 @@ msgstr "Falha ao remover a imagem"
msgid "Failed to retry jobs. Please try again later."
msgstr "Falha ao reiniciar trabalhos. Por favor, tente novamente mais tarde."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Layout"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sábado"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Salvar"
@@ -11597,11 +11594,6 @@ msgstr "Salvar como nova vista"
msgid "Save Dashboard"
msgstr "Salvar Painel"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Alguns"
msgid "Some folders"
msgstr "Algumas pastas"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Nu poți scana? Copiază"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Anulare"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Anulează ediția"
@@ -6187,9 +6185,9 @@ msgstr "Nu s-a reușit eliminarea imaginii"
msgid "Failed to retry jobs. Please try again later."
msgstr "Eșuat la reîncercarea locurilor de muncă. Încercați din nou mai târziu."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Aspect"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Sâmbătă"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Salvează"
@@ -11597,11 +11594,6 @@ msgstr "Salvează ca nouă vizualizare"
msgid "Save Dashboard"
msgstr "Salvează tabloul de bord"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Câteva"
msgid "Some folders"
msgstr "Unele foldere"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
Binary file not shown.
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Не можете да скенирате? Копирајте"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Откажи"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Откажи издање"
@@ -6187,9 +6185,9 @@ msgstr "Није успело уклањање слике"
msgid "Failed to retry jobs. Please try again later."
msgstr "Није успело понављање послова. Покушајте поново касније."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Изглед"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Субота"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Сачувај"
@@ -11597,11 +11594,6 @@ msgstr "Сачувај као ново приказ"
msgid "Save Dashboard"
msgstr "Сачувај контролну таблу"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Неки"
msgid "Some folders"
msgstr "Неке фасцикле"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Kan inte skanna? Kopiera "
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Avbryt"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Avbryt utgåva"
@@ -6187,9 +6185,9 @@ msgstr "Misslyckades med att ta bort bild"
msgid "Failed to retry jobs. Please try again later."
msgstr "Det gick inte att återsöka jobben. Försök igen senare."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Layout"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11586,7 +11584,6 @@ msgstr ""
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Spara"
@@ -11601,11 +11598,6 @@ msgstr "Spara som ny vy"
msgid "Save Dashboard"
msgstr "Spara instrumentpanel"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12644,6 +12636,11 @@ msgstr "Vissa"
msgid "Some folders"
msgstr "Vissa mappar"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Tarayamaz mısınız? Kopyalayın."
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "İptal"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Sürümü İptal Et"
@@ -6187,9 +6185,9 @@ msgstr "Resim kaldırılamadı"
msgid "Failed to retry jobs. Please try again later."
msgstr "İşleri yeniden deneme başarısız oldu. Lütfen daha sonra tekrar deneyiniz."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Düzen"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Cumartesi"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Kaydet"
@@ -11597,11 +11594,6 @@ msgstr "Yeni görünüm olarak kaydet"
msgid "Save Dashboard"
msgstr "Gösterge Panelini Kaydet"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Bazı"
msgid "Some folders"
msgstr "Bazı klasörler"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Не можете сканувати? Скопіюйте"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Скасувати"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Скасувати редакцію"
@@ -6187,9 +6185,9 @@ msgstr "Не вдалося видалити зображення"
msgid "Failed to retry jobs. Please try again later."
msgstr "Не вдалося повторити завдання. Будь ласка, спробуйте пізніше."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Макет"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Субота"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Зберегти"
@@ -11597,11 +11594,6 @@ msgstr "Зберегти як новий перегляд"
msgid "Save Dashboard"
msgstr "Зберегти інформаційну панель"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Деякі"
msgid "Some folders"
msgstr "Деякі папки"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "Không thể quét? Sao chép"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "Hủy bỏ"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "Hủy chỉnh sửa"
@@ -6187,9 +6185,9 @@ msgstr "Không thể xóa ảnh"
msgid "Failed to retry jobs. Please try again later."
msgstr "Không thể thử lại các công việc. Vui lòng thử lại sau."
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "Bố cục"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "Thứ Bảy"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "Lưu"
@@ -11597,11 +11594,6 @@ msgstr "Lưu dưới dạng chế độ xem mới"
msgid "Save Dashboard"
msgstr "Lưu bảng điều khiển"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "Một số"
msgid "Some folders"
msgstr "Một số thư mục"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "无法扫描?复制"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "取消"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "取消版本"
@@ -6187,9 +6185,9 @@ msgstr "移除图片失败"
msgid "Failed to retry jobs. Please try again later."
msgstr "重试任务失败。请稍后再试。"
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "布局"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "星期六"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "保存"
@@ -11597,11 +11594,6 @@ msgstr "另存为新视图"
msgid "Save Dashboard"
msgstr "保存仪表板"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "一些"
msgid "Some folders"
msgstr "某些文件夹"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
+9 -12
View File
@@ -2559,13 +2559,11 @@ msgstr "無法掃描? 複製"
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/settings/components/SaveAndCancelButtons/CancelButton.tsx
#: src/modules/object-record/record-update-multiple/components/UpdateMultipleRecordsFooter.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel"
msgstr "取消"
#. js-lingui-id: HYLdMN
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Cancel Edition"
msgstr "取消編輯"
@@ -6187,9 +6185,9 @@ msgstr "移除圖片失敗"
msgid "Failed to retry jobs. Please try again later."
msgstr "無法重試工作。請稍後重試。"
#. js-lingui-id: fuRtgB
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
msgid "Failed to save navigation layout"
#. js-lingui-id: farFvy
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Failed to save layout customization"
msgstr ""
#. js-lingui-id: y3HIOa
@@ -8021,7 +8019,7 @@ msgid "Layout"
msgstr "版面設計"
#. js-lingui-id: W4nIBb
#: src/modules/navigation-menu-item/edit/components/NavigationMenuEditModeBar.tsx
#: src/modules/layout-customization/components/LayoutCustomizationBar.tsx
msgid "Layout customization"
msgstr ""
@@ -11582,7 +11580,6 @@ msgstr "星期六"
#: src/pages/settings/applications/tabs/SettingsApplicationRegistrationOAuthTab.tsx
#: src/modules/settings/components/SaveAndCancelButtons/SaveButton.tsx
#: src/modules/settings/admin-panel/config-variables/components/ConfigVariableActionButtons.tsx
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
#: src/modules/command-menu-item/record/constants/DashboardCommandMenuItemsConfig.tsx
msgid "Save"
msgstr "保存"
@@ -11597,11 +11594,6 @@ msgstr "另存為新視圖"
msgid "Save Dashboard"
msgstr "保存儀表板"
#. js-lingui-id: BZVCCj
#: src/modules/command-menu-item/record/constants/DefaultRecordCommandMenuItemsConfig.tsx
msgid "Save Page Layout"
msgstr ""
#. js-lingui-id: veLq3R
#: src/pages/settings/applications/tabs/SettingsApplicationsDeveloperTab.tsx
msgid "Scaffold a new app, then use the CLI to develop, publish, and distribute"
@@ -12640,6 +12632,11 @@ msgstr "一些"
msgid "Some folders"
msgstr "某些資料夾"
#. js-lingui-id: hqUWge
#: src/modules/layout-customization/hooks/useSaveLayoutCustomization.ts
msgid "Some layout changes could not be saved"
msgstr ""
#. js-lingui-id: nwtY4N
#: src/pages/auth/Authorize.tsx
msgid "Something went wrong"
@@ -0,0 +1,93 @@
import { useExecuteTasksOnAnyLocationChange } from '@/app/hooks/useExecuteTasksOnAnyLocationChange';
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
import { isDashboardInEditModeComponentState } from '@/page-layout/states/isDashboardInEditModeComponentState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { act, renderHook } from '@testing-library/react';
import { createStore, Provider as JotaiProvider } from 'jotai';
import { type ReactNode } from 'react';
const mockCloseAnyOpenDropdown = jest.fn();
jest.mock('@/ui/layout/dropdown/hooks/useCloseAnyOpenDropdown', () => ({
useCloseAnyOpenDropdown: () => ({
closeAnyOpenDropdown: mockCloseAnyOpenDropdown,
}),
}));
const PAGE_LAYOUT_ID = 'test-page-layout-id';
const getWrapper =
(store = createStore()) =>
({ children }: { children: ReactNode }) => (
<JotaiProvider store={store}>{children}</JotaiProvider>
);
describe('useExecuteTasksOnAnyLocationChange', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should reset page layout edit state when layout customization is inactive', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(currentPageLayoutIdState.atom, PAGE_LAYOUT_ID);
store.set(
isDashboardInEditModeComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID,
}),
true,
);
store.set(isLayoutCustomizationModeEnabledState.atom, false);
const { result } = renderHook(() => useExecuteTasksOnAnyLocationChange(), {
wrapper,
});
act(() => {
result.current.executeTasksOnAnyLocationChange();
});
expect(mockCloseAnyOpenDropdown).toHaveBeenCalledTimes(1);
expect(store.get(currentPageLayoutIdState.atom)).toBeNull();
expect(
store.get(
isDashboardInEditModeComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID,
}),
),
).toBe(false);
});
it('should not reset page layout edit state when layout customization is active', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(currentPageLayoutIdState.atom, PAGE_LAYOUT_ID);
store.set(
isDashboardInEditModeComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID,
}),
true,
);
store.set(isLayoutCustomizationModeEnabledState.atom, true);
const { result } = renderHook(() => useExecuteTasksOnAnyLocationChange(), {
wrapper,
});
act(() => {
result.current.executeTasksOnAnyLocationChange();
});
expect(mockCloseAnyOpenDropdown).toHaveBeenCalledTimes(1);
expect(store.get(currentPageLayoutIdState.atom)).toBe(PAGE_LAYOUT_ID);
expect(
store.get(
isDashboardInEditModeComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID,
}),
),
).toBe(true);
});
});
@@ -1,3 +1,4 @@
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { MAIN_CONTEXT_STORE_INSTANCE_ID } from '@/context-store/constants/MainContextStoreInstanceId';
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
@@ -8,7 +9,7 @@ import { fieldsWidgetGroupsPersistedComponentState } from '@/page-layout/states/
import { fieldsWidgetUngroupedFieldsDraftComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsDraftComponentState';
import { fieldsWidgetUngroupedFieldsPersistedComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsPersistedComponentState';
import { hasInitializedFieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/hasInitializedFieldsWidgetGroupsDraftComponentState';
import { isPageLayoutInEditModeComponentState } from '@/page-layout/states/isPageLayoutInEditModeComponentState';
import { isDashboardInEditModeComponentState } from '@/page-layout/states/isDashboardInEditModeComponentState';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutIsInitializedComponentState } from '@/page-layout/states/pageLayoutIsInitializedComponentState';
@@ -57,7 +58,7 @@ export const useExecuteTasksOnAnyLocationChange = () => {
}
store.set(
isPageLayoutInEditModeComponentState.atomFamily({
isDashboardInEditModeComponentState.atomFamily({
instanceId: pageLayoutId,
}),
false,
@@ -137,7 +138,14 @@ export const useExecuteTasksOnAnyLocationChange = () => {
*/
const executeTasksOnAnyLocationChange = () => {
closeAnyOpenDropdown();
resetPageLayoutEditMode();
const isLayoutCustomizationModeEnabled = store.get(
isLayoutCustomizationModeEnabledState.atom,
);
if (!isLayoutCustomizationModeEnabled) {
resetPageLayoutEditMode();
}
};
return { executeTasksOnAnyLocationChange };
@@ -24,9 +24,7 @@ import { CancelDashboardSingleRecordCommand } from '@/command-menu-item/record/s
import { DuplicateDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/DuplicateDashboardSingleRecordCommand';
import { EditDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/EditDashboardSingleRecordCommand';
import { SaveDashboardSingleRecordCommand } from '@/command-menu-item/record/single-record/dashboard/components/SaveDashboardSingleRecordCommand';
import { CancelRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/CancelRecordPageLayoutSingleRecordCommand';
import { EditRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/EditRecordPageLayoutSingleRecordCommand';
import { SaveRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/SaveRecordPageLayoutSingleRecordCommand';
import { SeeVersionWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/SeeVersionWorkflowRunSingleRecordCommand';
import { SeeWorkflowWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/SeeWorkflowWorkflowRunSingleRecordCommand';
import { StopWorkflowRunSingleRecordCommand } from '@/command-menu-item/record/single-record/workflow-runs/components/StopWorkflowRunSingleRecordCommand';
@@ -96,9 +94,6 @@ export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
[EngineComponentKey.USE_AS_DRAFT_WORKFLOW_VERSION]: (
<UseAsDraftWorkflowVersionSingleRecordCommand />
),
[EngineComponentKey.SAVE_RECORD_PAGE_LAYOUT]: (
<SaveRecordPageLayoutSingleRecordCommand />
),
[EngineComponentKey.SAVE_DASHBOARD_LAYOUT]: (
<SaveDashboardSingleRecordCommand />
),
@@ -136,9 +131,6 @@ export const ENGINE_COMPONENT_KEY_COMPONENT_MAP: Record<
[EngineComponentKey.EDIT_RECORD_PAGE_LAYOUT]: (
<EditRecordPageLayoutSingleRecordCommand />
),
[EngineComponentKey.CANCEL_RECORD_PAGE_LAYOUT]: (
<CancelRecordPageLayoutSingleRecordCommand />
),
[EngineComponentKey.EDIT_DASHBOARD_LAYOUT]: (
<EditDashboardSingleRecordCommand />
),
@@ -13,14 +13,20 @@ export const CommandDropdownItem = ({
action,
onClick,
to,
disabled = false,
}: {
action: CommandMenuItemDisplayProps;
onClick?: () => void;
to?: string;
disabled?: boolean;
}) => {
const navigate = useNavigate();
const handleClick = () => {
if (disabled) {
return;
}
onClick?.();
if (isDefined(to)) {
navigate(to);
@@ -45,6 +51,7 @@ export const CommandDropdownItem = ({
LeftIcon={action.Icon}
onClick={handleClick}
text={getCommandMenuItemLabel(action.label)}
disabled={disabled}
/>
</SelectableListItem>
);
@@ -11,11 +11,13 @@ export const CommandListItem = ({
onClick,
to,
disabled = false,
showDisabledLoader = false,
}: {
action: CommandMenuItemDisplayProps;
onClick?: () => void;
to?: string;
disabled?: boolean;
showDisabledLoader?: boolean;
}) => {
const navigate = useNavigate();
@@ -41,7 +43,7 @@ export const CommandListItem = ({
onClick={disabled ? undefined : onClick}
hotKeys={action.hotKeys}
disabled={disabled}
RightComponent={disabled ? <Loader /> : undefined}
RightComponent={disabled && showDisabledLoader ? <Loader /> : undefined}
/>
</SelectableListItem>
);
@@ -5,10 +5,19 @@ export const CommandMenuItemButton = ({
action,
onClick,
to,
disabled = false,
}: {
action: CommandMenuItemDisplayProps;
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
to?: string;
disabled?: boolean;
}) => {
return <CommandMenuButton command={action} to={to} onClick={onClick} />;
return (
<CommandMenuButton
command={action}
to={to}
onClick={onClick}
disabled={disabled}
/>
);
};
@@ -3,8 +3,10 @@ import { CommandDropdownItem } from '@/command-menu-item/display/components/Comm
import { CommandListItem } from '@/command-menu-item/display/components/CommandListItem';
import { CommandConfigContext } from '@/command-menu-item/contexts/CommandConfigContext';
import { CommandMenuContext } from '@/command-menu-item/contexts/CommandMenuContext';
import { useIsCommandBlockedByGlobalLayoutCustomization } from '@/command-menu-item/hooks/useIsCommandBlockedByGlobalLayoutCustomization';
import { type MessageDescriptor } from '@lingui/core';
import { useContext } from 'react';
import { type Nullable } from 'twenty-shared/types';
import { assertUnreachable } from 'twenty-shared/utils';
import { type IconComponent } from 'twenty-ui/display';
import { type MenuItemAccent } from 'twenty-ui/navigation';
@@ -12,47 +14,72 @@ import { type MenuItemAccent } from 'twenty-ui/navigation';
export type CommandMenuItemDisplayProps = {
key: string;
label: MessageDescriptor | string;
shortLabel?: MessageDescriptor | string;
shortLabel?: Nullable<MessageDescriptor | string>;
description?: MessageDescriptor | string;
Icon: IconComponent;
isPrimaryCTA?: boolean;
accent?: MenuItemAccent;
hotKeys?: string[];
hotKeys?: Nullable<string[]>;
};
export const CommandMenuItemDisplay = ({
onClick,
to,
disabled,
showDisabledLoader = false,
}: {
onClick?: (event?: React.MouseEvent<HTMLElement>) => void;
to?: string;
disabled?: boolean;
showDisabledLoader?: boolean;
}) => {
const action = useContext(CommandConfigContext);
const { displayType } = useContext(CommandMenuContext);
const isBlockedByGlobalLayoutCustomization =
useIsCommandBlockedByGlobalLayoutCustomization(action);
if (!action) {
return null;
}
const isDisabled =
disabled === true || isBlockedByGlobalLayoutCustomization === true;
const onClickWhenEnabled = isDisabled ? undefined : onClick;
const toWhenEnabled = isDisabled ? undefined : to;
if (displayType === 'button') {
return <CommandMenuItemButton action={action} onClick={onClick} to={to} />;
return (
<CommandMenuItemButton
action={action}
onClick={onClickWhenEnabled}
to={toWhenEnabled}
disabled={isDisabled}
/>
);
}
if (displayType === 'listItem') {
return (
<CommandListItem
action={action}
onClick={onClick}
to={to}
disabled={disabled}
onClick={onClickWhenEnabled}
to={toWhenEnabled}
disabled={isDisabled}
showDisabledLoader={showDisabledLoader}
/>
);
}
if (displayType === 'dropdownItem') {
return <CommandDropdownItem action={action} onClick={onClick} to={to} />;
return (
<CommandDropdownItem
action={action}
onClick={onClickWhenEnabled}
to={toWhenEnabled}
disabled={isDisabled}
/>
);
}
return assertUnreachable(displayType, 'Unsupported display type');
@@ -38,5 +38,11 @@ export const HeadlessFrontComponentCommandMenuItem = ({
onClick();
};
return <CommandMenuItemDisplay onClick={handleClick} disabled={isMounted} />;
return (
<CommandMenuItemDisplay
onClick={handleClick}
disabled={isMounted}
showDisabledLoader={isMounted}
/>
);
};
@@ -16,6 +16,7 @@ export const COMMAND_MENU_ITEM_FRAGMENT = gql`
shortLabel
position
isPinned
hotKeys
conditionalAvailabilityExpression
availabilityType
availabilityObjectMetadataId
@@ -0,0 +1,87 @@
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { useIsCommandBlockedByGlobalLayoutCustomization } from '@/command-menu-item/hooks/useIsCommandBlockedByGlobalLayoutCustomization';
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
import { renderHook } from '@testing-library/react';
import { createStore, Provider as JotaiProvider } from 'jotai';
import { type ReactNode } from 'react';
import { CommandMenuItemViewType } from 'twenty-shared/types';
import { Icon123 } from 'twenty-ui/display';
const getWrapper =
(store = createStore()) =>
({ children }: { children: ReactNode }) => (
<JotaiProvider store={store}>{children}</JotaiProvider>
);
const buildCommandMenuItemConfig = (
isAllowedDuringGlobalLayoutCustomization?: boolean,
): CommandMenuItemConfig => ({
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.Global,
key: 'test-command',
label: 'Test Command',
position: 1,
Icon: Icon123,
availableOn: [CommandMenuItemViewType.GLOBAL],
shouldBeRegistered: () => true,
component: null,
isAllowedDuringGlobalLayoutCustomization,
});
describe('useIsCommandBlockedByGlobalLayoutCustomization', () => {
it('should not block commands when global layout customization is inactive', () => {
const store = createStore();
const wrapper = getWrapper(store);
const commandMenuItemConfig = buildCommandMenuItemConfig(false);
store.set(isLayoutCustomizationModeEnabledState.atom, false);
const { result } = renderHook(
() =>
useIsCommandBlockedByGlobalLayoutCustomization(commandMenuItemConfig),
{
wrapper,
},
);
expect(result.current).toBe(false);
});
it('should block commands by default when global layout customization is active', () => {
const store = createStore();
const wrapper = getWrapper(store);
const commandMenuItemConfig = buildCommandMenuItemConfig();
store.set(isLayoutCustomizationModeEnabledState.atom, true);
const { result } = renderHook(
() =>
useIsCommandBlockedByGlobalLayoutCustomization(commandMenuItemConfig),
{
wrapper,
},
);
expect(result.current).toBe(true);
});
it('should allow commands explicitly marked for global layout customization', () => {
const store = createStore();
const wrapper = getWrapper(store);
const commandMenuItemConfig = buildCommandMenuItemConfig(true);
store.set(isLayoutCustomizationModeEnabledState.atom, true);
const { result } = renderHook(
() =>
useIsCommandBlockedByGlobalLayoutCustomization(commandMenuItemConfig),
{
wrapper,
},
);
expect(result.current).toBe(false);
});
});
@@ -0,0 +1,186 @@
import { useRegisteredCommandMenuItems } from '@/command-menu-item/hooks/useRegisteredCommandMenuItems';
import { CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
import { contextStoreCurrentViewTypeComponentState } from '@/context-store/states/contextStoreCurrentViewTypeComponentState';
import { contextStoreIsPageInEditModeComponentState } from '@/context-store/states/contextStoreIsPageInEditModeComponentState';
import { contextStoreTargetedRecordsRuleComponentState } from '@/context-store/states/contextStoreTargetedRecordsRuleComponentState';
import { ContextStoreComponentInstanceContext } from '@/context-store/states/contexts/ContextStoreComponentInstanceContext';
import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType';
import { act, renderHook } from '@testing-library/react';
import { createStore, Provider as JotaiProvider } from 'jotai';
import { type ReactNode } from 'react';
import { CommandMenuItemViewType } from 'twenty-shared/types';
import { Icon123 } from 'twenty-ui/display';
jest.mock('@/command-menu-item/utils/getCommandMenuItemConfig', () => ({
getCommandMenuItemConfig: () => ({}),
}));
jest.mock(
'@/command-menu-item/record-agnostic/hooks/useRelatedRecordCommands',
() => ({
useRelatedRecordCommands: () => ({}),
}),
);
jest.mock('@/settings/roles/hooks/usePermissionFlagMap', () => ({
usePermissionFlagMap: () => ({}),
}));
jest.mock(
'@/command-menu-item/record-agnostic/hooks/useRecordAgnosticCommands',
() => ({
useRecordAgnosticCommands: () => ({
pageEditItem: {
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.Global,
key: 'page-edit-item',
label: 'Page Edit Item',
position: 0,
Icon: Icon123,
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
shouldBeRegistered: () => true,
component: null,
},
showItem: {
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.Global,
key: 'show-item',
label: 'Show Item',
position: 1,
Icon: Icon123,
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
shouldBeRegistered: () => true,
component: null,
},
globalItem: {
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.Global,
key: 'global-item',
label: 'Global Item',
position: 2,
Icon: Icon123,
availableOn: [CommandMenuItemViewType.GLOBAL],
shouldBeRegistered: () => true,
component: null,
},
}),
}),
);
const CONTEXT_STORE_INSTANCE_ID = 'test-context-store-instance-id';
const getWrapper = (store = createStore()) => {
return ({ children }: { children: ReactNode }) => (
<JotaiProvider store={store}>
<ContextStoreComponentInstanceContext.Provider
value={{ instanceId: CONTEXT_STORE_INSTANCE_ID }}
>
{children}
</ContextStoreComponentInstanceContext.Provider>
</JotaiProvider>
);
};
const shouldBeRegisteredParams = {
objectPermissions: {
canReadObjectRecords: true,
canUpdateObjectRecords: true,
canSoftDeleteObjectRecords: true,
canDestroyObjectRecords: true,
restrictedFields: {},
objectMetadataId: '',
rowLevelPermissionPredicates: [],
rowLevelPermissionPredicateGroups: [],
},
getTargetObjectReadPermission: () => true,
getTargetObjectWritePermission: () => true,
isFeatureFlagEnabled: () => true,
};
describe('useRegisteredCommandMenuItems', () => {
it('should register SHOW_PAGE and GLOBAL commands when page is not in edit mode', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
ContextStoreViewType.ShowPage,
);
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
{ mode: 'selection', selectedRecordIds: [] },
);
act(() => {
store.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
false,
);
});
const { result } = renderHook(
() =>
useRegisteredCommandMenuItems(
shouldBeRegisteredParams as Parameters<
typeof useRegisteredCommandMenuItems
>[0],
),
{
wrapper,
},
);
expect(result.current.map((item) => item.key)).toEqual([
'show-item',
'global-item',
]);
});
it('should register PAGE_EDIT_MODE commands when page is in edit mode', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(
contextStoreCurrentViewTypeComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
ContextStoreViewType.ShowPage,
);
store.set(
contextStoreTargetedRecordsRuleComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
{ mode: 'selection', selectedRecordIds: [] },
);
act(() => {
store.set(
contextStoreIsPageInEditModeComponentState.atomFamily({
instanceId: CONTEXT_STORE_INSTANCE_ID,
}),
true,
);
});
const { result } = renderHook(
() =>
useRegisteredCommandMenuItems(
shouldBeRegisteredParams as Parameters<
typeof useRegisteredCommandMenuItems
>[0],
),
{
wrapper,
},
);
expect(result.current.map((item) => item.key)).toEqual(['page-edit-item']);
});
});
@@ -0,0 +1,17 @@
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
export const useIsCommandBlockedByGlobalLayoutCustomization = (
commandMenuItemConfig: CommandMenuItemConfig | null,
) => {
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
if (!isLayoutCustomizationModeEnabled) {
return false;
}
return !commandMenuItemConfig?.isAllowedDuringGlobalLayoutCustomization;
};
@@ -21,9 +21,7 @@ import { NavigateToNextRecordSingleRecordCommand } from '@/command-menu-item/rec
import { NavigateToPreviousRecordSingleRecordCommand } from '@/command-menu-item/record/single-record/components/NavigateToPreviousRecordSingleRecordCommand';
import { RemoveFromFavoritesSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RemoveFromFavoritesSingleRecordCommand';
import { RestoreSingleRecordCommand } from '@/command-menu-item/record/single-record/components/RestoreSingleRecordCommand';
import { CancelRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/CancelRecordPageLayoutSingleRecordCommand';
import { EditRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/EditRecordPageLayoutSingleRecordCommand';
import { SaveRecordPageLayoutSingleRecordCommand } from '@/command-menu-item/record/single-record/record-page-layout/components/SaveRecordPageLayoutSingleRecordCommand';
import { RecordPageLayoutSingleRecordCommandKeys } from '@/command-menu-item/record/single-record/record-page-layout/types/RecordPageLayoutSingleRecordCommandKeys';
import { SingleRecordCommandKeys } from '@/command-menu-item/record/single-record/types/SingleRecordCommandKeys';
import { type CommandMenuItemConfig } from '@/command-menu-item/types/CommandMenuItemConfig';
@@ -45,11 +43,9 @@ import {
import {
IconArrowMerge,
IconBuildingSkyscraper,
IconCancel,
IconCheckbox,
IconChevronDown,
IconChevronUp,
IconDeviceFloppy,
IconEdit,
IconEyeOff,
IconFileExport,
@@ -814,59 +810,4 @@ export const DEFAULT_RECORD_COMMAND_MENU_ITEMS_CONFIG: Record<
availableOn: [CommandMenuItemViewType.SHOW_PAGE],
component: <EditRecordPageLayoutSingleRecordCommand />,
},
[RecordPageLayoutSingleRecordCommandKeys.SAVE_RECORD_PAGE_LAYOUT]: {
key: RecordPageLayoutSingleRecordCommandKeys.SAVE_RECORD_PAGE_LAYOUT,
label: msg`Save Page Layout`,
shortLabel: msg`Save`,
isPinned: true,
isPrimaryCTA: true,
position: 31,
Icon: IconDeviceFloppy,
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.RecordSelection,
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
shouldBeRegistered: ({
selectedRecord,
objectPermissions,
objectMetadataItem,
isFeatureFlagEnabled,
}) =>
isFeatureFlagEnabled(
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
) &&
isDefined(selectedRecord) &&
!selectedRecord?.isRemote &&
!isDefined(selectedRecord?.deletedAt) &&
objectPermissions.canUpdateObjectRecords &&
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
component: <SaveRecordPageLayoutSingleRecordCommand />,
},
[RecordPageLayoutSingleRecordCommandKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION]: {
key: RecordPageLayoutSingleRecordCommandKeys.CANCEL_RECORD_PAGE_LAYOUT_EDITION,
label: msg`Cancel Edition`,
shortLabel: msg`Cancel`,
isPinned: true,
position: 32,
Icon: IconCancel,
type: CommandMenuItemType.Standard,
scope: CommandMenuItemScope.RecordSelection,
requiredPermissionFlag: PermissionFlagType.LAYOUTS,
shouldBeRegistered: ({
selectedRecord,
objectPermissions,
objectMetadataItem,
isFeatureFlagEnabled,
}) =>
isFeatureFlagEnabled(
FeatureFlagKey.IS_RECORD_PAGE_LAYOUT_EDITING_ENABLED,
) &&
isDefined(selectedRecord) &&
!selectedRecord?.isRemote &&
!isDefined(selectedRecord?.deletedAt) &&
objectPermissions.canUpdateObjectRecords &&
objectMetadataItem?.nameSingular !== CoreObjectNameSingular.Dashboard,
availableOn: [CommandMenuItemViewType.PAGE_EDIT_MODE],
component: <CancelRecordPageLayoutSingleRecordCommand />,
},
};
@@ -1,15 +1,12 @@
import { useEnterLayoutCustomizationMode } from '@/layout-customization/hooks/useEnterLayoutCustomizationMode';
import { Command } from '@/command-menu-item/display/components/Command';
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/common/states/isNavigationMenuInEditModeState';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
export const EditNavigationSidebarNoSelectionRecordCommand = () => {
const setIsNavigationMenuInEditMode = useSetAtomState(
isNavigationMenuInEditModeState,
);
const { enterLayoutCustomizationMode } = useEnterLayoutCustomizationMode();
return (
<Command
onClick={() => setIsNavigationMenuInEditMode(true)}
onClick={() => enterLayoutCustomizationMode()}
closeSidePanelOnCommandMenuListExecution
/>
);
@@ -1,31 +0,0 @@
import { Command } from '@/command-menu-item/display/components/Command';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
import { useResetDraftPageLayoutToPersistedPageLayout } from '@/page-layout/hooks/useResetDraftPageLayoutToPersistedPageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
export const CancelRecordPageLayoutSingleRecordCommand = () => {
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
const { pageLayoutId } = useRecordPageLayoutIdFromRecordStoreOrThrow({
targetObjectNameSingular: objectMetadataItem.nameSingular,
});
const { closeSidePanelMenu } = useSidePanelMenu();
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const { resetDraftPageLayoutToPersistedPageLayout } =
useResetDraftPageLayoutToPersistedPageLayout(pageLayoutId);
const handleClick = () => {
closeSidePanelMenu();
resetDraftPageLayoutToPersistedPageLayout();
setIsPageLayoutInEditMode(false);
};
return <Command onClick={handleClick} />;
};
@@ -1,23 +1,14 @@
import { useEnterLayoutCustomizationMode } from '@/layout-customization/hooks/useEnterLayoutCustomizationMode';
import { Command } from '@/command-menu-item/display/components/Command';
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
import { useResetLocationHash } from 'twenty-ui/utilities';
export const EditRecordPageLayoutSingleRecordCommand = () => {
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
const { pageLayoutId } = useRecordPageLayoutIdFromRecordStoreOrThrow({
targetObjectNameSingular: objectMetadataItem.nameSingular,
});
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const { enterLayoutCustomizationMode } = useEnterLayoutCustomizationMode();
const { resetLocationHash } = useResetLocationHash();
const handleClick = () => {
setIsPageLayoutInEditMode(true);
enterLayoutCustomizationMode();
resetLocationHash();
};
@@ -1,38 +0,0 @@
import { Command } from '@/command-menu-item/display/components/Command';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useContextStoreObjectMetadataItemOrThrow } from '@/context-store/hooks/useContextStoreObjectMetadataItemOrThrow';
import { useRecordPageLayoutIdFromRecordStoreOrThrow } from '@/page-layout/hooks/useRecordPageLayoutIdFromRecordStoreOrThrow';
import { useSaveFieldsWidgetGroups } from '@/page-layout/hooks/useSaveFieldsWidgetGroups';
import { useSavePageLayout } from '@/page-layout/hooks/useSavePageLayout';
import { useSetIsPageLayoutInEditMode } from '@/page-layout/hooks/useSetIsPageLayoutInEditMode';
export const SaveRecordPageLayoutSingleRecordCommand = () => {
const { objectMetadataItem } = useContextStoreObjectMetadataItemOrThrow();
const { pageLayoutId } = useRecordPageLayoutIdFromRecordStoreOrThrow({
targetObjectNameSingular: objectMetadataItem.nameSingular,
});
const { savePageLayout } = useSavePageLayout(pageLayoutId);
const { saveFieldsWidgetGroups } = useSaveFieldsWidgetGroups({
pageLayoutId,
});
const { setIsPageLayoutInEditMode } =
useSetIsPageLayoutInEditMode(pageLayoutId);
const { closeSidePanelMenu } = useSidePanelMenu();
const handleClick = async () => {
const result = await savePageLayout();
if (result.status === 'successful') {
await saveFieldsWidgetGroups();
closeSidePanelMenu();
setIsPageLayoutInEditMode(false);
}
};
return <Command onClick={handleClick} />;
};
@@ -1,5 +1,3 @@
export enum RecordPageLayoutSingleRecordCommandKeys {
EDIT_RECORD_PAGE_LAYOUT = 'edit-record-page-layout-single-record',
SAVE_RECORD_PAGE_LAYOUT = 'save-record-page-layout-single-record',
CANCEL_RECORD_PAGE_LAYOUT_EDITION = 'cancel-record-page-layout-edition-single-record',
}
@@ -100,10 +100,11 @@ const buildCommandMenuItemFromFrontComponent = ({
key: `command-menu-item-front-component-${item.id}`,
scope,
label: displayLabel,
shortLabel: item.shortLabel ?? undefined,
shortLabel: item.shortLabel,
position: item.position,
isPinned,
Icon,
hotKeys: item.hotKeys,
shouldBeRegistered: () =>
evaluateConditionalAvailabilityExpression(
item.conditionalAvailabilityExpression,
@@ -148,10 +149,11 @@ const buildCommandItemFromEngineKey = ({
key: `command-menu-item-engine-${item.id}`,
scope,
label: item.label,
shortLabel: item.shortLabel ?? undefined,
shortLabel: item.shortLabel,
position: item.position,
isPinned,
Icon,
hotKeys: item.hotKeys,
shouldBeRegistered: () =>
evaluateConditionalAvailabilityExpression(
item.conditionalAvailabilityExpression,
@@ -1,8 +1,11 @@
import { type CommandMenuItemScope } from '@/command-menu-item/types/CommandMenuItemScope';
import { type CommandMenuItemType } from '@/command-menu-item/types/CommandMenuItemType';
import { type CommandMenuItemViewType } from 'twenty-shared/types';
import { type ShouldBeRegisteredFunctionParams } from '@/command-menu-item/types/ShouldBeRegisteredFunctionParams';
import { type MessageDescriptor } from '@lingui/core';
import {
type CommandMenuItemViewType,
type Nullable,
} from 'twenty-shared/types';
import { type IconComponent } from 'twenty-ui/display';
import { type MenuItemAccent } from 'twenty-ui/navigation';
import { type PermissionFlagType } from '~/generated-metadata/graphql';
@@ -12,7 +15,7 @@ export type CommandMenuItemConfig = {
scope: CommandMenuItemScope;
key: string;
label: MessageDescriptor | string;
shortLabel?: MessageDescriptor | string;
shortLabel?: Nullable<MessageDescriptor | string>;
description?: MessageDescriptor | string;
position: number;
Icon: IconComponent;
@@ -22,6 +25,7 @@ export type CommandMenuItemConfig = {
availableOn?: CommandMenuItemViewType[];
shouldBeRegistered: (params: ShouldBeRegisteredFunctionParams) => boolean;
component: React.ReactNode;
hotKeys?: string[];
hotKeys?: Nullable<string[]>;
requiredPermissionFlag?: PermissionFlagType;
isAllowedDuringGlobalLayoutCustomization?: boolean;
};
@@ -2,6 +2,8 @@ import { styled } from '@linaria/react';
import { i18n, type MessageDescriptor } from '@lingui/core';
import { isString } from '@sniptt/guards';
import { type MouseEvent } from 'react';
import { type Nullable } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
import {
AppTooltip,
type IconComponent,
@@ -19,12 +21,13 @@ export type CommandMenuButtonProps = {
command: {
key: string;
label: string | MessageDescriptor;
shortLabel?: string | MessageDescriptor;
shortLabel?: Nullable<string | MessageDescriptor>;
Icon: IconComponent;
isPrimaryCTA?: boolean;
};
onClick?: (event?: MouseEvent<HTMLElement>) => void;
to?: string;
disabled?: boolean;
};
const getCommandMenuButtonLabel = (
@@ -37,13 +40,13 @@ export const CommandMenuButton = ({
command,
onClick,
to,
disabled = false,
}: CommandMenuButtonProps) => {
const resolvedLabel = getCommandMenuButtonLabel(command.label);
const resolvedShortLabel =
command.shortLabel === undefined
? undefined
: getCommandMenuButtonLabel(command.shortLabel);
const resolvedShortLabel = isDefined(command.shortLabel)
? getCommandMenuButtonLabel(command.shortLabel)
: undefined;
const buttonAccent = command.isPrimaryCTA ? 'blue' : 'default';
@@ -57,6 +60,7 @@ export const CommandMenuButton = ({
accent={buttonAccent}
to={to}
onClick={onClick}
disabled={disabled}
title={resolvedShortLabel}
ariaLabel={resolvedLabel}
/>
@@ -69,6 +73,7 @@ export const CommandMenuButton = ({
accent={buttonAccent}
to={to}
onClick={onClick}
disabled={disabled}
ariaLabel={resolvedLabel}
/>
<StyledWrapper>
@@ -4,9 +4,10 @@ import { IconArrowUpRight, type IconComponent } from 'twenty-ui/display';
import { MenuItem } from 'twenty-ui/navigation';
import { useCommandMenuOnItemClick } from '@/command-menu/hooks/useCommandMenuOnItemClick';
import { isDefined } from 'twenty-shared/utils';
import { isSelectedItemIdComponentFamilyState } from '@/ui/layout/selectable-list/states/isSelectedItemIdComponentFamilyState';
import { useAtomComponentFamilyStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomComponentFamilyStateValue';
import { type Nullable } from 'twenty-shared/types';
import { isDefined } from 'twenty-shared/utils';
export type CommandMenuItemProps = {
label: string;
@@ -15,7 +16,7 @@ export type CommandMenuItemProps = {
id: string;
onClick?: () => void;
Icon?: IconComponent;
hotKeys?: string[];
hotKeys?: Nullable<string[]>;
LeftComponent?: ReactNode;
RightComponent?: ReactNode;
contextualTextPosition?: 'left' | 'right';
@@ -0,0 +1,78 @@
import { useCancelLayoutCustomization } from '@/layout-customization/hooks/useCancelLayoutCustomization';
import { useIsLayoutCustomizationDirty } from '@/layout-customization/hooks/useIsLayoutCustomizationDirty';
import { useSaveLayoutCustomization } from '@/layout-customization/hooks/useSaveLayoutCustomization';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { SaveAndCancelButtons } from '@/settings/components/SaveAndCancelButtons/SaveAndCancelButtons';
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
import { styled } from '@linaria/react';
import { useLingui } from '@lingui/react/macro';
import { AnimatePresence, motion } from 'framer-motion';
import { useContext } from 'react';
import { IconCheck, IconPaint } from 'twenty-ui/display';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
const StyledContainer = styled.div`
align-items: center;
background: ${themeCssVariables.color.blue};
box-sizing: border-box;
color: ${themeCssVariables.font.color.inverted};
display: flex;
justify-content: space-between;
padding: ${themeCssVariables.spacing[2]} ${themeCssVariables.spacing[3]};
width: 100%;
`;
const StyledTitle = styled.span`
align-items: center;
display: flex;
gap: ${themeCssVariables.spacing[2]};
`;
const LayoutCustomizationBarContent = () => {
const { theme } = useContext(ThemeContext);
const { t } = useLingui();
const { save, isSaving } = useSaveLayoutCustomization();
const { cancel } = useCancelLayoutCustomization();
const { isDirty } = useIsLayoutCustomizationDirty();
return (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{
duration: theme.animation.duration.normal,
ease: 'easeInOut',
}}
>
<StyledContainer>
<StyledTitle>
<IconPaint size={theme.icon.size.md} />
{t`Layout customization`}
</StyledTitle>
<SaveAndCancelButtons
onSave={save}
onCancel={cancel}
isSaveDisabled={!isDirty || isSaving}
isCancelDisabled={isSaving}
isLoading={isSaving}
inverted
saveIcon={IconCheck}
/>
</StyledContainer>
</motion.div>
);
};
export const LayoutCustomizationBar = () => {
const isLayoutCustomizationModeEnabled = useAtomStateValue(
isLayoutCustomizationModeEnabledState,
);
return (
<AnimatePresence>
{isLayoutCustomizationModeEnabled && <LayoutCustomizationBarContent />}
</AnimatePresence>
);
};
@@ -0,0 +1,224 @@
import { useIsLayoutCustomizationDirty } from '@/layout-customization/hooks/useIsLayoutCustomizationDirty';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { metadataStoreState } from '@/metadata-store/states/metadataStoreState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { renderHook } from '@testing-library/react';
import { createStore, Provider as JotaiProvider } from 'jotai';
import { type ReactNode } from 'react';
import {
type NavigationMenuItem,
NavigationMenuItemType,
PageLayoutType,
} from '~/generated-metadata/graphql';
const PAGE_LAYOUT_ID_1 = 'page-layout-1';
const PAGE_LAYOUT_ID_2 = 'page-layout-2';
const MOCK_PAGE_LAYOUT: PageLayout = {
__typename: 'PageLayout',
id: PAGE_LAYOUT_ID_1,
name: 'Test Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: 'obj-1',
tabs: [],
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
deletedAt: null,
defaultTabToFocusOnMobileAndSidePanelId: null,
};
const MOCK_DRAFT_PAGE_LAYOUT = {
id: PAGE_LAYOUT_ID_1,
name: 'Test Layout',
type: PageLayoutType.RECORD_PAGE,
objectMetadataId: 'obj-1',
tabs: [] as PageLayout['tabs'],
defaultTabToFocusOnMobileAndSidePanelId: null,
};
const getWrapper =
(store = createStore()) =>
({ children }: { children: ReactNode }) => (
<JotaiProvider store={store}>{children}</JotaiProvider>
);
describe('useIsLayoutCustomizationDirty', () => {
it('should return not dirty when no layouts are touched and nav is clean', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(metadataStoreState.atomFamily('navigationMenuItems'), {
current: [],
draft: [],
status: 'up-to-date',
});
store.set(isLayoutCustomizationModeEnabledState.atom, false);
const { result } = renderHook(() => useIsLayoutCustomizationDirty(), {
wrapper,
});
expect(result.current.isDirty).toBe(false);
});
it('should return dirty when a touched page layout draft differs from persisted', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(metadataStoreState.atomFamily('navigationMenuItems'), {
current: [],
draft: [],
status: 'up-to-date',
});
store.set(isLayoutCustomizationModeEnabledState.atom, true);
store.set(activeCustomizationPageLayoutIdsState.atom, [PAGE_LAYOUT_ID_1]);
store.set(
pageLayoutPersistedComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
MOCK_PAGE_LAYOUT,
);
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
{ ...MOCK_DRAFT_PAGE_LAYOUT, name: 'Modified Layout' },
);
const { result } = renderHook(() => useIsLayoutCustomizationDirty(), {
wrapper,
});
expect(result.current.isDirty).toBe(true);
});
it('should return not dirty when all touched layouts match persisted', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(metadataStoreState.atomFamily('navigationMenuItems'), {
current: [],
draft: [],
status: 'up-to-date',
});
store.set(isLayoutCustomizationModeEnabledState.atom, true);
store.set(activeCustomizationPageLayoutIdsState.atom, [PAGE_LAYOUT_ID_1]);
store.set(
pageLayoutPersistedComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
MOCK_PAGE_LAYOUT,
);
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
MOCK_DRAFT_PAGE_LAYOUT,
);
const { result } = renderHook(() => useIsLayoutCustomizationDirty(), {
wrapper,
});
expect(result.current.isDirty).toBe(false);
});
it('should return dirty when nav is dirty even if page layouts are clean', () => {
const store = createStore();
const wrapper = getWrapper(store);
// userWorkspaceId must be null so item passes workspace filter
const mockNavItem: NavigationMenuItem = {
id: 'nav-1',
position: 0,
type: NavigationMenuItemType.OBJECT,
viewId: null,
targetObjectMetadataId: null,
folderId: null,
name: null,
link: null,
icon: null,
color: null,
targetRecordId: null,
createdAt: '2024-01-01',
updatedAt: '2024-01-01',
userWorkspaceId: null,
};
store.set(metadataStoreState.atomFamily('navigationMenuItems'), {
current: [mockNavItem],
draft: [],
status: 'up-to-date',
});
store.set(isLayoutCustomizationModeEnabledState.atom, true);
// Nav draft differs from prefetch
store.set(navigationMenuItemsDraftState.atom, []);
const { result } = renderHook(() => useIsLayoutCustomizationDirty(), {
wrapper,
});
expect(result.current.isDirty).toBe(true);
});
it('should check multiple touched layouts', () => {
const store = createStore();
const wrapper = getWrapper(store);
store.set(metadataStoreState.atomFamily('navigationMenuItems'), {
current: [],
draft: [],
status: 'up-to-date',
});
store.set(isLayoutCustomizationModeEnabledState.atom, true);
store.set(activeCustomizationPageLayoutIdsState.atom, [
PAGE_LAYOUT_ID_1,
PAGE_LAYOUT_ID_2,
]);
// First layout is clean
store.set(
pageLayoutPersistedComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
MOCK_PAGE_LAYOUT,
);
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_1,
}),
MOCK_PAGE_LAYOUT,
);
// Second layout is dirty
const secondLayout: PageLayout = {
...MOCK_PAGE_LAYOUT,
id: PAGE_LAYOUT_ID_2,
name: 'Second Layout',
};
store.set(
pageLayoutPersistedComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_2,
}),
secondLayout,
);
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: PAGE_LAYOUT_ID_2,
}),
{ ...secondLayout, name: 'Modified Second' },
);
const { result } = renderHook(() => useIsLayoutCustomizationDirty(), {
wrapper,
});
expect(result.current.isDirty).toBe(true);
});
});
@@ -0,0 +1,99 @@
import { useExitLayoutCustomizationMode } from '@/layout-customization/hooks/useExitLayoutCustomizationMode';
import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { fieldsWidgetEditorModeDraftComponentState } from '@/page-layout/states/fieldsWidgetEditorModeDraftComponentState';
import { fieldsWidgetEditorModePersistedComponentState } from '@/page-layout/states/fieldsWidgetEditorModePersistedComponentState';
import { fieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/fieldsWidgetGroupsDraftComponentState';
import { fieldsWidgetGroupsPersistedComponentState } from '@/page-layout/states/fieldsWidgetGroupsPersistedComponentState';
import { fieldsWidgetUngroupedFieldsDraftComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsDraftComponentState';
import { fieldsWidgetUngroupedFieldsPersistedComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsPersistedComponentState';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
import { useStore } from 'jotai';
import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
export const useCancelLayoutCustomization = () => {
const store = useStore();
const { exitLayoutCustomizationMode } = useExitLayoutCustomizationMode();
const cancel = useCallback(() => {
const activePageLayoutIds = store.get(
activeCustomizationPageLayoutIdsState.atom,
);
for (const pageLayoutId of activePageLayoutIds) {
const persisted = store.get(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (isDefined(persisted)) {
store.set(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
{
id: persisted.id,
name: persisted.name,
type: persisted.type,
objectMetadataId: persisted.objectMetadataId,
tabs: persisted.tabs,
defaultTabToFocusOnMobileAndSidePanelId:
persisted.defaultTabToFocusOnMobileAndSidePanelId,
} satisfies DraftPageLayout,
);
store.set(
pageLayoutCurrentLayoutsComponentState.atomFamily({
instanceId: pageLayoutId,
}),
convertPageLayoutToTabLayouts(persisted),
);
}
const fieldsWidgetGroupsPersisted = store.get(
fieldsWidgetGroupsPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
store.set(
fieldsWidgetGroupsDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
fieldsWidgetGroupsPersisted,
);
const fieldsWidgetUngroupedFieldsPersisted = store.get(
fieldsWidgetUngroupedFieldsPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
store.set(
fieldsWidgetUngroupedFieldsDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
fieldsWidgetUngroupedFieldsPersisted,
);
const fieldsWidgetEditorModePersisted = store.get(
fieldsWidgetEditorModePersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
store.set(
fieldsWidgetEditorModeDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
fieldsWidgetEditorModePersisted,
);
}
exitLayoutCustomizationMode();
}, [store, exitLayoutCustomizationMode]);
return { cancel };
};
@@ -0,0 +1,35 @@
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
import { useStore } from 'jotai';
import { useCallback } from 'react';
export const useEnterLayoutCustomizationMode = () => {
const store = useStore();
const enterLayoutCustomizationMode = useCallback(() => {
const isLayoutCustomizationModeAlreadyEnabled = store.get(
isLayoutCustomizationModeEnabledState.atom,
);
if (isLayoutCustomizationModeAlreadyEnabled) {
return;
}
const prefetchNavigationMenuItems = store.get(
navigationMenuItemsSelector.atom,
);
const workspaceNavigationMenuItems = filterWorkspaceNavigationMenuItems(
prefetchNavigationMenuItems,
);
store.set(navigationMenuItemsDraftState.atom, workspaceNavigationMenuItems);
store.set(activeCustomizationPageLayoutIdsState.atom, []);
store.set(isLayoutCustomizationModeEnabledState.atom, true);
}, [store]);
return { enterLayoutCustomizationMode };
};
@@ -0,0 +1,42 @@
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { isLayoutCustomizationModeEnabledState } from '@/layout-customization/states/isLayoutCustomizationModeEnabledState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { selectedNavigationMenuItemInEditModeState } from '@/navigation-menu-item/common/states/selectedNavigationMenuItemInEditModeState';
import { currentPageLayoutIdState } from '@/page-layout/states/currentPageLayoutIdState';
import { useSidePanelMenu } from '@/side-panel/hooks/useSidePanelMenu';
import { useSetAtomState } from '@/ui/utilities/state/jotai/hooks/useSetAtomState';
import { useStore } from 'jotai';
import { useCallback } from 'react';
export const useExitLayoutCustomizationMode = () => {
const store = useStore();
const { closeSidePanelMenu } = useSidePanelMenu();
const setNavigationMenuItemsDraft = useSetAtomState(
navigationMenuItemsDraftState,
);
const setSelectedNavigationMenuItemInEditMode = useSetAtomState(
selectedNavigationMenuItemInEditModeState,
);
const setIsLayoutCustomizationModeEnabled = useSetAtomState(
isLayoutCustomizationModeEnabledState,
);
const exitLayoutCustomizationMode = useCallback(() => {
setNavigationMenuItemsDraft(null);
setSelectedNavigationMenuItemInEditMode(null);
store.set(currentPageLayoutIdState.atom, null);
store.set(activeCustomizationPageLayoutIdsState.atom, []);
setIsLayoutCustomizationModeEnabled(false);
closeSidePanelMenu();
}, [
setNavigationMenuItemsDraft,
setSelectedNavigationMenuItemInEditMode,
setIsLayoutCustomizationModeEnabled,
closeSidePanelMenu,
store,
]);
return { exitLayoutCustomizationMode };
};
@@ -0,0 +1,97 @@
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout';
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/edit/hooks/useNavigationMenuItemsDraftState';
import { fieldsWidgetGroupsDraftComponentState } from '@/page-layout/states/fieldsWidgetGroupsDraftComponentState';
import { fieldsWidgetGroupsPersistedComponentState } from '@/page-layout/states/fieldsWidgetGroupsPersistedComponentState';
import { fieldsWidgetUngroupedFieldsDraftComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsDraftComponentState';
import { fieldsWidgetUngroupedFieldsPersistedComponentState } from '@/page-layout/states/fieldsWidgetUngroupedFieldsPersistedComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { atom, useAtomValue } from 'jotai';
import { useMemo } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
export const useIsLayoutCustomizationDirty = () => {
const { isDirty: isNavigationDirty } = useNavigationMenuItemsDraftState();
const isAnyPageLayoutDirtyAtom = useMemo(
() =>
atom((get) => {
const activePageLayoutIds = get(
activeCustomizationPageLayoutIdsState.atom,
);
for (const pageLayoutId of activePageLayoutIds) {
const draft = get(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const persisted = get(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (!isDefined(draft) || !isDefined(persisted)) {
continue;
}
const persistedAsDraft: DraftPageLayout = {
id: persisted.id,
name: persisted.name,
type: persisted.type,
objectMetadataId: persisted.objectMetadataId,
tabs: persisted.tabs,
defaultTabToFocusOnMobileAndSidePanelId:
persisted.defaultTabToFocusOnMobileAndSidePanelId,
};
if (!isDeeplyEqual(draft, persistedAsDraft)) {
return true;
}
const fieldsWidgetGroupsDraft = get(
fieldsWidgetGroupsDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const fieldsWidgetGroupsPersisted = get(
fieldsWidgetGroupsPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (
!isDeeplyEqual(fieldsWidgetGroupsDraft, fieldsWidgetGroupsPersisted)
) {
return true;
}
const ungroupedFieldsDraft = get(
fieldsWidgetUngroupedFieldsDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const ungroupedFieldsPersisted = get(
fieldsWidgetUngroupedFieldsPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (!isDeeplyEqual(ungroupedFieldsDraft, ungroupedFieldsPersisted)) {
return true;
}
}
return false;
}),
[],
);
const isAnyPageLayoutDirty = useAtomValue(isAnyPageLayoutDirtyAtom);
return { isDirty: isNavigationDirty || isAnyPageLayoutDirty };
};
@@ -0,0 +1,167 @@
import { useExitLayoutCustomizationMode } from '@/layout-customization/hooks/useExitLayoutCustomizationMode';
import { activeCustomizationPageLayoutIdsState } from '@/layout-customization/states/activeCustomizationPageLayoutIdsState';
import { useSaveNavigationMenuItemsDraft } from '@/navigation-menu-item/edit/hooks/useSaveNavigationMenuItemsDraft';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { navigationMenuItemsSelector } from '@/navigation-menu-item/common/states/navigationMenuItemsSelector';
import { filterWorkspaceNavigationMenuItems } from '@/navigation-menu-item/common/utils/filterWorkspaceNavigationMenuItems';
import { useSaveFieldsWidgetGroups } from '@/page-layout/hooks/useSaveFieldsWidgetGroups';
import { useUpdatePageLayoutWithTabsAndWidgets } from '@/page-layout/hooks/useUpdatePageLayoutWithTabsAndWidgets';
import { pageLayoutCurrentLayoutsComponentState } from '@/page-layout/states/pageLayoutCurrentLayoutsComponentState';
import { pageLayoutDraftComponentState } from '@/page-layout/states/pageLayoutDraftComponentState';
import { pageLayoutPersistedComponentState } from '@/page-layout/states/pageLayoutPersistedComponentState';
import { type DraftPageLayout } from '@/page-layout/types/DraftPageLayout';
import { type PageLayout } from '@/page-layout/types/PageLayout';
import { convertPageLayoutDraftToUpdateInput } from '@/page-layout/utils/convertPageLayoutDraftToUpdateInput';
import { convertPageLayoutToTabLayouts } from '@/page-layout/utils/convertPageLayoutToTabLayouts';
import { reInjectDynamicRelationWidgetsFromDraft } from '@/page-layout/utils/reInjectDynamicRelationWidgetsFromDraft';
import { transformPageLayout } from '@/page-layout/utils/transformPageLayout';
import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar';
import { useLingui } from '@lingui/react/macro';
import { useStore } from 'jotai';
import { useCallback, useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { PageLayoutType } from '~/generated-metadata/graphql';
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
import { logError } from '~/utils/logError';
export const useSaveLayoutCustomization = () => {
const [isSaving, setIsSaving] = useState(false);
const store = useStore();
const { t } = useLingui();
const { saveDraft } = useSaveNavigationMenuItemsDraft();
const { enqueueErrorSnackBar } = useSnackBar();
const { updatePageLayoutWithTabsAndWidgets } =
useUpdatePageLayoutWithTabsAndWidgets();
const { exitLayoutCustomizationMode } = useExitLayoutCustomizationMode();
const { saveFieldsWidgetGroups } = useSaveFieldsWidgetGroups();
const save = useCallback(async () => {
setIsSaving(true);
try {
const navigationDraft = store.get(navigationMenuItemsDraftState.atom);
const prefetchItems = store.get(navigationMenuItemsSelector.atom);
const workspaceItems = filterWorkspaceNavigationMenuItems(prefetchItems);
const isNavigationDirty =
isDefined(navigationDraft) &&
!isDeeplyEqual(navigationDraft, workspaceItems);
// TODO: consider a single server mutation (e.g. saveLayoutCustomization)
// that saves navigation + page layouts + field widgets in one transaction.
// Currently, partial failure leaves mixed state — navigation may commit
// while page layouts fail.
if (isNavigationDirty) {
await saveDraft();
}
const activePageLayoutIds = store.get(
activeCustomizationPageLayoutIdsState.atom,
);
let hasAnyFailure = false;
for (const pageLayoutId of activePageLayoutIds) {
const draft = store.get(
pageLayoutDraftComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
const persisted = store.get(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
);
if (!isDefined(draft) || !isDefined(persisted)) {
continue;
}
const persistedAsDraft: DraftPageLayout = {
id: persisted.id,
name: persisted.name,
type: persisted.type,
objectMetadataId: persisted.objectMetadataId,
tabs: persisted.tabs,
defaultTabToFocusOnMobileAndSidePanelId:
persisted.defaultTabToFocusOnMobileAndSidePanelId,
};
const isPageLayoutStructureDirty = !isDeeplyEqual(
draft,
persistedAsDraft,
);
if (isPageLayoutStructureDirty) {
const updateInput = convertPageLayoutDraftToUpdateInput(draft);
const result = await updatePageLayoutWithTabsAndWidgets(
pageLayoutId,
updateInput,
);
if (result.status === 'successful') {
const updatedPageLayout =
result.response.data?.updatePageLayoutWithTabsAndWidgets;
if (isDefined(updatedPageLayout)) {
const persistedLayout: PageLayout =
transformPageLayout(updatedPageLayout);
const pageLayoutToPersist =
persistedLayout.type === PageLayoutType.RECORD_PAGE
? reInjectDynamicRelationWidgetsFromDraft(
persistedLayout,
draft,
)
: persistedLayout;
store.set(
pageLayoutPersistedComponentState.atomFamily({
instanceId: pageLayoutId,
}),
pageLayoutToPersist,
);
store.set(
pageLayoutCurrentLayoutsComponentState.atomFamily({
instanceId: pageLayoutId,
}),
convertPageLayoutToTabLayouts(pageLayoutToPersist),
);
}
} else {
// goes away with a single server mutation (see TODO above)
hasAnyFailure = true;
continue;
}
}
await saveFieldsWidgetGroups(pageLayoutId);
}
if (hasAnyFailure) {
enqueueErrorSnackBar({
message: t`Some layout changes could not be saved`,
});
return;
}
exitLayoutCustomizationMode();
} catch (error) {
logError(error);
enqueueErrorSnackBar({
message: t`Failed to save layout customization`,
});
} finally {
setIsSaving(false);
}
}, [
saveDraft,
updatePageLayoutWithTabsAndWidgets,
saveFieldsWidgetGroups,
exitLayoutCustomizationMode,
enqueueErrorSnackBar,
store,
t,
]);
return { save, isSaving };
};
@@ -0,0 +1,6 @@
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
export const activeCustomizationPageLayoutIdsState = createAtomState<string[]>({
key: 'activeCustomizationPageLayoutIdsState',
defaultValue: [],
});
@@ -0,0 +1,6 @@
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
export const isLayoutCustomizationModeEnabledState = createAtomState<boolean>({
key: 'isLayoutCustomizationModeEnabledState',
defaultValue: false,
});
@@ -1,6 +0,0 @@
import { createAtomState } from '@/ui/utilities/state/jotai/utils/createAtomState';
export const isNavigationMenuInEditModeState = createAtomState<boolean>({
key: 'isNavigationMenuInEditModeState',
defaultValue: false,
});
@@ -4,6 +4,7 @@ import { useCallback } from 'react';
import { isDefined } from 'twenty-shared/utils';
import { IconFolder, IconLink, useIcons } from 'twenty-ui/display';
import { useEnterLayoutCustomizationMode } from '@/layout-customization/hooks/useEnterLayoutCustomizationMode';
import { ADD_TO_NAV_SOURCE_DROPPABLE_ID } from '@/navigation-menu-item/common/constants/AddToNavSourceDroppableId';
import { NavigationMenuItemType } from 'twenty-shared/types';
import { useAddFolderToNavigationMenuDraft } from '@/navigation-menu-item/edit/folder/hooks/useAddFolderToNavigationMenuDraft';
@@ -14,7 +15,6 @@ import { useAddViewToNavigationMenuDraft } from '@/navigation-menu-item/edit/vie
import { useNavigationMenuItemsDraftState } from '@/navigation-menu-item/edit/hooks/useNavigationMenuItemsDraftState';
import { useOpenNavigationMenuItemInSidePanel } from '@/navigation-menu-item/edit/hooks/useOpenNavigationMenuItemInSidePanel';
import { addToNavPayloadRegistryState } from '@/navigation-menu-item/common/states/addToNavPayloadRegistryState';
import { isNavigationMenuInEditModeState } from '@/navigation-menu-item/common/states/isNavigationMenuInEditModeState';
import { navigationMenuItemsDraftState } from '@/navigation-menu-item/common/states/navigationMenuItemsDraftState';
import { openNavigationMenuItemFolderIdsState } from '@/navigation-menu-item/common/states/openNavigationMenuItemFolderIdsState';
import { getObjectMetadataIdsInDraft } from '@/navigation-menu-item/common/utils/getObjectMetadataIdsInDraft';
@@ -43,9 +43,7 @@ export const useHandleAddToNavigationDrop = () => {
const { objectMetadataItems } = useObjectMetadataItems();
const views = useAtomStateValue(viewsSelector);
const { getIcon } = useIcons();
const setIsNavigationMenuInEditMode = useSetAtomState(
isNavigationMenuInEditModeState,
);
const { enterLayoutCustomizationMode } = useEnterLayoutCustomizationMode();
const setOpenNavigationMenuItemFolderIds = useSetAtomState(
openNavigationMenuItemFolderIdsState,
);
@@ -92,7 +90,7 @@ export const useHandleAddToNavigationDrop = () => {
'itemId'
>,
) => {
setIsNavigationMenuInEditMode(true);
enterLayoutCustomizationMode();
openNavigationMenuItemInSidePanel({ ...options, itemId: newItemId });
};
@@ -216,7 +214,7 @@ export const useHandleAddToNavigationDrop = () => {
objectMetadataItems,
openNavigationMenuItemInSidePanel,
setOpenNavigationMenuItemFolderIds,
setIsNavigationMenuInEditMode,
enterLayoutCustomizationMode,
workspaceNavigationMenuItems,
store,
],

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