drf-react-by-schema
Version:
Components and Tools for building a React App having Django Rest Framework (DRF) as server
368 lines (367 loc) • 12.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.slugify = exports.slugToCamelCase = exports.getPatternFormat = exports.isTmpId = exports.getTmpId = exports.errorProps = exports.buildGenericYupValidationSchema = exports.populateValues = exports.getChoiceByValue = exports.emptyByType = void 0;
exports.reducer = reducer;
exports.buildDateFormatBySchema = buildDateFormatBySchema;
exports.mergeFilterItems = mergeFilterItems;
const Yup = __importStar(require("yup"));
const emptyByType = (field, forDatabase = false) => {
if (field.model_default) {
return field.model_default;
}
switch (field.type) {
case 'nested object':
// emptyByType must be an empty object for the database, but must be null for the mui-autocomplete component. So I had to make this ugly hack here, when we're preparing to sendo to the database:
return forDatabase ? {} : null;
case 'field':
if (field.child) {
return [];
}
return forDatabase ? undefined : null;
case 'string':
case 'email':
return '';
case 'integer':
return 0;
case 'array':
return [];
case 'boolean':
return false;
default:
return null;
}
};
exports.emptyByType = emptyByType;
const getChoiceByValue = (value, choices) => {
if (!choices) {
return null;
}
for (const choice of choices) {
if (choice.value === value) {
return choice.display_name;
}
}
};
exports.getChoiceByValue = getChoiceByValue;
const populateValues = ({ data, schema }) => {
const values = {};
for (const [key, field] of Object.entries(schema)) {
if (key === 'id' && (0, exports.isTmpId)(data[key])) {
continue;
}
if (!data[key]) {
values[key] = (0, exports.emptyByType)(field);
continue;
}
if (field.type === 'field' && field.child) {
if (Array.isArray(data[key])) {
const arValues = [];
for (const row of data[key]) {
const value = (0, exports.populateValues)({
data: row,
schema: field.child.children,
});
arValues.push(value);
}
values[key] = arValues;
continue;
}
values[key] = (0, exports.populateValues)({
data: data[key],
schema: field.child.children,
});
continue;
}
if (field.type === 'choice') {
values[key] = data[key]
? {
value: data[key],
display_name: (0, exports.getChoiceByValue)(data[key], field.choices),
}
: (0, exports.emptyByType)(field);
continue;
}
values[key] = data[key];
}
// console.log(values);
return values;
};
exports.populateValues = populateValues;
const getYupValidator = (type) => {
let yupFunc;
try {
switch (type) {
case 'slug':
yupFunc = Yup.string();
break;
case 'email':
yupFunc = Yup.string().email('Este campo deve ser um e-mail válido.');
break;
case 'integer':
yupFunc = Yup.number().integer('Este campo deve ser um número inteiro');
break;
case 'choice':
yupFunc = Yup.object();
break;
case 'field':
yupFunc = Yup.mixed();
break;
case 'nested object':
yupFunc = Yup.object();
break;
case 'date':
yupFunc = Yup.date();
break;
case 'string':
yupFunc = Yup.string();
break;
case 'number':
case 'decimal':
yupFunc = Yup.number();
break;
case 'boolean':
yupFunc = Yup.bool();
break;
case 'array':
yupFunc = Yup.array();
break;
case 'object':
yupFunc = Yup.object();
break;
default:
yupFunc = Yup.string();
break;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (e) {
yupFunc = Yup.string();
}
return yupFunc.nullable();
};
const buildGenericYupValidationSchema = ({ data, schema, many = false, skipFields = [], extraValidators = {}, }) => {
const yupValidator = {};
for (const entry of Object.entries(schema)) {
const [key, field] = entry;
if (!data || !(key in data) || key === 'id' || skipFields.includes(key)) {
continue;
}
// console.log({ key, field, data: data[key] });
// OneToMany or ManyToMany:
if (field.type === 'field' && field.child) {
yupValidator[key] = (0, exports.buildGenericYupValidationSchema)({
schema: field.child.children,
many: true,
data: data[key],
extraValidators: key in extraValidators ? extraValidators[key] : {},
});
continue;
}
// Nested Object:
if (field.type === 'nested object' && field.children) {
// yupValidator[key] = buildGenericYupValidationSchema({
// schema: field.children,
// many: false,
// data: data[key],
// extraValidators: key in extraValidators ? extraValidators[key] : {}
// });
// if (!field.model_required) {
// yupValidator[key] = yupValidator[key].nullable();
// }
// continue;
}
yupValidator[key] = key in extraValidators ? extraValidators[key] : getYupValidator(field.type);
if (field.model_required) {
yupValidator[key] = yupValidator[key].required('Este campo é obrigatório');
}
if (field.max_length) {
yupValidator[key] = yupValidator[key].max(parseInt(field.max_length), `Este campo só pode ter no máximo ${field.max_length} caracteres`);
}
if (field.max_digits && field.type === 'decimal') {
const maxDigits = field.max_digits;
yupValidator[key] = yupValidator[key].test('len', `Este número pode ter no máximo ${maxDigits} dígitos`, (val) => {
if (!val) {
return true;
}
return val.toString().length <= maxDigits;
});
}
if (!field.read_only && field.validators_regex) {
for (const validator of field.validators_regex) {
yupValidator[key] = yupValidator[key].matches(validator.regex, validator.message);
}
}
}
// console.log({ yupValidator });
return many ? Yup.array().of(Yup.object().shape(yupValidator)) : Yup.object().shape(yupValidator);
};
exports.buildGenericYupValidationSchema = buildGenericYupValidationSchema;
const errorProps = ({ errors, fieldKey, fieldKeyProp, index, }) => {
let error;
let helperText;
if (index) {
const hasErrors = errors &&
errors[fieldKey] &&
errors[fieldKey][index] &&
Boolean(errors[fieldKey][index][fieldKeyProp]);
error = hasErrors;
helperText = hasErrors ? errors[fieldKey][index][fieldKeyProp].message : null;
return { error, helperText };
}
const hasErrors = errors && errors[fieldKey] && Boolean(errors[fieldKey][fieldKeyProp]);
error = hasErrors;
helperText = hasErrors ? errors[fieldKey][fieldKeyProp].message : null;
return { error, helperText };
};
exports.errorProps = errorProps;
const getTmpId = () => {
return 'tmp' + Math.floor(Math.random() * 1000000);
};
exports.getTmpId = getTmpId;
const isTmpId = (id) => {
if (!id) {
return true;
}
return id === 'novo' || id.toString().slice(0, 3) === 'tmp';
};
exports.isTmpId = isTmpId;
function reducer(state, newState) {
if (newState === null) {
return null;
}
if (state === null) {
return newState;
}
return Object.assign(Object.assign({}, state), newState);
}
const getPatternFormat = (type) => {
let format = '';
switch (type) {
case 'telefone':
case 'fone':
case 'phone':
case 'contact':
case 'contato':
format = '(##)#####-####';
break;
case 'cpf':
format = '###.###.###-##';
break;
case 'cnpj':
format = '##.###.###/####-##';
break;
case 'cep':
format = '##.###-###';
break;
}
return format;
};
exports.getPatternFormat = getPatternFormat;
function buildDateFormatBySchema(dateViews) {
const defaultFormat = 'DD/MM/YYYY';
if (!dateViews) {
return defaultFormat;
}
const hasDay = dateViews.includes('day');
const hasMonth = dateViews.includes('month');
const hasYear = dateViews.includes('year');
if (hasDay && hasMonth && hasYear) {
return defaultFormat;
}
if (!hasDay && hasMonth && hasYear) {
return 'MM/YYYY';
}
if (!hasDay && !hasMonth && hasYear) {
return 'YYYY';
}
if (!hasDay && hasMonth && !hasYear) {
return 'MM';
}
if (hasDay && !hasMonth && !hasYear) {
return 'DD';
}
return defaultFormat;
}
const slugToCamelCase = (str, includeFirst = false) => {
if (!str) {
return '';
}
const ret = str.replace(/_([a-z])/g, function (g) {
return g[1].toUpperCase();
});
if (includeFirst) {
return ret[0].toUpperCase() + ret.slice(1);
}
return ret;
};
exports.slugToCamelCase = slugToCamelCase;
const slugify = (text) => {
if (!text) {
return '';
}
return text
.toString()
.normalize('NFD')
.replace(/[\u0300-\u036f]/g, '')
.toLowerCase()
.trim()
.replace(/\s+/g, '-')
.replace(/[^\w-]+/g, '')
.replace(/--+/g, '-');
};
exports.slugify = slugify;
function mergeFilterItems(defaultFilter, filter) {
if (!filter && defaultFilter) {
return defaultFilter;
}
if (!defaultFilter && filter) {
return filter;
}
if (filter && defaultFilter) {
const items = filter.items;
const defaultItems = defaultFilter.items;
const mergedItems = defaultItems.map((defaultItem) => {
const itemFound = items.find((item) => item.columnField === defaultItem.columnField);
if (itemFound) {
return itemFound;
}
return defaultItem;
});
const unMergedItems = items.filter((item) => !defaultItems.map((defaultItem) => defaultItem.columnField).includes(item.columnField));
return Object.assign(Object.assign({}, filter), { items: [...mergedItems, ...unMergedItems] });
}
return undefined;
}