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
@@ -161,6 +161,56 @@ export class Translation {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves a paginated list of entities for a given entity type with only their
|
||||
* translatable fields.
|
||||
* It sends a request to the
|
||||
* Get Translation Entities API route.
|
||||
*
|
||||
* @param query - The query parameters including the entity type and pagination configurations.
|
||||
* @param headers - Headers to pass in the request.
|
||||
* @returns The paginated list of entities with their translatable fields.
|
||||
*
|
||||
* @example
|
||||
* To retrieve the entities for a given entity type:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.entities({
|
||||
* type: "product"
|
||||
* })
|
||||
* .then(({ data, count, offset, limit }) => {
|
||||
* console.log(data)
|
||||
* })
|
||||
* ```
|
||||
*
|
||||
* To configure the pagination, pass the `limit` and `offset` query parameters.
|
||||
*
|
||||
* For example, to retrieve only 10 items and skip 10 items:
|
||||
*
|
||||
* ```ts
|
||||
* sdk.admin.translation.entities({
|
||||
* type: "product",
|
||||
* limit: 10,
|
||||
* offset: 10
|
||||
* })
|
||||
* .then(({ data, count, offset, limit }) => {
|
||||
* console.log(data)
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
async entities(
|
||||
query: HttpTypes.AdminTranslationEntitiesParams,
|
||||
headers?: ClientHeaders
|
||||
) {
|
||||
return await this.client.fetch<HttpTypes.AdminTranslationEntitiesResponse>(
|
||||
`/admin/translations/entities`,
|
||||
{
|
||||
headers,
|
||||
query,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves the statistics for the translations for a given entity type or all entity types if no entity type is provided.
|
||||
* It sends a request to the
|
||||
|
||||
@@ -5,11 +5,30 @@ import {
|
||||
} from "../modules-sdk"
|
||||
|
||||
import type { RedisOptions } from "ioredis"
|
||||
|
||||
import { ConnectionOptions } from "node:tls"
|
||||
// @ts-ignore
|
||||
import type { InlineConfig } from "vite"
|
||||
import type { Logger } from "../logger"
|
||||
|
||||
/**
|
||||
* Registry for module options types. Modules can augment this interface
|
||||
* using declaration merging to provide typed options in defineConfig.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* // In @medusajs/translation module:
|
||||
* declare module "@medusajs/types" {
|
||||
* interface ModuleOptions {
|
||||
* "@medusajs/translation": {
|
||||
* entities?: { type: string; fields: string[] }[]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export interface ModuleOptions {}
|
||||
|
||||
/**
|
||||
* @interface
|
||||
*
|
||||
@@ -1093,17 +1112,6 @@ export type ConfigModule = {
|
||||
logger?: Logger
|
||||
}
|
||||
|
||||
type InternalModuleDeclarationOverride = InternalModuleDeclaration & {
|
||||
/**
|
||||
* Optional key to be used to identify the module, if not provided, it will be inferred from the module joiner config service name.
|
||||
*/
|
||||
key?: string
|
||||
/**
|
||||
* By default, modules are enabled, if provided as true, this will disable the module entirely.
|
||||
*/
|
||||
disable?: boolean
|
||||
}
|
||||
|
||||
type ExternalModuleDeclarationOverride = ExternalModuleDeclaration & {
|
||||
/**
|
||||
* key to be used to identify the module, if not provided, it will be inferred from the module joiner config service name.
|
||||
@@ -1116,11 +1124,41 @@ type ExternalModuleDeclarationOverride = ExternalModuleDeclaration & {
|
||||
}
|
||||
|
||||
/**
|
||||
* Modules accepted by the defineConfig function
|
||||
* Generates a union of typed module configs for all known modules in the ModuleOptions registry.
|
||||
* This enables automatic type inference when using registered module resolve strings.
|
||||
*/
|
||||
export type InputConfigModules = Partial<
|
||||
InternalModuleDeclarationOverride | ExternalModuleDeclarationOverride
|
||||
>[]
|
||||
type KnownModuleConfigs = {
|
||||
[K in keyof ModuleOptions]: Partial<
|
||||
Omit<InternalModuleDeclaration, "options"> & {
|
||||
key?: string
|
||||
disable?: boolean
|
||||
resolve: K
|
||||
options?: ModuleOptions[K]
|
||||
}
|
||||
>
|
||||
}[keyof ModuleOptions]
|
||||
|
||||
/**
|
||||
* Generic module config for modules not registered in ModuleOptions.
|
||||
*/
|
||||
type GenericModuleConfig = Partial<
|
||||
Omit<InternalModuleDeclaration, "options"> & {
|
||||
key?: string
|
||||
disable?: boolean
|
||||
resolve?: string
|
||||
options?: Record<string, unknown>
|
||||
}
|
||||
>
|
||||
|
||||
/**
|
||||
* Modules accepted by the defineConfig function.
|
||||
* Automatically infers options type for known modules registered in ModuleOptions.
|
||||
*/
|
||||
export type InputConfigModules = (
|
||||
| KnownModuleConfigs
|
||||
| GenericModuleConfig
|
||||
| ExternalModuleDeclarationOverride
|
||||
)[]
|
||||
|
||||
/**
|
||||
* The configuration accepted by the "defineConfig" helper
|
||||
|
||||
@@ -61,3 +61,28 @@ export interface AdminTranslationSettingsParams {
|
||||
*/
|
||||
entity_type?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Query parameters for translation entities endpoint.
|
||||
*/
|
||||
export interface AdminTranslationEntitiesParams extends FindParams {
|
||||
/**
|
||||
* The entity type to retrieve (e.g., "product", "product_variant").
|
||||
* This determines which table to query and which translatable fields to return.
|
||||
*
|
||||
* @example
|
||||
* "product"
|
||||
*/
|
||||
type: string
|
||||
|
||||
/**
|
||||
* Filter by entity ID(s). Can be a single ID or an array of IDs.
|
||||
*
|
||||
* @example
|
||||
* "prod_123"
|
||||
*
|
||||
* @example
|
||||
* ["prod_123", "prod_456"]
|
||||
*/
|
||||
id?: string | string[]
|
||||
}
|
||||
|
||||
@@ -99,3 +99,35 @@ export interface AdminTranslationSettingsResponse {
|
||||
*/
|
||||
translatable_fields: Record<string, string[]>
|
||||
}
|
||||
|
||||
/**
|
||||
* Response for translation entities endpoint.
|
||||
* Returns paginated entities with only their translatable fields and all their translations.
|
||||
*/
|
||||
export interface AdminTranslationEntitiesResponse {
|
||||
/**
|
||||
* The list of entities with their translatable fields.
|
||||
* Each entity contains only the fields configured as translatable
|
||||
* for that entity type in the translation settings, plus all
|
||||
* translations for all locales.
|
||||
*/
|
||||
data: (Record<string, unknown> & {
|
||||
id: string
|
||||
translations: AdminTranslation[]
|
||||
})[]
|
||||
|
||||
/**
|
||||
* The total count of entities.
|
||||
*/
|
||||
count: number
|
||||
|
||||
/**
|
||||
* The offset of the current page.
|
||||
*/
|
||||
offset: number
|
||||
|
||||
/**
|
||||
* The limit of items per page.
|
||||
*/
|
||||
limit: number
|
||||
}
|
||||
|
||||
@@ -80,6 +80,41 @@ export interface TranslationDTO {
|
||||
deleted_at: Date | string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The translation settings details.
|
||||
*/
|
||||
export interface TranslationSettingsDTO {
|
||||
/**
|
||||
* The ID of the settings record.
|
||||
*/
|
||||
id: string
|
||||
|
||||
/**
|
||||
* The entity type these settings apply to (e.g., "product", "product_variant").
|
||||
*/
|
||||
entity_type: string
|
||||
|
||||
/**
|
||||
* The translatable fields for this entity type.
|
||||
*/
|
||||
fields: string[]
|
||||
|
||||
/**
|
||||
* The date and time the settings were created.
|
||||
*/
|
||||
created_at: Date | string
|
||||
|
||||
/**
|
||||
* The date and time the settings were last updated.
|
||||
*/
|
||||
updated_at: Date | string
|
||||
|
||||
/**
|
||||
* The date and time the settings were deleted.
|
||||
*/
|
||||
deleted_at: Date | string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* The filters to apply on the retrieved locales.
|
||||
*/
|
||||
|
||||
@@ -704,27 +704,31 @@ export interface ITranslationModuleService extends IModuleService {
|
||||
): Promise<TranslationStatisticsOutput>
|
||||
|
||||
/**
|
||||
* This method retrieves the translatable fields of a resource. For example,
|
||||
* product entities have translatable fields such as `title` and `description`.
|
||||
* This method retrieves the translatable fields of a resource from the database.
|
||||
* For example, product entities have translatable fields such as `title` and `description`.
|
||||
*
|
||||
* @param {string} entityType - Name of the resource's table to get translatable fields for.
|
||||
* If not provided, returns all translatable fields for all entity types. For example, `product` or `product_variant`.
|
||||
* @returns {Record<string, string[]>} A mapping of resource names to their translatable fields.
|
||||
* @param {Context} sharedContext - A context used to share resources, such as transaction manager, between the application and the module.
|
||||
* @returns {Promise<Record<string, string[]>>} A mapping of resource names to their translatable fields.
|
||||
*
|
||||
* @example
|
||||
* To get translatable fields for all resources:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* const allFields = translationModuleService.getTranslatableFields()
|
||||
* const allFields = await translationModuleService.getTranslatableFields()
|
||||
* // Returns: { product: ["title", "description", ...], product_variant: ["title", ...] }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* To get translatable fields for a specific resource:
|
||||
*
|
||||
* ```ts
|
||||
* const productFields = translationModuleService.getTranslatableFields("product")
|
||||
* const productFields = await translationModuleService.getTranslatableFields("product")
|
||||
* // Returns: { product: ["title", "description", "subtitle", "status"] }
|
||||
* ```
|
||||
*/
|
||||
getTranslatableFields(entityType?: string): Record<string, string[]>
|
||||
getTranslatableFields(
|
||||
entityType?: string,
|
||||
sharedContext?: Context
|
||||
): Promise<Record<string, string[]>>
|
||||
}
|
||||
|
||||
@@ -349,11 +349,11 @@ function resolveModules(
|
||||
...(isObject(moduleConfig)
|
||||
? moduleConfig
|
||||
: { disable: !moduleConfig }),
|
||||
})
|
||||
} as InputConfigModules[number])
|
||||
})
|
||||
} else if (Array.isArray(configModules)) {
|
||||
const modules_ = (configModules ?? []) as InternalModuleDeclaration[]
|
||||
modules.push(...modules_)
|
||||
modules.push(...(modules_ as InputConfigModules))
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid modules configuration. Should be an array or object."
|
||||
|
||||
Reference in New Issue
Block a user