diff --git a/packages/api-form-builder/src/index.ts b/packages/api-form-builder/src/index.ts index 53cc151ac07..6e6dffa5441 100644 --- a/packages/api-form-builder/src/index.ts +++ b/packages/api-form-builder/src/index.ts @@ -1,6 +1,7 @@ import createCruds from "./plugins/crud"; import graphql from "./plugins/graphql"; import triggerHandlers from "./plugins/triggers"; +import settings from "./plugins/settings"; import validators from "./plugins/validators"; import formsGraphQL from "./plugins/graphql/form"; import formSettingsGraphQL from "./plugins/graphql/formSettings"; @@ -19,6 +20,7 @@ export const createFormBuilder = (params: CreateFormBuilderParams) => { validators, formsGraphQL, formSettingsGraphQL, - formBuilderPrerenderingPlugins() + formBuilderPrerenderingPlugins(), + settings ]; }; diff --git a/packages/api-form-builder/src/plugins/crud/settings.crud.ts b/packages/api-form-builder/src/plugins/crud/settings.crud.ts index d0999ec84ca..96de28eeec8 100644 --- a/packages/api-form-builder/src/plugins/crud/settings.crud.ts +++ b/packages/api-form-builder/src/plugins/crud/settings.crud.ts @@ -60,7 +60,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCR onSettingsBeforeDelete, onSettingsAfterDelete, async getSettings(this: FormBuilder, params) { - const { auth, throwOnNotFound } = params || {}; + const { auth, throwOnNotFound, locale } = params || {}; if (auth !== false) { await settingsPermissions.ensure(); @@ -70,7 +70,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCR try { settings = await this.storageOperations.getSettings({ tenant: getTenant().id, - locale: getLocale().code + locale: locale || getLocale().code }); } catch (ex) { throw new WebinyError( @@ -89,7 +89,11 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCR const data = await formBuilderSettings.toJSON(); - const original = await this.getSettings({ auth: false }); + const original = await this.getSettings({ + auth: false, + locale: input.locale + }); + if (original) { throw new WebinyError( `"Form Builder" settings already exist.`, @@ -106,7 +110,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCR domain: data.domain, reCaptcha: data.reCaptcha, tenant: getTenant().id, - locale: getLocale().code + locale: input.locale || getLocale().code }; try { await onSettingsBeforeCreate.publish({ @@ -186,10 +190,11 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCR ); } }, - async deleteSettings(this: FormBuilder) { + async deleteSettings(this: FormBuilder, params) { + const { locale } = params || {}; await settingsPermissions.ensure(); - const settings = await this.getSettings(); + const settings = await this.getSettings({ locale }); if (!settings) { return; } diff --git a/packages/api-form-builder/src/plugins/settings/createSettingsForNewLocale.ts b/packages/api-form-builder/src/plugins/settings/createSettingsForNewLocale.ts new file mode 100644 index 00000000000..3b41790b735 --- /dev/null +++ b/packages/api-form-builder/src/plugins/settings/createSettingsForNewLocale.ts @@ -0,0 +1,12 @@ +import { ContextPlugin } from "@webiny/api"; +import { FormBuilderContext } from "~/types"; + +export const createSettingsForNewLocale = new ContextPlugin(context => { + context.i18n.locales.onLocaleAfterCreate.subscribe(async ({ locale }) => { + const currentSettings = await context.formBuilder.getSettings({ + auth: false + }); + + await context.formBuilder.createSettings({ ...currentSettings, locale: locale.code }); + }); +}); diff --git a/packages/api-form-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts b/packages/api-form-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts new file mode 100644 index 00000000000..9e9542aeeb6 --- /dev/null +++ b/packages/api-form-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts @@ -0,0 +1,8 @@ +import { ContextPlugin } from "@webiny/api"; +import { FormBuilderContext } from "~/types"; + +export const deleteSettingsForDeletedLocale = new ContextPlugin(context => { + context.i18n.locales.onLocaleAfterDelete.subscribe(async ({ locale }) => { + await context.formBuilder.deleteSettings({ locale: locale.code }); + }); +}); diff --git a/packages/api-form-builder/src/plugins/settings/index.ts b/packages/api-form-builder/src/plugins/settings/index.ts new file mode 100644 index 00000000000..899f552eb2e --- /dev/null +++ b/packages/api-form-builder/src/plugins/settings/index.ts @@ -0,0 +1,4 @@ +import { createSettingsForNewLocale } from "./createSettingsForNewLocale"; +import { deleteSettingsForDeletedLocale } from "./deleteSettingsForDeletedLocale"; + +export default [createSettingsForNewLocale, deleteSettingsForDeletedLocale]; diff --git a/packages/api-form-builder/src/types.ts b/packages/api-form-builder/src/types.ts index 8d69c9ba340..66c8b9acb49 100644 --- a/packages/api-form-builder/src/types.ts +++ b/packages/api-form-builder/src/types.ts @@ -385,6 +385,11 @@ export interface Settings { export interface SettingsCRUDGetParams { auth?: boolean; throwOnNotFound?: boolean; + locale?: string; +} + +export interface SettingsCRUDDeleteParams { + locale?: string; } /** @@ -415,7 +420,7 @@ export interface SettingsCRUD { getSettings(params?: SettingsCRUDGetParams): Promise; createSettings(data: Partial): Promise; updateSettings(data: Partial): Promise; - deleteSettings(): Promise; + deleteSettings(params?: SettingsCRUDDeleteParams): Promise; /** * Lifecycle Events */ diff --git a/packages/api-page-builder-so-ddb-es/src/operations/settings/index.ts b/packages/api-page-builder-so-ddb-es/src/operations/settings/index.ts index 8fbbf736304..92532366562 100644 --- a/packages/api-page-builder-so-ddb-es/src/operations/settings/index.ts +++ b/packages/api-page-builder-so-ddb-es/src/operations/settings/index.ts @@ -5,7 +5,8 @@ import { SettingsStorageOperations, SettingsStorageOperationsCreateParams, SettingsStorageOperationsGetParams, - SettingsStorageOperationsUpdateParams + SettingsStorageOperationsUpdateParams, + SettingsStorageOperationsDeleteParams } from "@webiny/api-page-builder/types"; import { Entity } from "@webiny/db-dynamodb/toolbox"; import { getClean } from "@webiny/db-dynamodb/utils/get"; @@ -17,8 +18,8 @@ import { put } from "@webiny/db-dynamodb"; * it in consideration and create the partition key for the global settings. */ interface PartitionKeyParams { - tenant: string | boolean | undefined; - locale: string | boolean | undefined; + tenant?: string | boolean; + locale?: string | boolean; } const createPartitionKey = (params: PartitionKeyParams): string => { @@ -165,6 +166,25 @@ export const createSettingsStorageOperations = ({ ); } }; + + const deleteSettings = async (params: SettingsStorageOperationsDeleteParams) => { + const { settings } = params; + const keys = { + PK: createPartitionKey(settings), + SK: "A" + }; + try { + await entity.delete(keys); + } catch (ex) { + throw new WebinyError( + ex.message || "Could not delete the settings record by given keys.", + ex.code || "DELETE_SETTINGS_ERROR", + { + keys + } + ); + } + }; /** * We can simply return the partition key for this storage operations. */ @@ -177,6 +197,7 @@ export const createSettingsStorageOperations = ({ getDefaults, create, update, + delete: deleteSettings, createCacheKey }; }; diff --git a/packages/api-page-builder-so-ddb/src/operations/settings/index.ts b/packages/api-page-builder-so-ddb/src/operations/settings/index.ts index f98feb84edd..6e5536a187d 100644 --- a/packages/api-page-builder-so-ddb/src/operations/settings/index.ts +++ b/packages/api-page-builder-so-ddb/src/operations/settings/index.ts @@ -5,7 +5,8 @@ import { SettingsStorageOperations, SettingsStorageOperationsCreateParams, SettingsStorageOperationsGetParams, - SettingsStorageOperationsUpdateParams + SettingsStorageOperationsUpdateParams, + SettingsStorageOperationsDeleteParams } from "@webiny/api-page-builder/types"; import { Entity } from "@webiny/db-dynamodb/toolbox"; import { getClean } from "@webiny/db-dynamodb/utils/get"; @@ -17,8 +18,8 @@ import { put } from "@webiny/db-dynamodb"; * it in consideration and create the partition key for the global settings. */ interface PartitionKeyParams { - tenant: string | boolean | undefined; - locale: string | boolean | undefined; + tenant?: string | boolean; + locale?: string | boolean; } const createPartitionKey = (params: PartitionKeyParams): string => { @@ -162,6 +163,25 @@ export const createSettingsStorageOperations = ({ ); } }; + + const deleteSettings = async (params: SettingsStorageOperationsDeleteParams) => { + const { settings } = params; + const keys = { + PK: createPartitionKey(settings), + SK: "A" + }; + try { + await entity.delete(keys); + } catch (ex) { + throw new WebinyError( + ex.message || "Could not delete the settings record by given keys.", + ex.code || "DELETE_SETTINGS_ERROR", + { + keys + } + ); + } + }; /** * We can simply return the partition key for this storage operations. */ @@ -174,6 +194,7 @@ export const createSettingsStorageOperations = ({ getDefaults, create, update, + delete: deleteSettings, createCacheKey }; }; diff --git a/packages/api-page-builder/src/graphql/crud.ts b/packages/api-page-builder/src/graphql/crud.ts index 2444bce6b75..e124bde2250 100644 --- a/packages/api-page-builder/src/graphql/crud.ts +++ b/packages/api-page-builder/src/graphql/crud.ts @@ -120,6 +120,12 @@ const setup = (params: CreateCrudParams) => { fullAccessPermissionName: "pb.*" }); + const settingsPermissions = new PageTemplatesPermissions({ + getIdentity: context.security.getIdentity, + getPermissions: () => context.security.getPermissions("pb.settings"), + fullAccessPermissionName: "pb.*" + }); + const system = await createSystemCrud({ context, storageOperations, @@ -129,6 +135,7 @@ const setup = (params: CreateCrudParams) => { const settings = createSettingsCrud({ context, storageOperations, + settingsPermissions, getTenantId, getLocaleCode }); diff --git a/packages/api-page-builder/src/graphql/crud/categories.crud.ts b/packages/api-page-builder/src/graphql/crud/categories.crud.ts index 7f0edaf50a1..220d683be72 100644 --- a/packages/api-page-builder/src/graphql/crud/categories.crud.ts +++ b/packages/api-page-builder/src/graphql/crud/categories.crud.ts @@ -85,13 +85,13 @@ export const createCategoriesCrud = (params: CreateCategoriesCrudParams): Catego * This method should return category or null. No error throwing on not found. */ async getCategory(slug, options = { auth: true }) { - const { auth } = options; + const { auth, locale } = options; const params: CategoryStorageOperationsGetParams = { where: { slug, tenant: getTenantId(), - locale: getLocaleCode() + locale: locale || getLocaleCode() } }; @@ -177,7 +177,8 @@ export const createCategoriesCrud = (params: CreateCategoriesCrudParams): Catego } const existingCategory = await this.getCategory(input.slug, { - auth: false + auth: false, + locale: input.locale }); if (existingCategory) { throw new NotFoundError(`Category with slug "${input.slug}" already exists.`); @@ -196,7 +197,7 @@ export const createCategoriesCrud = (params: CreateCategoriesCrudParams): Catego displayName: identity.displayName }, tenant: getTenantId(), - locale: getLocaleCode() + locale: input.locale || getLocaleCode() }; try { diff --git a/packages/api-page-builder/src/graphql/crud/settings.crud.ts b/packages/api-page-builder/src/graphql/crud/settings.crud.ts index ec486d034da..b3a668fae20 100644 --- a/packages/api-page-builder/src/graphql/crud/settings.crud.ts +++ b/packages/api-page-builder/src/graphql/crud/settings.crud.ts @@ -1,6 +1,8 @@ import { OnSettingsAfterUpdateTopicParams, OnSettingsBeforeUpdateTopicParams, + OnSettingsAfterDeleteTopicParams, + OnSettingsBeforeDeleteTopicParams, PageBuilderContextObject, PageBuilderStorageOperations, PageSpecialType, @@ -17,6 +19,7 @@ import lodashGet from "lodash/get"; import DataLoader from "dataloader"; import { createTopic } from "@webiny/pubsub"; import { createSettingsCreateValidation } from "~/graphql/crud/settings/validation"; +import { PageTemplatesPermissions } from "~/graphql/crud/permissions/PageTemplatesPermissions"; import { createZodError, removeUndefinedValues } from "@webiny/utils"; interface SettingsParams { @@ -35,12 +38,13 @@ enum SETTINGS_TYPE { export interface CreateSettingsCrudParams { context: PbContext; storageOperations: PageBuilderStorageOperations; + settingsPermissions: PageTemplatesPermissions; getTenantId: () => string; getLocaleCode: () => string; } export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCrud => { - const { storageOperations, getLocaleCode, getTenantId } = params; + const { storageOperations, settingsPermissions, getLocaleCode, getTenantId } = params; const settingsDataLoader = new DataLoader( async keys => { @@ -57,6 +61,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr } ); + // create const onSettingsBeforeUpdate = createTopic( "pageBuilder.onSettingsBeforeUpdate" ); @@ -64,6 +69,14 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr "pageBuilder.onSettingsAfterUpdate" ); + // delete + const onSettingsBeforeDelete = createTopic( + "pageBuilder.onSettingsBeforeDelete" + ); + const onSettingsAfterDelete = createTopic( + "pageBuilder.onSettingsAfterDelete" + ); + return { /** * Lifecycle events - deprecated in 5.34.0 - will be removed in 5.36.0 @@ -75,6 +88,8 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr */ onSettingsBeforeUpdate, onSettingsAfterUpdate, + onSettingsBeforeDelete, + onSettingsAfterDelete, async getCurrentSettings(this: PageBuilderContextObject) { // With this line commented, we made this endpoint public. // We did this because of the public website pages which need to access the settings. @@ -91,7 +106,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr } }); }, - async getSettings(this: PageBuilderContextObject) { + async getSettings(this: PageBuilderContextObject, options) { // With this line commented, we made this endpoint public. // We did this because of the public website pages which need to access the settings. // It's possible we'll create another GraphQL field, made for this exact purpose. @@ -99,7 +114,7 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr const key = { tenant: getTenantId(), - locale: getLocaleCode() + locale: options?.locale || getLocaleCode() }; try { @@ -119,11 +134,11 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr async updateSettings(this: PageBuilderContextObject, input) { const params = { tenant: getTenantId(), - locale: getLocaleCode(), + locale: input.locale || getLocaleCode(), type: SETTINGS_TYPE.DEFAULT }; - let original = await this.getSettings(); + let original = await this.getSettings({ locale: params.locale }); if (!original) { const data: SettingsStorageOperationsCreateParams = { input: input, @@ -247,6 +262,31 @@ export const createSettingsCrud = (params: CreateSettingsCrudParams): SettingsCr } ); } + }, + async deleteSettings(this: PageBuilderContextObject, params) { + const { locale } = params || {}; + await settingsPermissions.ensure({ rwd: "d" }); + + const settings = await this.getSettings({ locale }); + if (!settings) { + return; + } + try { + await onSettingsBeforeDelete.publish({ + settings + }); + + await storageOperations.settings.delete({ settings }); + + await onSettingsAfterDelete.publish({ + settings + }); + } catch (ex) { + throw new WebinyError( + ex.message || "Could not delete settings.", + ex.code || "DELETE_SETTINGS_ERROR" + ); + } } }; }; diff --git a/packages/api-page-builder/src/graphql/index.ts b/packages/api-page-builder/src/graphql/index.ts index 559cb76e829..2bd9065ccb8 100644 --- a/packages/api-page-builder/src/graphql/index.ts +++ b/packages/api-page-builder/src/graphql/index.ts @@ -4,6 +4,7 @@ import { GraphQLSchemaPlugin } from "@webiny/handler-graphql/types"; import { createCrud, CreateCrudParams } from "./crud"; import graphql from "./graphql"; import { createElementProcessors } from "~/graphql/elementProcessors"; +import settingsPlugins from "~/plugins/settings"; export const createPageBuilderGraphQL = (): GraphQLSchemaPlugin[] => { return graphql(); @@ -11,5 +12,5 @@ export const createPageBuilderGraphQL = (): GraphQLSchemaPlugin[] => { export type ContextParams = CreateCrudParams; export const createPageBuilderContext = (params: ContextParams) => { - return [createCrud(params), createElementProcessors()]; + return [createCrud(params), createElementProcessors(), settingsPlugins]; }; diff --git a/packages/api-page-builder/src/graphql/types.ts b/packages/api-page-builder/src/graphql/types.ts index 8a88285d8c1..e4e7df52397 100644 --- a/packages/api-page-builder/src/graphql/types.ts +++ b/packages/api-page-builder/src/graphql/types.ts @@ -397,7 +397,10 @@ export interface OnCategoryAfterDeleteTopicParams { * @category Categories */ export interface CategoriesCrud { - getCategory(slug: string, options?: { auth: boolean }): Promise; + getCategory( + slug: string, + options?: { auth: boolean; locale?: string } + ): Promise; listCategories(): Promise; createCategory(data: PbCategoryInput): Promise; updateCategory(slug: string, data: PbCategoryInput): Promise; @@ -550,8 +553,8 @@ export interface MenusCrud { * The options passed into the crud methods */ export interface DefaultSettingsCrudOptions { - tenant: string | false | undefined; - locale: string | false; + tenant?: string | false; + locale?: string | false; } export interface SettingsUpdateTopicMetaParams { @@ -575,6 +578,18 @@ export interface OnSettingsAfterUpdateTopicParams { settings: Settings; meta: SettingsUpdateTopicMetaParams; } +/** + * @category Lifecycle events + */ +export interface OnSettingsBeforeDeleteTopicParams { + settings: Settings; +} +/** + * @category Lifecycle events + */ +export interface OnSettingsAfterDeleteTopicParams { + settings: Settings; +} /** * @category Settings @@ -589,6 +604,7 @@ export interface SettingsCrud { data: Record, options?: { auth?: boolean } & DefaultSettingsCrudOptions ) => Promise; + deleteSettings(params?: DefaultSettingsCrudOptions): Promise; /** * Lifecycle events - deprecated in 5.34.0 - will be removed in 5.36.0 */ @@ -605,6 +621,8 @@ export interface SettingsCrud { */ onSettingsBeforeUpdate: Topic; onSettingsAfterUpdate: Topic; + onSettingsBeforeDelete: Topic; + onSettingsAfterDelete: Topic; } /** @@ -1025,6 +1043,7 @@ export interface PbCategoryInput { slug: string; url: string; layout: string; + locale?: string; } export interface PbUpdatePageInput { diff --git a/packages/api-page-builder/src/plugins/index.ts b/packages/api-page-builder/src/plugins/index.ts index aaffe3bd236..346b005c360 100644 --- a/packages/api-page-builder/src/plugins/index.ts +++ b/packages/api-page-builder/src/plugins/index.ts @@ -1,3 +1,4 @@ +export * from "./settings"; export * from "./ContentCompressionPlugin"; export * from "./JsonpackContentCompressionPlugin"; export * from "./PageBuilderPageValidationModifierPlugin"; diff --git a/packages/api-page-builder/src/plugins/settings/createInitialPageCategoryForNewLocale.ts b/packages/api-page-builder/src/plugins/settings/createInitialPageCategoryForNewLocale.ts new file mode 100644 index 00000000000..0a09d18840d --- /dev/null +++ b/packages/api-page-builder/src/plugins/settings/createInitialPageCategoryForNewLocale.ts @@ -0,0 +1,16 @@ +import { ContextPlugin } from "@webiny/api"; +import { PbContext } from "~/types"; + +export const createInitialPageCategoryForNewLocale = new ContextPlugin(context => { + context.i18n.locales.onLocaleAfterCreate.subscribe(async ({ locale }) => { + try { + await context.pageBuilder.createCategory({ + name: "Static", + slug: "static", + url: "/static/", + layout: "static", + locale: locale.code + }); + } catch {} + }); +}); diff --git a/packages/api-page-builder/src/plugins/settings/createSettingsForNewLocale.ts b/packages/api-page-builder/src/plugins/settings/createSettingsForNewLocale.ts new file mode 100644 index 00000000000..6cb650be3fb --- /dev/null +++ b/packages/api-page-builder/src/plugins/settings/createSettingsForNewLocale.ts @@ -0,0 +1,22 @@ +import { ContextPlugin } from "@webiny/api"; +import { PbContext } from "~/types"; + +export const createSettingsForNewLocale = new ContextPlugin(context => { + context.i18n.locales.onLocaleAfterCreate.subscribe(async ({ locale }) => { + const existingSettings = await context.pageBuilder.getSettings({ + locale: locale.code + }); + + if (!existingSettings) { + const currentLocaleSettings = await context.pageBuilder.getSettings(); + await context.pageBuilder.updateSettings({ + ...currentLocaleSettings, + pages: { + home: null, + notFound: null + }, + locale: locale.code + }); + } + }); +}); diff --git a/packages/api-page-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts b/packages/api-page-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts new file mode 100644 index 00000000000..adde462f52d --- /dev/null +++ b/packages/api-page-builder/src/plugins/settings/deleteSettingsForDeletedLocale.ts @@ -0,0 +1,8 @@ +import { ContextPlugin } from "@webiny/api"; +import { PbContext } from "~/types"; + +export const deleteSettingsForDeletedLocale = new ContextPlugin(context => { + context.i18n.locales.onLocaleAfterDelete.subscribe(async ({ locale }) => { + await context.pageBuilder.deleteSettings({ locale: locale.code }); + }); +}); diff --git a/packages/api-page-builder/src/plugins/settings/index.ts b/packages/api-page-builder/src/plugins/settings/index.ts new file mode 100644 index 00000000000..675200a70c8 --- /dev/null +++ b/packages/api-page-builder/src/plugins/settings/index.ts @@ -0,0 +1,9 @@ +import { createInitialPageCategoryForNewLocale } from "./createInitialPageCategoryForNewLocale"; +import { createSettingsForNewLocale } from "./createSettingsForNewLocale"; +import { deleteSettingsForDeletedLocale } from "./deleteSettingsForDeletedLocale"; + +export default [ + createInitialPageCategoryForNewLocale, + createSettingsForNewLocale, + deleteSettingsForDeletedLocale +]; diff --git a/packages/api-page-builder/src/types.ts b/packages/api-page-builder/src/types.ts index a3cbb40550e..48814b73db5 100644 --- a/packages/api-page-builder/src/types.ts +++ b/packages/api-page-builder/src/types.ts @@ -453,6 +453,13 @@ export interface SettingsStorageOperationsUpdateParams { original: Settings; settings: Settings; } +/** + * @category StorageOperations + * @category SettingsStorageOperations + */ +export interface SettingsStorageOperationsDeleteParams { + settings: Settings; +} /** * @category StorageOperations * @category SettingsStorageOperations @@ -468,6 +475,7 @@ export interface SettingsStorageOperations { getDefaults: () => Promise; create: (params: SettingsStorageOperationsCreateParams) => Promise; update: (params: SettingsStorageOperationsUpdateParams) => Promise; + delete: (params: SettingsStorageOperationsDeleteParams) => Promise; } /** diff --git a/packages/app-i18n/src/admin/views/locales/hooks/useLocaleForm.ts b/packages/app-i18n/src/admin/views/locales/hooks/useLocaleForm.ts index db833ea6726..847e4d1a961 100644 --- a/packages/app-i18n/src/admin/views/locales/hooks/useLocaleForm.ts +++ b/packages/app-i18n/src/admin/views/locales/hooks/useLocaleForm.ts @@ -65,7 +65,7 @@ export const useLocaleForm = (): UseLocaleForm => { !isUpdate && history.push(`/i18n/locales?code=${data.code}`); showSnackbar(t`Locale saved successfully.`); - refetchLocales(); + await refetchLocales(); // Reload page window.location.reload(); },