feat(): Translation settings + user configuration + admin hook and js sdk + dashboard (#14355)
* feat(): Translation settings + user configuration * feat(): Translation settings + user configuration * Create gentle-bees-grow.md * add entities end point * add entities end point * add admin hook and js sdk method * update changeset * fix tests * fix tests * rm unnecessary copy * update dashboard to use the new resources * update dashboard to use the new resources * update dashboard to use the new resources * allow type inference through interface augmentation in the defineConfig of medusa-config * allow type inference through interface augmentation in the defineConfig of medusa-config * exclude id and _id props --------- Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
co-authored by
Oli Juhl
parent
797878af26
commit
b21a599d11
@@ -21,13 +21,18 @@ import {
|
||||
} from "@medusajs/framework/utils"
|
||||
import Locale from "@models/locale"
|
||||
import Translation from "@models/translation"
|
||||
import Settings from "@models/settings"
|
||||
import { computeTranslatedFieldCount } from "@utils/compute-translated-field-count"
|
||||
import { TRANSLATABLE_FIELDS_CONFIG_KEY } from "@utils/constants"
|
||||
import { filterTranslationFields } from "@utils/filter-translation-fields"
|
||||
|
||||
type InjectedDependencies = {
|
||||
baseRepository: DAL.RepositoryService
|
||||
translationService: ModulesSdkTypes.IMedusaInternalService<typeof Translation>
|
||||
localeService: ModulesSdkTypes.IMedusaInternalService<typeof Locale>
|
||||
translationSettingsService: ModulesSdkTypes.IMedusaInternalService<
|
||||
typeof Settings
|
||||
>
|
||||
[TRANSLATABLE_FIELDS_CONFIG_KEY]: Record<string, string[]>
|
||||
}
|
||||
|
||||
@@ -39,9 +44,13 @@ export default class TranslationModuleService
|
||||
Translation: {
|
||||
dto: TranslationTypes.TranslationDTO
|
||||
}
|
||||
TranslationSettings: {
|
||||
dto: TranslationTypes.TranslationSettingsDTO
|
||||
}
|
||||
}>({
|
||||
Locale,
|
||||
Translation,
|
||||
TranslationSettings: Settings,
|
||||
})
|
||||
implements ITranslationModuleService
|
||||
{
|
||||
@@ -52,20 +61,38 @@ export default class TranslationModuleService
|
||||
protected localeService_: ModulesSdkTypes.IMedusaInternalService<
|
||||
typeof Locale
|
||||
>
|
||||
|
||||
private readonly translatableFieldsConfig_: Record<string, string[]>
|
||||
protected settingsService_: ModulesSdkTypes.IMedusaInternalService<
|
||||
typeof Settings
|
||||
>
|
||||
|
||||
constructor({
|
||||
baseRepository,
|
||||
translationService,
|
||||
localeService,
|
||||
translatableFieldsConfig,
|
||||
translationSettingsService,
|
||||
}: InjectedDependencies) {
|
||||
super(...arguments)
|
||||
this.baseRepository_ = baseRepository
|
||||
this.translationService_ = translationService
|
||||
this.localeService_ = localeService
|
||||
this.translatableFieldsConfig_ = translatableFieldsConfig
|
||||
this.settingsService_ = translationSettingsService
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
async getTranslatableFields(
|
||||
entityType?: string,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<Record<string, string[]>> {
|
||||
const filters = entityType ? { entity_type: entityType } : {}
|
||||
const settings = await this.settingsService_.list(
|
||||
filters,
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
return settings.reduce((acc, setting) => {
|
||||
acc[setting.entity_type] = setting.fields as unknown as string[]
|
||||
return acc
|
||||
}, {} as Record<string, string[]>)
|
||||
}
|
||||
|
||||
static prepareFilters(
|
||||
@@ -83,6 +110,54 @@ export default class TranslationModuleService
|
||||
return restFilters
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
// @ts-expect-error
|
||||
async retrieveTranslation(
|
||||
id: string,
|
||||
config: FindConfig<TranslationTypes.TranslationDTO> = {},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TranslationTypes.TranslationDTO> {
|
||||
const configWithReference =
|
||||
TranslationModuleService.ensureReferenceFieldInConfig(config)
|
||||
|
||||
const result = await this.translationService_.retrieve(
|
||||
id,
|
||||
configWithReference,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const serialized =
|
||||
await this.baseRepository_.serialize<TranslationTypes.TranslationDTO>(
|
||||
result
|
||||
)
|
||||
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return filterTranslationFields([serialized], translatableFieldsConfig)[0]
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the 'reference' field is included in the select config.
|
||||
* This is needed for filtering translations by translatable fields.
|
||||
*/
|
||||
static ensureReferenceFieldInConfig(
|
||||
config: FindConfig<TranslationTypes.TranslationDTO>
|
||||
): FindConfig<TranslationTypes.TranslationDTO> {
|
||||
if (!config?.select?.length) {
|
||||
return config
|
||||
}
|
||||
|
||||
const select = config.select as string[]
|
||||
if (!select.includes("reference")) {
|
||||
return { ...config, select: [...select, "reference"] }
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
// @ts-expect-error
|
||||
async listTranslations(
|
||||
@@ -91,16 +166,25 @@ export default class TranslationModuleService
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TranslationTypes.TranslationDTO[]> {
|
||||
const preparedFilters = TranslationModuleService.prepareFilters(filters)
|
||||
const configWithReference =
|
||||
TranslationModuleService.ensureReferenceFieldInConfig(config)
|
||||
|
||||
const results = await this.translationService_.list(
|
||||
preparedFilters,
|
||||
config,
|
||||
configWithReference,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return await this.baseRepository_.serialize<
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TranslationTypes.TranslationDTO[]
|
||||
>(results)
|
||||
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return filterTranslationFields(serialized, translatableFieldsConfig)
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
@@ -111,17 +195,26 @@ export default class TranslationModuleService
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<[TranslationTypes.TranslationDTO[], number]> {
|
||||
const preparedFilters = TranslationModuleService.prepareFilters(filters)
|
||||
const configWithReference =
|
||||
TranslationModuleService.ensureReferenceFieldInConfig(config)
|
||||
|
||||
const [results, count] = await this.translationService_.listAndCount(
|
||||
preparedFilters,
|
||||
config,
|
||||
configWithReference,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const serialized = await this.baseRepository_.serialize<
|
||||
TranslationTypes.TranslationDTO[]
|
||||
>(results)
|
||||
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return [
|
||||
await this.baseRepository_.serialize<TranslationTypes.TranslationDTO[]>(
|
||||
results
|
||||
),
|
||||
filterTranslationFields(serialized, translatableFieldsConfig),
|
||||
count,
|
||||
]
|
||||
}
|
||||
@@ -183,12 +276,16 @@ export default class TranslationModuleService
|
||||
TranslationTypes.TranslationDTO | TranslationTypes.TranslationDTO[]
|
||||
> {
|
||||
const dataArray = Array.isArray(data) ? data : [data]
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
const normalizedData = dataArray.map((translation) => ({
|
||||
...translation,
|
||||
locale_code: normalizeLocale(translation.locale_code),
|
||||
translated_field_count: computeTranslatedFieldCount(
|
||||
translation.translations as Record<string, unknown>,
|
||||
this.translatableFieldsConfig_[translation.reference]
|
||||
translatableFieldsConfig[translation.reference]
|
||||
),
|
||||
}))
|
||||
|
||||
@@ -248,6 +345,11 @@ export default class TranslationModuleService
|
||||
)
|
||||
}
|
||||
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
for (const update of dataArray) {
|
||||
if (update.translations) {
|
||||
const reference = update.reference || referenceMap[update.id]
|
||||
@@ -257,7 +359,7 @@ export default class TranslationModuleService
|
||||
}
|
||||
).translated_field_count = computeTranslatedFieldCount(
|
||||
update.translations as Record<string, unknown>,
|
||||
this.translatableFieldsConfig_[reference] || []
|
||||
translatableFieldsConfig[reference] || []
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -275,13 +377,6 @@ export default class TranslationModuleService
|
||||
return Array.isArray(data) ? serialized : serialized[0]
|
||||
}
|
||||
|
||||
getTranslatableFields(entityType?: string): Record<string, string[]> {
|
||||
if (entityType) {
|
||||
return { [entityType]: this.translatableFieldsConfig_[entityType] }
|
||||
}
|
||||
return this.translatableFieldsConfig_
|
||||
}
|
||||
|
||||
@InjectManager()
|
||||
async getStatistics(
|
||||
input: TranslationTypes.TranslationStatisticsInput,
|
||||
@@ -309,11 +404,16 @@ export default class TranslationModuleService
|
||||
sharedContext.manager) as SqlEntityManager
|
||||
const knex = manager.getKnex()
|
||||
|
||||
const translatableFieldsConfig = await this.getTranslatableFields(
|
||||
undefined,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const result: TranslationTypes.TranslationStatisticsOutput = {}
|
||||
const entityTypes: string[] = []
|
||||
|
||||
for (const entityType of Object.keys(entities)) {
|
||||
const translatableFields = this.translatableFieldsConfig_[entityType]
|
||||
const translatableFields = translatableFieldsConfig[entityType]
|
||||
|
||||
if (!translatableFields || translatableFields.length === 0) {
|
||||
result[entityType] = {
|
||||
@@ -352,7 +452,7 @@ export default class TranslationModuleService
|
||||
)
|
||||
|
||||
for (const entityType of entityTypes) {
|
||||
const translatableFields = this.translatableFieldsConfig_[entityType]
|
||||
const translatableFields = translatableFieldsConfig[entityType]
|
||||
const fieldsPerEntity = translatableFields.length
|
||||
const entityCount = entities[entityType].count
|
||||
const expectedPerLocale = entityCount * fieldsPerEntity
|
||||
|
||||
Reference in New Issue
Block a user