mirror of
https://github.com/YunoHost/yunohost-admin.git
synced 2024-09-03 20:06:15 +02:00
170 lines
5.3 KiB
TypeScript
170 lines
5.3 KiB
TypeScript
import {
|
|
flattenObjectLiteral,
|
|
getFileContent,
|
|
isEmptyValue,
|
|
isObjectLiteral,
|
|
} from '@/helpers/commons'
|
|
import store from '@/store'
|
|
import type { Translation } from '@/types/commons'
|
|
import type { AdressModelValue } from '@/types/form'
|
|
|
|
export const DEFAULT_STATUS_ICON = {
|
|
[null]: null,
|
|
danger: 'times',
|
|
error: 'times',
|
|
info: 'info',
|
|
success: 'check',
|
|
warning: 'warning',
|
|
}
|
|
|
|
// FORMAT FROM CORE
|
|
|
|
/**
|
|
* Tries to find a translation corresponding to the user's locale/fallback locale in a
|
|
* Yunohost argument or simply return the string if it's not an object literal.
|
|
*
|
|
* @param field - A field value containing a translation object or string
|
|
* @return translated field or empty string
|
|
*/
|
|
export function formatI18nField(field?: Translation): string {
|
|
if (!field) return ''
|
|
if (typeof field === 'string') return field
|
|
const { locale, fallbackLocale } = store.state as {
|
|
locale: string
|
|
fallbackLocale: string
|
|
}
|
|
return field[locale] || field[fallbackLocale] || field.en || ''
|
|
}
|
|
|
|
/**
|
|
* Returns a string size declaration to a M value.
|
|
*
|
|
* @param size - A size declared like '500M' or '56k'
|
|
* @return a number in M
|
|
*/
|
|
export function sizeToM(size: string) {
|
|
const unit = size.slice(-1)
|
|
const value = parseInt(size.slice(0, -1))
|
|
if (unit === 'M') return value
|
|
if (unit === 'b') return Math.ceil(value / (1024 * 1024))
|
|
if (unit === 'k') return Math.ceil(value / 1024)
|
|
if (unit === 'G') return Math.ceil(value * 1024)
|
|
if (unit === 'T') return Math.ceil(value * 1024 * 1024)
|
|
}
|
|
|
|
/**
|
|
* Returns an address as AdressModelValue to be used by AdressItem component.
|
|
*
|
|
* @param address - A string representing an adress (subdomain or email)
|
|
* @return Parsed address as `AdressModelValue`
|
|
*/
|
|
export function formatAdress(address: string): AdressModelValue {
|
|
const separator = address.includes('@') ? '@' : '.'
|
|
const [localPart, domain] = address.split(separator)
|
|
return { localPart, separator, domain }
|
|
}
|
|
|
|
// FORMAT TO CORE
|
|
|
|
/**
|
|
* Parse a front-end value to its API equivalent. This function returns a Promise or an
|
|
* Object `{ key: Promise }` if `key` is supplied. When parsing a form, all those
|
|
* objects must be merged to define the final sent form.
|
|
*
|
|
* Convert Boolean to '1' (true) or '0' (false),
|
|
* Concatenate two parts adresses (subdomain or email for example) into a single string,
|
|
* Convert File to its Base64 representation or set its value to '' to ask for a removal.
|
|
*
|
|
* @param {*} value
|
|
* @return {*}
|
|
*/
|
|
export function formatFormDataValue(value, key = null) {
|
|
if (Array.isArray(value)) {
|
|
return Promise.all(value.map((value_) => formatFormDataValue(value_))).then(
|
|
(resolvedValues) => ({ [key]: resolvedValues }),
|
|
)
|
|
}
|
|
|
|
let result = value
|
|
if (typeof value === 'boolean') result = value ? 1 : 0
|
|
if (isObjectLiteral(value) && 'file' in value) {
|
|
// File has to be deleted
|
|
if (value.removed) result = ''
|
|
// File has not changed (will not be sent)
|
|
else if (value.current || value.file === null) result = null
|
|
else {
|
|
return getFileContent(value.file, { base64: true }).then((content) => {
|
|
return {
|
|
[key]: content.replace(/data:[^;]*;base64,/, ''),
|
|
[key + '[name]']: value.file.name,
|
|
}
|
|
})
|
|
}
|
|
} else if (isObjectLiteral(value) && 'separator' in value) {
|
|
result = Object.values(value).join('')
|
|
}
|
|
|
|
// Returns a resolved Promise for non async values
|
|
return Promise.resolve(key ? { [key]: result } : result)
|
|
}
|
|
|
|
/**
|
|
* Convinient helper to properly parse a front-end form to its API equivalent.
|
|
* This parse each values asynchronously, allow to inject keys into the final form and
|
|
* make sure every async values resolves before resolving itself.
|
|
*
|
|
* @param {Object} formData
|
|
* @return {Object}
|
|
*/
|
|
function formatFormDataValues(formData) {
|
|
const promisedValues = Object.entries(formData).map(([key, value]) => {
|
|
return formatFormDataValue(value, key)
|
|
})
|
|
|
|
return Promise.all(promisedValues).then((resolvedValues) => {
|
|
return resolvedValues.reduce((form, obj) => ({ ...form, ...obj }), {})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Format a form produced by a vue view to be sent to the server.
|
|
*
|
|
* @param {Object} formData - An object literal containing form values.
|
|
* @param {Object} [extraParams] - Optionnal params
|
|
* @param {Array} [extraParams.extract] - An array of keys that should be extracted from the form.
|
|
* @param {Boolean} [extraParams.flatten=false] - Flattens or not the passed formData.
|
|
* @param {Boolean} [extraParams.removeEmpty=true] - Removes "empty" values from the object.
|
|
* @return {Object} the parsed data to be sent to the server, with extracted values if specified.
|
|
*/
|
|
export async function formatFormData(
|
|
formData,
|
|
{
|
|
extract = null,
|
|
flatten = false,
|
|
removeEmpty = true,
|
|
removeNull = false,
|
|
} = {},
|
|
) {
|
|
const output = {
|
|
data: {},
|
|
extracted: {},
|
|
}
|
|
|
|
const values = await formatFormDataValues(formData)
|
|
for (const key in values) {
|
|
const type = extract && extract.includes(key) ? 'extracted' : 'data'
|
|
const value = values[key]
|
|
if (removeEmpty && isEmptyValue(value)) {
|
|
continue
|
|
} else if (removeNull && [null, undefined].includes(value)) {
|
|
continue
|
|
} else if (flatten && isObjectLiteral(value)) {
|
|
flattenObjectLiteral(value, output[type])
|
|
} else {
|
|
output[type][key] = value
|
|
}
|
|
}
|
|
|
|
const { data, extracted } = output
|
|
return extract ? { data, ...extracted } : data
|
|
}
|