@elastic-suite/gally-admin-shared
Version:
Shared package for gally admin BO
225 lines (207 loc) • 6.14 kB
text/typescript
import { TFunction } from 'i18next'
import { booleanRegexp } from '../constants'
import {
DataContentType,
IField,
IFieldConfig,
IHydraMapping,
IHydraMember,
IHydraResponse,
IImage,
IResource,
} from '../types'
import { getFieldLabelTranslationArgs, joinUrlPath } from './format'
import { getField, getFieldType } from './hydra'
interface IMapping extends IHydraMapping {
field: IField
multiple: boolean
}
export function getFieldDataContentType(field: IField): DataContentType {
const type = getFieldType(field)
if (type === 'boolean') {
return DataContentType.BOOLEAN
} else if (type === 'integer' || type === 'float' || type === 'percentage') {
return DataContentType.NUMBER
}
return DataContentType.STRING
}
const dataContentTypes = Object.values(DataContentType)
export function isDataContentType<T>(
type: T | DataContentType
): type is DataContentType {
return dataContentTypes.includes(type as DataContentType)
}
export function getFieldInput(
field: IField,
fallback: DataContentType
): DataContentType {
if (field.gally?.input && isDataContentType(field.gally.input)) {
return field.gally.input
}
return fallback
}
const customValidations: Record<
string,
{
attribute: string
value: string | number
error: {
name: string
message: string
}
}
> = {
prompt: {
attribute: 'pattern',
value: '^(?:(?!%)[\\s\\S])*%s(?:(?!%)[\\s\\S])*$',
error: {
name: 'patternMismatch',
message: 'prompt',
},
},
email: {
attribute: 'type',
value: 'email',
error: {
name: 'typeMismatch',
message: 'typeMismatchEmail',
},
},
}
export function getFieldConfig(
field: IField
): Pick<
IFieldConfig,
| 'depends'
| 'field'
| 'suffix'
| 'validation'
| 'showError'
| 'replacementErrorsMessages'
> {
const validation: Record<string, string | number> = {}
const replacementErrorsMessages: Record<string, string> = {}
let showError = false
Object.entries(field.gally?.validation || {}).forEach(([key, value]) => {
if (customValidations[key] && value) {
validation[customValidations[key].attribute] =
customValidations[key].value
replacementErrorsMessages[customValidations[key].error.name] =
customValidations[key].error.message
showError = true
} else if (!customValidations[key] && typeof value !== 'boolean') {
validation[key] = value
}
})
return {
depends: field.gally?.depends,
field,
suffix: field.gally?.input === 'percentage' ? '%' : '',
validation,
showError,
replacementErrorsMessages,
}
}
export function getFieldHeader(field: IField, t: TFunction): IFieldConfig {
const fieldConfig = getFieldConfig(field)
const type = getFieldDataContentType(field)
const id = field.title
const input = getFieldInput(field, type)
return {
...fieldConfig,
editable: field.gally?.editable && field.writeable,
fieldset: field.gally?.fieldset,
gridHeaderInfoTooltip: field.gally?.gridHeaderInfoTooltip,
id,
input,
infoTooltip: field.gally?.infoTooltip,
label:
field.property.label ?? t(...getFieldLabelTranslationArgs(field.title)),
name: id,
required: field.gally?.required ?? field.required,
type,
multipleValueFormat: field.gally?.multipleValueFormat,
requestTypeConfigurations: field.gally?.requestTypeConfigurations,
multipleInputConfiguration: field.gally?.multipleInputConfiguration,
placeholder: field.gally?.placeholder,
defaultValue: field.gally?.defaultValue,
}
}
export function getFilterType(mapping: IMapping): DataContentType {
return mapping.multiple
? DataContentType.SELECT
: booleanRegexp.test(mapping.property)
? DataContentType.BOOLEAN
: DataContentType.STRING
}
export function getFilter(mapping: IMapping, t: TFunction): IFieldConfig {
const fieldConfig = getFieldConfig(mapping.field)
const type = getFilterType(mapping)
const id = mapping.variable
const input = getFieldInput(mapping.field, type)
return {
...fieldConfig,
editable: true,
id,
input: mapping.variable.endsWith('[between]')
? DataContentType.RANGE
: input,
label: mapping.field
? mapping.field.property.label ??
t(...getFieldLabelTranslationArgs(mapping.field.title))
: t(...getFieldLabelTranslationArgs(mapping.property)),
multiple: mapping.multiple,
name: id,
required: false, // Always false for filter
type,
}
}
export function getMappings<T extends IHydraMember>(
apiData: IHydraResponse<T>,
resource: IResource
): IMapping[] {
const mappings: IMapping[] = apiData?.['hydra:search']['hydra:mapping']
.filter(
(mapping) =>
!mapping.variable.endsWith('[lt]') &&
!mapping.variable.endsWith('[gt]') &&
!mapping.variable.endsWith('[lte]') &&
!mapping.variable.endsWith('[gte]')
)
.map((mapping) => ({
...mapping,
field: getField(resource, mapping.property),
multiple: mapping.variable.endsWith('[]'),
}))
.filter((mapping) => mapping.field)
const arrayProperties = mappings
.filter((mapping) => mapping.multiple)
.map((mapping) => mapping.property)
return mappings
?.filter((mapping) => mapping.field.gally?.visible)
.filter(
(mapping) =>
!arrayProperties.includes(mapping.property) || mapping.multiple
)
.sort((a, b) => a.field.gally?.position - b.field.gally?.position)
}
export function getImagePath(image: IImage | string): string {
return typeof image === 'object' && 'path' in image
? image.path
: (image as string)
}
export function isIImage(image: IImage | string): boolean {
return typeof image === 'object' && 'path' in image && 'icons' in image
}
export function getImageValue(
baseUrl: string,
rowValue: string | IImage
): string | IImage {
let image = rowValue
if (typeof image !== 'string' && isIImage(rowValue)) {
image = { ...image, path: joinUrlPath(baseUrl, image.path) }
} else {
image = joinUrlPath(baseUrl, getImagePath(image))
}
return image
}