Compare commits

..

13 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
f92acbcbe0 fix: Getting the sheet columns in import sheet 2024-08-30 17:03:16 +02:00
Ahmed Bouhuolia
c986585cd9 Merge pull request #640 from bigcapitalhq/fix-typo-one-click-demo
fix: Typo one-click demo page
2024-08-30 00:15:29 +02:00
Ahmed Bouhuolia
250f0a30ef fix: Typo one-click demo page 2024-08-30 00:14:59 +02:00
Ahmed Bouhuolia
84b5e1adc1 Merge pull request #639 from bigcapitalhq/add-customer-type-to-customers
fix: Add customer type to customers resource
2024-08-29 22:50:20 +02:00
Ahmed Bouhuolia
2ab28370db fix: Add customer type to customers resource 2024-08-29 22:49:49 +02:00
Ahmed Bouhuolia
a88a525326 Merge pull request #638 from bigcapitalhq/use-standard-date-format-export
fix: use standard ISO 8601 format for exported data
2024-08-29 22:40:59 +02:00
Ahmed Bouhuolia
fce8e2c5a4 fix: use standard ISO 8601 format for exported data 2024-08-29 22:39:51 +02:00
Ahmed Bouhuolia
095608266c Merge pull request #632 from bigcapitalhq/split-lazy-loading
feat: Optimize loading perf. by splitting big chunks and lazy loading them
2024-08-29 21:27:40 +02:00
Ahmed Bouhuolia
0ec8aaa330 fix: delete unwanted files 2024-08-29 21:27:20 +02:00
Ahmed Bouhuolia
9fcb3ef77d feat: Add fallback spinner to authentication lazy-loaded pages 2024-08-29 21:19:36 +02:00
Ahmed Bouhuolia
dc61c57daf fix: Add spinner to preferences lazy loaded pages 2024-08-29 21:03:27 +02:00
Ahmed Bouhuolia
af284f3f6d feat: split the preferences pages 2024-08-29 20:49:08 +02:00
Ahmed Bouhuolia
c9fe6d9b37 feat: Optimize loading perf. by spliting big chunks and lazy loading them 2024-08-26 22:51:40 +02:00
32 changed files with 407 additions and 362 deletions

View File

@@ -40,7 +40,6 @@ export class ImportController extends BaseController {
body('mapping.*.group').optional(), body('mapping.*.group').optional(),
body('mapping.*.from').exists(), body('mapping.*.from').exists(),
body('mapping.*.to').exists(), body('mapping.*.to').exists(),
body('mapping.*.dateFormat').optional({ nullable: true }),
], ],
this.validationResult, this.validationResult,
this.asyncMiddleware(this.mapping.bind(this)), this.asyncMiddleware(this.mapping.bind(this)),

View File

@@ -2,6 +2,7 @@ import moment from 'moment';
import * as R from 'ramda'; import * as R from 'ramda';
import { includes, isFunction, isObject, isUndefined, omit } from 'lodash'; import { includes, isFunction, isObject, isUndefined, omit } from 'lodash';
import { formatNumber, sortObjectKeysAlphabetically } from 'utils'; import { formatNumber, sortObjectKeysAlphabetically } from 'utils';
import { EXPORT_DTE_FORMAT } from '@/services/Export/constants';
export class Transformer { export class Transformer {
public context: any; public context: any;
@@ -156,22 +157,34 @@ export class Transformer {
} }
/** /**
* * Format date.
* @param date * @param {} date
* @returns * @param {string} format -
* @returns {}
*/ */
protected formatDate(date) { protected formatDate(date, format?: string) {
return date ? moment(date).format(this.dateFormat) : ''; // Use the export date format if the async operation is in exporting,
// otherwise use the given or default format.
const _format = this.context.exportAls.isExport
? EXPORT_DTE_FORMAT
: format || this.dateFormat;
return date ? moment(date).format(_format) : '';
} }
protected formatDateFromNow(date){ /**
*
* @param date
* @returns {}
*/
protected formatDateFromNow(date) {
return date ? moment(date).fromNow(true) : ''; return date ? moment(date).fromNow(true) : '';
} }
/** /**
* *
* @param number * @param number
* @returns * @returns {}
*/ */
protected formatNumber(number, props?) { protected formatNumber(number, props?) {
return formatNumber(number, { money: false, ...props }); return formatNumber(number, { money: false, ...props });
@@ -181,7 +194,7 @@ export class Transformer {
* *
* @param money * @param money
* @param options * @param options
* @returns * @returns {}
*/ */
protected formatMoney(money, options?) { protected formatMoney(money, options?) {
return formatNumber(money, { return formatNumber(money, {

View File

@@ -3,12 +3,17 @@ import { isNull } from 'lodash';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import { TenantMetadata } from '@/system/models'; import { TenantMetadata } from '@/system/models';
import { Transformer } from './Transformer'; import { Transformer } from './Transformer';
import { ImportAls } from '@/services/Import/ImportALS';
import { ExportAls } from '@/services/Export/ExportAls';
@Service() @Service()
export class TransformerInjectable { export class TransformerInjectable {
@Inject() @Inject()
private tenancy: HasTenancyService; private tenancy: HasTenancyService;
@Inject()
private exportAls: ExportAls;
/** /**
* Retrieves the application context of all tenant transformers. * Retrieves the application context of all tenant transformers.
* @param {number} tenantId * @param {number} tenantId
@@ -17,10 +22,12 @@ export class TransformerInjectable {
async getApplicationContext(tenantId: number) { async getApplicationContext(tenantId: number) {
const i18n = this.tenancy.i18n(tenantId); const i18n = this.tenancy.i18n(tenantId);
const organization = await TenantMetadata.query().findOne({ tenantId }); const organization = await TenantMetadata.query().findOne({ tenantId });
const exportAls = this.exportAls;
return { return {
organization, organization,
i18n, i18n,
exportAls,
}; };
} }

View File

@@ -95,6 +95,11 @@ export default {
}, },
}, },
columns: { columns: {
customerType: {
name: 'Customer Type',
type: 'text',
accessor: 'formattedCustomerType',
},
firstName: { firstName: {
name: 'vendor.field.first_name', name: 'vendor.field.first_name',
type: 'text', type: 'text',
@@ -135,116 +140,117 @@ export default {
openingBalance: { openingBalance: {
name: 'vendor.field.opening_balance', name: 'vendor.field.opening_balance',
type: 'number', type: 'number',
printable: false printable: false,
}, },
openingBalanceAt: { openingBalanceAt: {
name: 'vendor.field.opening_balance_at', name: 'vendor.field.opening_balance_at',
type: 'date', type: 'date',
printable: false printable: false,
accessor: 'formattedOpeningBalanceAt'
}, },
currencyCode: { currencyCode: {
name: 'vendor.field.currency', name: 'vendor.field.currency',
type: 'text', type: 'text',
printable: false printable: false,
}, },
status: { status: {
name: 'vendor.field.status', name: 'vendor.field.status',
printable: false printable: false,
}, },
note: { note: {
name: 'vendor.field.note', name: 'vendor.field.note',
printable: false printable: false,
}, },
// Billing Address // Billing Address
billingAddress1: { billingAddress1: {
name: 'Billing Address 1', name: 'Billing Address 1',
column: 'billing_address1', column: 'billing_address1',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddress2: { billingAddress2: {
name: 'Billing Address 2', name: 'Billing Address 2',
column: 'billing_address2', column: 'billing_address2',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddressCity: { billingAddressCity: {
name: 'Billing Address City', name: 'Billing Address City',
column: 'billing_address_city', column: 'billing_address_city',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddressCountry: { billingAddressCountry: {
name: 'Billing Address Country', name: 'Billing Address Country',
column: 'billing_address_country', column: 'billing_address_country',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddressPostcode: { billingAddressPostcode: {
name: 'Billing Address Postcode', name: 'Billing Address Postcode',
column: 'billing_address_postcode', column: 'billing_address_postcode',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddressState: { billingAddressState: {
name: 'Billing Address State', name: 'Billing Address State',
column: 'billing_address_state', column: 'billing_address_state',
type: 'text', type: 'text',
printable: false printable: false,
}, },
billingAddressPhone: { billingAddressPhone: {
name: 'Billing Address Phone', name: 'Billing Address Phone',
column: 'billing_address_phone', column: 'billing_address_phone',
type: 'text', type: 'text',
printable: false printable: false,
}, },
// Shipping Address // Shipping Address
shippingAddress1: { shippingAddress1: {
name: 'Shipping Address 1', name: 'Shipping Address 1',
column: 'shipping_address1', column: 'shipping_address1',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddress2: { shippingAddress2: {
name: 'Shipping Address 2', name: 'Shipping Address 2',
column: 'shipping_address2', column: 'shipping_address2',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddressCity: { shippingAddressCity: {
name: 'Shipping Address City', name: 'Shipping Address City',
column: 'shipping_address_city', column: 'shipping_address_city',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddressCountry: { shippingAddressCountry: {
name: 'Shipping Address Country', name: 'Shipping Address Country',
column: 'shipping_address_country', column: 'shipping_address_country',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddressPostcode: { shippingAddressPostcode: {
name: 'Shipping Address Postcode', name: 'Shipping Address Postcode',
column: 'shipping_address_postcode', column: 'shipping_address_postcode',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddressPhone: { shippingAddressPhone: {
name: 'Shipping Address Phone', name: 'Shipping Address Phone',
column: 'shipping_address_phone', column: 'shipping_address_phone',
type: 'text', type: 'text',
printable: false printable: false,
}, },
shippingAddressState: { shippingAddressState: {
name: 'Shipping Address State', name: 'Shipping Address State',
column: 'shipping_address_state', column: 'shipping_address_state',
type: 'text', type: 'text',
printable: false printable: false,
}, },
createdAt: { createdAt: {
name: 'vendor.field.created_at', name: 'vendor.field.created_at',
type: 'date', type: 'date',
printable: false printable: false,
}, },
}, },
fields2: { fields2: {

View File

@@ -0,0 +1,48 @@
import { Service } from 'typedi';
import { AsyncLocalStorage } from 'async_hooks';
@Service()
export class ExportAls {
private als: AsyncLocalStorage<Map<string, any>>;
constructor() {
this.als = new AsyncLocalStorage();
}
/**
* Runs a callback function within the context of a new AsyncLocalStorage store.
* @param callback The function to be executed within the AsyncLocalStorage context.
* @returns The result of the callback function.
*/
public run<T>(callback: () => T): T {
return this.als.run<T>(new Map(), () => {
this.markAsExport();
return callback();
});
}
/**
* Retrieves the current AsyncLocalStorage store.
* @returns The current store or undefined if not in a valid context.
*/
public getStore(): Map<string, any> | undefined {
return this.als.getStore();
}
/**
* Marks the current context as an export operation.
* @param flag Boolean flag to set or unset the export status. Defaults to true.
*/
public markAsExport(flag: boolean = true): void {
const store = this.getStore();
store?.set('isExport', flag);
}
/**
* Checks if the current context is an export operation.
* @returns {boolean} True if the context is an export operation, false otherwise.
*/
public get isExport(): boolean {
return !!this.getStore()?.get('isExport');
}
}

View File

@@ -10,6 +10,7 @@ import { Errors, ExportFormat } from './common';
import { IModelMeta, IModelMetaColumn } from '@/interfaces'; import { IModelMeta, IModelMetaColumn } from '@/interfaces';
import { flatDataCollections, getDataAccessor } from './utils'; import { flatDataCollections, getDataAccessor } from './utils';
import { ExportPdf } from './ExportPdf'; import { ExportPdf } from './ExportPdf';
import { ExportAls } from './ExportAls';
@Service() @Service()
export class ExportResourceService { export class ExportResourceService {
@@ -22,13 +23,33 @@ export class ExportResourceService {
@Inject() @Inject()
private exportPdf: ExportPdf; private exportPdf: ExportPdf;
@Inject()
private exportAls: ExportAls;
/**
*
* @param {number} tenantId
* @param {string} resourceName
* @param {ExportFormat} format
* @returns
*/
public async export(
tenantId: number,
resourceName: string,
format: ExportFormat = ExportFormat.Csv
) {
return this.exportAls.run(() =>
this.exportAlsRun(tenantId, resourceName, format)
);
}
/** /**
* Exports the given resource data through csv, xlsx or pdf. * Exports the given resource data through csv, xlsx or pdf.
* @param {number} tenantId - Tenant id. * @param {number} tenantId - Tenant id.
* @param {string} resourceName - Resource name. * @param {string} resourceName - Resource name.
* @param {ExportFormat} format - File format. * @param {ExportFormat} format - File format.
*/ */
public async export( public async exportAlsRun(
tenantId: number, tenantId: number,
resourceName: string, resourceName: string,
format: ExportFormat = ExportFormat.Csv format: ExportFormat = ExportFormat.Csv

View File

@@ -1 +1,2 @@
export const EXPORT_SIZE_LIMIT = 9999999; export const EXPORT_SIZE_LIMIT = 9999999;
export const EXPORT_DTE_FORMAT = 'YYYY-MM-DD';

View File

@@ -8,6 +8,7 @@ export class ImportAls {
constructor() { constructor() {
this.als = new AsyncLocalStorage(); this.als = new AsyncLocalStorage();
} }
/** /**
* Runs a callback function within the context of a new AsyncLocalStorage store. * Runs a callback function within the context of a new AsyncLocalStorage store.
* @param callback The function to be executed within the AsyncLocalStorage context. * @param callback The function to be executed within the AsyncLocalStorage context.
@@ -82,7 +83,7 @@ export class ImportAls {
* Checks if the current context is an import operation. * Checks if the current context is an import operation.
* @returns {boolean} True if the context is an import operation, false otherwise. * @returns {boolean} True if the context is an import operation, false otherwise.
*/ */
public isImport(): boolean { public get isImport(): boolean {
return !!this.getStore()?.get('isImport'); return !!this.getStore()?.get('isImport');
} }
@@ -90,7 +91,7 @@ export class ImportAls {
* Checks if the current context is an import commit operation. * Checks if the current context is an import commit operation.
* @returns {boolean} True if the context is an import commit operation, false otherwise. * @returns {boolean} True if the context is an import commit operation, false otherwise.
*/ */
public isImportCommit(): boolean { public get isImportCommit(): boolean {
return !!this.getStore()?.get('isImportCommit'); return !!this.getStore()?.get('isImportCommit');
} }
@@ -98,7 +99,7 @@ export class ImportAls {
* Checks if the current context is an import preview operation. * Checks if the current context is an import preview operation.
* @returns {boolean} True if the context is an import preview operation, false otherwise. * @returns {boolean} True if the context is an import preview operation, false otherwise.
*/ */
public isImportPreview(): boolean { public get isImportPreview(): boolean {
return !!this.getStore()?.get('isImportPreview'); return !!this.getStore()?.get('isImportPreview');
} }
} }

View File

@@ -1,4 +1,3 @@
import XLSX from 'xlsx';
import bluebird from 'bluebird'; import bluebird from 'bluebird';
import * as R from 'ramda'; import * as R from 'ramda';
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
@@ -12,11 +11,7 @@ import {
ImportableContext, ImportableContext,
} from './interfaces'; } from './interfaces';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { import { getUniqueImportableValue, trimObject } from './_utils';
convertMappingsToObject,
getUniqueImportableValue,
trimObject,
} from './_utils';
import { ImportableResources } from './ImportableResources'; import { ImportableResources } from './ImportableResources';
import ResourceService from '../Resource/ResourceService'; import ResourceService from '../Resource/ResourceService';
import { Import } from '@/system/models'; import { Import } from '@/system/models';
@@ -32,21 +27,6 @@ export class ImportFileCommon {
@Inject() @Inject()
private resource: ResourceService; private resource: ResourceService;
/**
* Maps the columns of the imported data based on the provided mapping attributes.
* @param {Record<string, any>[]} body - The array of data objects to map.
* @param {ImportMappingAttr[]} map - The mapping attributes.
* @returns {Record<string, any>[]} - The mapped data objects.
*/
public parseXlsxSheet(buffer: Buffer): Record<string, unknown>[] {
const workbook = XLSX.read(buffer, { type: 'buffer', raw: true });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
return XLSX.utils.sheet_to_json(worksheet, {});
}
/** /**
* Imports the given parsed data to the resource storage through registered importable service. * Imports the given parsed data to the resource storage through registered importable service.
* @param {number} tenantId - * @param {number} tenantId -
@@ -85,14 +65,11 @@ export class ImportFileCommon {
rowNumber, rowNumber,
uniqueValue, uniqueValue,
}; };
const mappingSettings = convertMappingsToObject(importFile.columnsParsed);
try { try {
// Validate the DTO object before passing it to the service layer. // Validate the DTO object before passing it to the service layer.
await this.importFileValidator.validateData( await this.importFileValidator.validateData(
resourceFields, resourceFields,
transformedDTO, transformedDTO
mappingSettings
); );
try { try {
// Run the importable function and listen to the errors. // Run the importable function and listen to the errors.

View File

@@ -1,9 +1,5 @@
import { Service } from 'typedi'; import { Service } from 'typedi';
import { import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces';
ImportInsertError,
ImportMappingAttr,
ResourceMetaFieldsMap,
} from './interfaces';
import { ERRORS, convertFieldsToYupValidation } from './_utils'; import { ERRORS, convertFieldsToYupValidation } from './_utils';
import { IModelMeta } from '@/interfaces'; import { IModelMeta } from '@/interfaces';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
@@ -28,13 +24,9 @@ export class ImportFileDataValidator {
*/ */
public async validateData( public async validateData(
importableFields: ResourceMetaFieldsMap, importableFields: ResourceMetaFieldsMap,
data: Record<string, any>, data: Record<string, any>
mappingSettings: Record<string, ImportMappingAttr> = {}
): Promise<void | ImportInsertError[]> { ): Promise<void | ImportInsertError[]> {
const YupSchema = convertFieldsToYupValidation( const YupSchema = convertFieldsToYupValidation(importableFields);
importableFields,
mappingSettings
);
const _data = { ...data }; const _data = { ...data };
try { try {

View File

@@ -2,18 +2,14 @@ import { Inject, Service } from 'typedi';
import { chain } from 'lodash'; import { chain } from 'lodash';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { import { ERRORS, getUnmappedSheetColumns, readImportFile } from './_utils';
ERRORS,
getSheetColumns,
getUnmappedSheetColumns,
readImportFile,
} from './_utils';
import { ImportFileCommon } from './ImportFileCommon'; import { ImportFileCommon } from './ImportFileCommon';
import { ImportFileDataTransformer } from './ImportFileDataTransformer'; import { ImportFileDataTransformer } from './ImportFileDataTransformer';
import ResourceService from '../Resource/ResourceService'; import ResourceService from '../Resource/ResourceService';
import UnitOfWork from '../UnitOfWork'; import UnitOfWork from '../UnitOfWork';
import { ImportFilePreviewPOJO } from './interfaces'; import { ImportFilePreviewPOJO } from './interfaces';
import { Import } from '@/system/models'; import { Import } from '@/system/models';
import { parseSheetData } from './sheet_utils';
@Service() @Service()
export class ImportFileProcess { export class ImportFileProcess {
@@ -49,10 +45,10 @@ export class ImportFileProcess {
if (!importFile.isMapped) { if (!importFile.isMapped) {
throw new ServiceError(ERRORS.IMPORT_FILE_NOT_MAPPED); throw new ServiceError(ERRORS.IMPORT_FILE_NOT_MAPPED);
} }
// Read the imported file. // Read the imported file and parse the given buffer to get columns
// and sheet data in json format.
const buffer = await readImportFile(importFile.filename); const buffer = await readImportFile(importFile.filename);
const sheetData = this.importCommon.parseXlsxSheet(buffer); const [sheetData, sheetColumns] = parseSheetData(buffer);
const header = getSheetColumns(sheetData);
const resource = importFile.resource; const resource = importFile.resource;
const resourceFields = this.resource.getResourceFields2(tenantId, resource); const resourceFields = this.resource.getResourceFields2(tenantId, resource);
@@ -87,7 +83,7 @@ export class ImportFileProcess {
.flatten() .flatten()
.value(); .value();
const unmappedColumns = getUnmappedSheetColumns(header, mapping); const unmappedColumns = getUnmappedSheetColumns(sheetColumns, mapping);
const totalCount = allData.length; const totalCount = allData.length;
const createdCount = successedImport.length; const createdCount = successedImport.length;

View File

@@ -11,6 +11,7 @@ import { ImportFileCommon } from './ImportFileCommon';
import { ImportFileDataValidator } from './ImportFileDataValidator'; import { ImportFileDataValidator } from './ImportFileDataValidator';
import { ImportFileUploadPOJO } from './interfaces'; import { ImportFileUploadPOJO } from './interfaces';
import { Import } from '@/system/models'; import { Import } from '@/system/models';
import { parseSheetData } from './sheet_utils';
@Service() @Service()
export class ImportFileUploadService { export class ImportFileUploadService {
@@ -77,14 +78,12 @@ export class ImportFileUploadService {
const buffer = await readImportFile(filename); const buffer = await readImportFile(filename);
// Parse the buffer file to array data. // Parse the buffer file to array data.
const sheetData = this.importFileCommon.parseXlsxSheet(buffer); const [sheetData, sheetColumns] = parseSheetData(buffer);
const coumnsStringified = JSON.stringify(sheetColumns);
// Throws service error if the sheet data is empty. // Throws service error if the sheet data is empty.
validateSheetEmpty(sheetData); validateSheetEmpty(sheetData);
const sheetColumns = this.importFileCommon.parseSheetColumns(sheetData);
const coumnsStringified = JSON.stringify(sheetColumns);
try { try {
// Validates the params Yup schema. // Validates the params Yup schema.
await this.importFileCommon.validateParamsSchema(resource, params); await this.importFileCommon.validateParamsSchema(resource, params);

View File

@@ -17,10 +17,9 @@ import {
head, head,
split, split,
last, last,
set,
} from 'lodash'; } from 'lodash';
import pluralize from 'pluralize'; import pluralize from 'pluralize';
import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces'; import { ResourceMetaFieldsMap } from './interfaces';
import { IModelMetaField, IModelMetaField2 } from '@/interfaces'; import { IModelMetaField, IModelMetaField2 } from '@/interfaces';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { multiNumberParse } from '@/utils/multi-number-parse'; import { multiNumberParse } from '@/utils/multi-number-parse';
@@ -59,19 +58,11 @@ export function trimObject(obj: Record<string, string | number>) {
* @param {ResourceMetaFieldsMap} fields * @param {ResourceMetaFieldsMap} fields
* @returns {Yup} * @returns {Yup}
*/ */
export const convertFieldsToYupValidation = ( export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
fields: ResourceMetaFieldsMap,
parentFieldName: string = '',
mappingSettings: Record<string, ImportMappingAttr> = {}
) => {
const yupSchema = {}; const yupSchema = {};
Object.keys(fields).forEach((fieldName: string) => { Object.keys(fields).forEach((fieldName: string) => {
const field = fields[fieldName] as IModelMetaField; const field = fields[fieldName] as IModelMetaField;
const fieldPath = parentFieldName
? `${parentFieldName}.${fieldName}`
: fieldName;
let fieldSchema; let fieldSchema;
fieldSchema = Yup.string().label(field.name); fieldSchema = Yup.string().label(field.name);
@@ -114,23 +105,13 @@ export const convertFieldsToYupValidation = (
if (!val) { if (!val) {
return true; return true;
} }
const fieldDateFormat = return moment(val, 'YYYY-MM-DD', true).isValid();
(get(
mappingSettings,
`${fieldPath}.dateFormat`
) as unknown as string) || 'YYYY-MM-DD';
return moment(val, fieldDateFormat, true).isValid();
} }
); );
} else if (field.fieldType === 'url') { } else if (field.fieldType === 'url') {
fieldSchema = fieldSchema.url(); fieldSchema = fieldSchema.url();
} else if (field.fieldType === 'collection') { } else if (field.fieldType === 'collection') {
const nestedFieldShema = convertFieldsToYupValidation( const nestedFieldShema = convertFieldsToYupValidation(field.fields);
field.fields,
field.name,
mappingSettings
);
fieldSchema = Yup.array().label(field.name); fieldSchema = Yup.array().label(field.name);
if (!isUndefined(field.collectionMaxLength)) { if (!isUndefined(field.collectionMaxLength)) {
@@ -277,7 +258,6 @@ export const getResourceColumns = (resourceColumns: {
]) => { ]) => {
const extra: Record<string, any> = {}; const extra: Record<string, any> = {};
const key = fieldKey; const key = fieldKey;
const type = field.fieldType;
if (group) { if (group) {
extra.group = group; extra.group = group;
@@ -290,7 +270,6 @@ export const getResourceColumns = (resourceColumns: {
name, name,
required, required,
hint: importHint, hint: importHint,
type,
order, order,
...extra, ...extra,
}; };
@@ -343,8 +322,6 @@ export const valueParser =
}); });
const result = await relationQuery.first(); const result = await relationQuery.first();
_value = get(result, 'id'); _value = get(result, 'id');
} else if (field.fieldType === 'date') {
} else if (field.fieldType === 'collection') { } else if (field.fieldType === 'collection') {
const ObjectFieldKey = key.includes('.') ? key.split('.')[1] : key; const ObjectFieldKey = key.includes('.') ? key.split('.')[1] : key;
const _valueParser = valueParser(fields, tenantModels); const _valueParser = valueParser(fields, tenantModels);
@@ -456,8 +433,8 @@ export const getMapToPath = (to: string, group = '') =>
group ? `${group}.${to}` : to; group ? `${group}.${to}` : to;
export const getImportsStoragePath = () => { export const getImportsStoragePath = () => {
return path.join(global.__storage_dir, `/imports`); return path.join(global.__storage_dir, `/imports`);
}; }
/** /**
* Deletes the imported file from the storage and database. * Deletes the imported file from the storage and database.
@@ -480,19 +457,3 @@ export const readImportFile = (filename: string) => {
return fs.readFile(`${filePath}/${filename}`); return fs.readFile(`${filePath}/${filename}`);
}; };
/**
* Converts an array of mapping objects to a structured object.
* @param {Array<Object>} mappings - Array of mapping objects.
* @returns {Object} - Structured object based on the mappings.
*/
export const convertMappingsToObject = (mappings) => {
return mappings.reduce((acc, mapping) => {
const { to, group } = mapping;
const key = group ? `['${group}.${to}']` : to;
set(acc, key, mapping);
return acc;
}, {});
};

View File

@@ -0,0 +1,56 @@
import XLSX from 'xlsx';
import { first } from 'lodash';
/**
* Parses the given sheet buffer to worksheet.
* @param {Buffer} buffer
* @returns {XLSX.WorkSheet}
*/
export function parseFirstSheet(buffer: Buffer): XLSX.WorkSheet {
const workbook = XLSX.read(buffer, { type: 'buffer', raw: true });
const firstSheetName = workbook.SheetNames[0];
const worksheet = workbook.Sheets[firstSheetName];
return worksheet;
}
/**
* Extracts the given worksheet to columns.
* @param {XLSX.WorkSheet} worksheet
* @returns {Array<string>}
*/
export function extractSheetColumns(worksheet: XLSX.WorkSheet): Array<string> {
// By default, sheet_to_json scans the first row and uses the values as headers.
// With the header: 1 option, the function exports an array of arrays of values.
const sheetCells = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
const sheetCols = first(sheetCells) as Array<string>;
return sheetCols.filter((col) => col);
}
/**
* Parses the given worksheet to json values. the keys are columns labels.
* @param {XLSX.WorkSheet} worksheet
* @returns {Array<Record<string, string>>}
*/
export function parseSheetToJson(
worksheet: XLSX.WorkSheet
): Array<Record<string, string>> {
return XLSX.utils.sheet_to_json(worksheet, {});
}
/**
* Parses the given sheet buffer then retrieves the sheet data and columns.
* @param {Buffer} buffer
*/
export function parseSheetData(
buffer: Buffer
): [Array<Record<string, string>>, string[]] {
const worksheet = parseFirstSheet(buffer);
const columns = extractSheetColumns(worksheet);
const data = parseSheetToJson(worksheet);
return [data, columns];
}

View File

@@ -92,7 +92,7 @@ export default class InventorySubscriber {
inventoryTransactions, inventoryTransactions,
trx, trx,
}: IInventoryTransactionsCreatedPayload) => { }: IInventoryTransactionsCreatedPayload) => {
const inImportPreviewScope = this.importAls.isImportPreview(); const inImportPreviewScope = this.importAls.isImportPreview;
// Avoid running the cost items job if the async process is in import preview. // Avoid running the cost items job if the async process is in import preview.
if (inImportPreviewScope) return; if (inImportPreviewScope) return;

View File

@@ -1,4 +1,5 @@
// @ts-nocheck // @ts-nocheck
import { lazy, Suspense } from 'react';
import { Router, Switch, Route } from 'react-router'; import { Router, Switch, Route } from 'react-router';
import { createBrowserHistory } from 'history'; import { createBrowserHistory } from 'history';
import { QueryClientProvider, QueryClient } from 'react-query'; import { QueryClientProvider, QueryClient } from 'react-query';
@@ -11,25 +12,26 @@ import 'moment/locale/es-us';
import AppIntlLoader from './AppIntlLoader'; import AppIntlLoader from './AppIntlLoader';
import { EnsureAuthenticated } from '@/components/Guards/EnsureAuthenticated'; import { EnsureAuthenticated } from '@/components/Guards/EnsureAuthenticated';
import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors'; import GlobalErrors from '@/containers/GlobalErrors/GlobalErrors';
import DashboardPrivatePages from '@/components/Dashboard/PrivatePages';
import { Authentication } from '@/containers/Authentication/Authentication';
import LazyLoader from '@/components/LazyLoader';
import { SplashScreen, DashboardThemeProvider } from '../components'; import { SplashScreen, DashboardThemeProvider } from '../components';
import { queryConfig } from '../hooks/query/base'; import { queryConfig } from '../hooks/query/base';
import { EnsureUserEmailVerified } from './Guards/EnsureUserEmailVerified';
import { EnsureAuthNotAuthenticated } from './Guards/EnsureAuthNotAuthenticated';
import { EnsureUserEmailNotVerified } from './Guards/EnsureUserEmailNotVerified'; import { EnsureUserEmailNotVerified } from './Guards/EnsureUserEmailNotVerified';
const EmailConfirmation = LazyLoader({ const DashboardPrivatePages = lazy(
loader: () => import('@/containers/Authentication/EmailConfirmation'), () => import('@/components/Dashboard/PrivatePages'),
}); );
const RegisterVerify = LazyLoader({ const AuthenticationPage = lazy(
loader: () => import('@/containers/Authentication/RegisterVerify'), () => import('@/containers/Authentication/AuthenticationPage'),
}); );
const OneClickDemoPage = LazyLoader({ const EmailConfirmation = lazy(
loader: () => import('@/containers/OneClickDemo/OneClickDemoPage'), () => import('@/containers/Authentication/EmailConfirmation'),
}); );
const RegisterVerify = lazy(
() => import('@/containers/Authentication/RegisterVerify'),
);
const OneClickDemoPage = lazy(
() => import('@/containers/OneClickDemo/OneClickDemoPage'),
);
/** /**
* App inner. * App inner.
@@ -38,36 +40,27 @@ function AppInsider({ history }) {
return ( return (
<div className="App"> <div className="App">
<DashboardThemeProvider> <DashboardThemeProvider>
<Router history={history}> <Suspense fallback={'Loading...'}>
<Switch> <Router history={history}>
<Route path={'/one_click_demo'} children={<OneClickDemoPage />} /> <Switch>
<Route path={'/auth/register/verify'}> <Route path={'/one_click_demo'} children={<OneClickDemoPage />} />
<EnsureAuthenticated> <Route path={'/auth/register/verify'}>
<EnsureUserEmailNotVerified> <EnsureAuthenticated>
<RegisterVerify /> <EnsureUserEmailNotVerified>
</EnsureUserEmailNotVerified> <RegisterVerify />
</EnsureAuthenticated> </EnsureUserEmailNotVerified>
</Route> </EnsureAuthenticated>
</Route>
<Route path={'/auth/email_confirmation'}> <Route
<EmailConfirmation /> path={'/auth/email_confirmation'}
</Route> children={<EmailConfirmation />}
/>
<Route path={'/auth'}> <Route path={'/auth'} children={<AuthenticationPage />} />
<EnsureAuthNotAuthenticated> <Route path={'/'} children={<DashboardPrivatePages />} />
<Authentication /> </Switch>
</EnsureAuthNotAuthenticated> </Router>
</Route> </Suspense>
<Route path={'/'}>
<EnsureAuthenticated>
<EnsureUserEmailVerified>
<DashboardPrivatePages />
</EnsureUserEmailVerified>
</EnsureAuthenticated>
</Route>
</Switch>
</Router>
<GlobalErrors /> <GlobalErrors />
</DashboardThemeProvider> </DashboardThemeProvider>

View File

@@ -1,35 +1,37 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { lazy } from 'react';
import { Switch, Route } from 'react-router'; import { Switch, Route } from 'react-router';
import Dashboard from '@/components/Dashboard/Dashboard'; import Dashboard from '@/components/Dashboard/Dashboard';
import SetupWizardPage from '@/containers/Setup/WizardSetupPage';
import EnsureOrganizationIsReady from '../Guards/EnsureOrganizationIsReady';
import EnsureOrganizationIsNotReady from '../Guards/EnsureOrganizationIsNotReady';
import { PrivatePagesProvider } from './PrivatePagesProvider'; import { PrivatePagesProvider } from './PrivatePagesProvider';
import EnsureOrganizationIsReady from '../Guards/EnsureOrganizationIsReady';
import { EnsureAuthenticated } from '../Guards/EnsureAuthenticated';
import { EnsureUserEmailVerified } from '../Guards/EnsureUserEmailVerified';
import '@/style/pages/Dashboard/Dashboard.scss'; import '@/style/pages/Dashboard/Dashboard.scss';
const SetupWizardPage = lazy(
() => import('@/containers/Setup/WizardSetupPage'),
);
/** /**
* Dashboard inner private pages. * Dashboard inner private pages.
*/ */
export default function DashboardPrivatePages() { export default function DashboardPrivatePages() {
return ( return (
<PrivatePagesProvider> <EnsureAuthenticated>
<Switch> <EnsureUserEmailVerified>
<Route path={'/setup'}> <PrivatePagesProvider>
<EnsureOrganizationIsNotReady> <Switch>
<SetupWizardPage /> <Route path={'/setup'} children={<SetupWizardPage />} />
</EnsureOrganizationIsNotReady> <Route path="/">
</Route> <EnsureOrganizationIsReady>
<Dashboard />
<Route path="/"> </EnsureOrganizationIsReady>
<EnsureOrganizationIsReady> </Route>
<Dashboard /> </Switch>
</EnsureOrganizationIsReady> </PrivatePagesProvider>
</Route> </EnsureUserEmailVerified>
</Switch> </EnsureAuthenticated>
</PrivatePagesProvider>
); );
} }

View File

@@ -1,22 +0,0 @@
// @ts-nocheck
import * as React from 'react';
import * as Loadable from 'react-loadable';
const Loader = (config) =>
Loadable({
loading: (props) => {
if (props.error) {
/* tslint:disable */
console.error(`======= DefaultLoader Error =======`);
console.error(props.error);
console.error(`======= DefaultLoader Error =======`);
/* tslint:enable */
return null;
}
return null;
},
delay: 250,
...config
});
export default Loader;

View File

@@ -1,21 +1,33 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React, { Suspense } from 'react';
import { Route, Switch } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import preferencesRoutes from '@/routes/preferences'; import { getPreferenceRoutes } from '@/routes/preferences';
import { Spinner } from '@blueprintjs/core';
import { Box } from '../Layout';
export default function DashboardContentRoute() { export default function DashboardContentRoute() {
const preferencesRoutes = getPreferenceRoutes();
return ( return (
<Route pathname="/preferences"> <Route pathname="/preferences">
<Switch> <Suspense
{preferencesRoutes.map((route, index) => ( fallback={
<Route <Box style={{ padding: 20 }}>
key={index} <Spinner size={20} />
path={`${route.path}`} </Box>
exact={route.exact} }
component={route.component} >
/> <Switch>
))} {preferencesRoutes.map((route, index) => (
</Switch> <Route
key={index}
path={`${route.path}`}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</Suspense>
</Route> </Route>
); );
} }

View File

@@ -2,10 +2,12 @@
import { Route, Switch, useLocation } from 'react-router-dom'; import { Route, Switch, useLocation } from 'react-router-dom';
import BodyClassName from 'react-body-classname'; import BodyClassName from 'react-body-classname';
import styled from 'styled-components'; import styled from 'styled-components';
import { Suspense } from 'react';
import { TransitionGroup, CSSTransition } from 'react-transition-group'; import { TransitionGroup, CSSTransition } from 'react-transition-group';
import { Spinner } from '@blueprintjs/core';
import authenticationRoutes from '@/routes/authentication'; import authenticationRoutes from '@/routes/authentication';
import { Icon, FormattedMessage as T } from '@/components'; import { Box, Icon, FormattedMessage as T } from '@/components';
import { AuthMetaBootProvider } from './AuthMetaBoot'; import { AuthMetaBootProvider } from './AuthMetaBoot';
import '@/style/pages/Authentication/Auth.scss'; import '@/style/pages/Authentication/Auth.scss';
@@ -20,7 +22,15 @@ export function Authentication() {
</AuthLogo> </AuthLogo>
<AuthMetaBootProvider> <AuthMetaBootProvider>
<AuthenticationRoutes /> <Suspense
fallback={
<Box style={{ marginTop: '5rem' }}>
<Spinner size={30} />
</Box>
}
>
<AuthenticationRoutes />
</Suspense>
</AuthMetaBootProvider> </AuthMetaBootProvider>
</AuthInsider> </AuthInsider>
</AuthPage> </AuthPage>

View File

@@ -0,0 +1,10 @@
import { EnsureAuthNotAuthenticated } from "@/components/Guards/EnsureAuthNotAuthenticated";
import { Authentication } from "./Authentication";
export default function AuthenticationPage() {
return (
<EnsureAuthNotAuthenticated>
<Authentication />
</EnsureAuthNotAuthenticated>
);
}

View File

@@ -14,7 +14,7 @@
th.label, th.label,
td.label{ td.label{
width: 30% !important; width: 32% !important;
} }
thead{ thead{
@@ -31,14 +31,16 @@
tr td { tr td {
vertical-align: middle; vertical-align: middle;
} }
tr td{
:global(.bp4-popover-target .bp4-button),
:global(.bp4-popover-wrapper){
max-width: 250px;
}
}
} }
} }
.requiredSign{ .requiredSign{
color: rgb(250, 82, 82); color: rgb(250, 82, 82);
} }
.columnSelectButton{
max-width: 250px;
min-width: 250px;
}

View File

@@ -8,12 +8,9 @@ import { EntityColumnField, useImportFileContext } from './ImportFileProvider';
import { CLASSES } from '@/constants'; import { CLASSES } from '@/constants';
import { ImportFileContainer } from './ImportFileContainer'; import { ImportFileContainer } from './ImportFileContainer';
import { ImportStepperStep } from './_types'; import { ImportStepperStep } from './_types';
import { import { ImportFileMapBootProvider } from './ImportFileMappingBoot';
ImportFileMapBootProvider,
useImportFileMapBootContext,
} from './ImportFileMappingBoot';
import styles from './ImportFileMapping.module.scss'; import styles from './ImportFileMapping.module.scss';
import { getDateFieldKey, getFieldKey } from './_utils'; import { getFieldKey } from './_utils';
export function ImportFileMapping() { export function ImportFileMapping() {
const { importId, entityColumns } = useImportFileContext(); const { importId, entityColumns } = useImportFileContext();
@@ -85,7 +82,6 @@ interface ImportFileMappingFieldsProps {
*/ */
function ImportFileMappingFields({ fields }: ImportFileMappingFieldsProps) { function ImportFileMappingFields({ fields }: ImportFileMappingFieldsProps) {
const { sheetColumns } = useImportFileContext(); const { sheetColumns } = useImportFileContext();
const { dateFormats } = useImportFileMapBootContext();
const items = useMemo( const items = useMemo(
() => sheetColumns.map((column) => ({ value: column, text: column })), () => sheetColumns.map((column) => ({ value: column, text: column })),
@@ -99,35 +95,22 @@ function ImportFileMappingFields({ fields }: ImportFileMappingFieldsProps) {
{column.required && <span className={styles.requiredSign}>*</span>} {column.required && <span className={styles.requiredSign}>*</span>}
</td> </td>
<td className={styles.field}> <td className={styles.field}>
<Group spacing={12} noWrap> <Group spacing={4}>
<FSelect <FSelect
name={`['${getFieldKey(column.key, column.group)}'].from`} name={getFieldKey(column.key, column.group)}
items={items} items={items}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
minimal={true} minimal={true}
fill={true} fill={true}
className={styles.columnSelectButton}
/> />
{column.hint && ( {column.hint && (
<Hint content={column.hint} position={Position.BOTTOM} /> <Hint content={column.hint} position={Position.BOTTOM} />
)} )}
{column.type === 'date' && (
<FSelect
name={getDateFieldKey(column.key, column.group)}
items={dateFormats}
placeholder={'Select date format'}
minimal={true}
fill={true}
valueAccessor={'key'}
textAccessor={'label'}
labelAccessor={''}
/>
)}
</Group> </Group>
</td> </td>
</tr> </tr>
), ),
[items, dateFormats], [items],
); );
const columns = useMemo( const columns = useMemo(
() => fields.map(columnMapper), () => fields.map(columnMapper),

View File

@@ -2,11 +2,8 @@ import { Spinner } from '@blueprintjs/core';
import React, { createContext, useContext } from 'react'; import React, { createContext, useContext } from 'react';
import { Box } from '@/components'; import { Box } from '@/components';
import { useImportFileMeta } from '@/hooks/query/import'; import { useImportFileMeta } from '@/hooks/query/import';
import { useDateFormats } from '@/hooks/query';
interface ImportFileMapBootContextValue { interface ImportFileMapBootContextValue {}
dateFormats: Array<any>;
}
const ImportFileMapBootContext = createContext<ImportFileMapBootContextValue>( const ImportFileMapBootContext = createContext<ImportFileMapBootContextValue>(
{} as ImportFileMapBootContextValue, {} as ImportFileMapBootContextValue,
@@ -42,22 +39,14 @@ export const ImportFileMapBootProvider = ({
enabled: Boolean(importId), enabled: Boolean(importId),
}); });
// Fetch date format options.
const { data: dateFormats, isLoading: isDateFormatsLoading } =
useDateFormats();
const value = { const value = {
importFile, importFile,
isImportFileLoading, isImportFileLoading,
isImportFileFetching, isImportFileFetching,
dateFormats,
isDateFormatsLoading,
}; };
const isLoading = isDateFormatsLoading || isImportFileLoading;
return ( return (
<ImportFileMapBootContext.Provider value={value}> <ImportFileMapBootContext.Provider value={value}>
{isLoading ? ( {isImportFileLoading ? (
<Box style={{ padding: '2rem', textAlign: 'center' }}> <Box style={{ padding: '2rem', textAlign: 'center' }}>
<Spinner size={26} /> <Spinner size={26} />
</Box> </Box>

View File

@@ -13,7 +13,6 @@ export type EntityColumnField = {
required?: boolean; required?: boolean;
hint?: string; hint?: string;
group?: string; group?: string;
type: string;
}; };
export interface EntityColumn { export interface EntityColumn {

View File

@@ -6,8 +6,8 @@
flex: 1; flex: 1;
padding: 32px 20px; padding: 32px 20px;
padding-bottom: 80px; padding-bottom: 80px;
min-width: 800px; min-width: 660px;
max-width: 800px; max-width: 760px;
width: 75%; width: 75%;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;

View File

@@ -12,13 +12,4 @@ export interface ImportFileMappingFormProps {
children: React.ReactNode; children: React.ReactNode;
} }
export type ImportFileMappingFormValues = Record< export type ImportFileMappingFormValues = Record<string, string | null>;
string,
{ from: string | null; dateFormat?: string }
>;
export type ImportFileMappingRes = {
from: string;
to: string;
group: string;
}[];

View File

@@ -17,15 +17,13 @@ import {
} from './ImportFileProvider'; } from './ImportFileProvider';
import { useImportFileMapBootContext } from './ImportFileMappingBoot'; import { useImportFileMapBootContext } from './ImportFileMappingBoot';
import { deepdash, transformToForm } from '@/utils'; import { deepdash, transformToForm } from '@/utils';
import { ImportFileMappingFormValues, ImportFileMappingRes } from './_types'; import { ImportFileMappingFormValues } from './_types';
export const getFieldKey = (key: string, group = '') => { export const getFieldKey = (key: string, group = '') => {
return group ? `${group}.${key}` : key; return group ? `${group}.${key}` : key;
}; };
export const getDateFieldKey = (key: string, group: string = '') => { type ImportFileMappingRes = { from: string; to: string; group: string }[];
return `${getFieldKey(key, group)}.dateFormat`;
};
/** /**
* Transformes the mapping form values to request. * Transformes the mapping form values to request.
@@ -36,10 +34,10 @@ export const transformValueToReq = (
value: ImportFileMappingFormValues, value: ImportFileMappingFormValues,
): { mapping: ImportFileMappingRes[] } => { ): { mapping: ImportFileMappingRes[] } => {
const mapping = chain(value) const mapping = chain(value)
.pickBy((_value, key) => !isEmpty(_value) && _value?.from) .thru(deepdash.index)
.map((_value, key) => ({ .pickBy((_value, key) => !isEmpty(get(value, key)))
from: _value.from, .map((from, key) => ({
dateFormat: _value.dateFormat, from,
to: key.includes('.') ? last(key.split('.')) : key, to: key.includes('.') ? last(key.split('.')) : key,
group: key.includes('.') ? head(key.split('.')) : '', group: key.includes('.') ? head(key.split('.')) : '',
})) }))
@@ -54,15 +52,11 @@ export const transformValueToReq = (
* @returns {Record<string, object | string>} * @returns {Record<string, object | string>}
*/ */
export const transformResToFormValues = ( export const transformResToFormValues = (
value: { from: string; to: string; group: string }[], value: { from: string; to: string , group: string }[],
): Record<string, object | string> => { ): Record<string, object | string> => {
return value?.reduce((acc, map) => { return value?.reduce((acc, map) => {
const path = map?.group ? `['${map.group}.${map.to}']` : map.to; const path = map?.group ? `${map.group}.${map.to}` : map.to;
const dateFormatObj = map?.dateFormat set(acc, path, map.from);
? { dateFormat: map?.dateFormat }
: {};
set(acc, path, { from: map?.from, ...dateFormatObj });
return acc; return acc;
}, {}); }, {});
}; };
@@ -82,10 +76,10 @@ const getInitialDefaultValues = (
const _matched = sheetColumns.find( const _matched = sheetColumns.find(
(column) => lowerCase(column) === _name, (column) => lowerCase(column) === _name,
); );
const path = groupKey ? `['${groupKey}.${key}']` : key; const _key = groupKey ? `${groupKey}.${key}` : key;
const from = _matched ? _matched : ''; const _value = _matched ? _matched : '';
set(acc, path, { from }); set(acc, _key, _value);
}); });
return acc; return acc;
}, {}); }, {});
@@ -108,6 +102,7 @@ export const useImportFileMappingInitialValues = () => {
() => getInitialDefaultValues(entityColumns, sheetColumns), () => getInitialDefaultValues(entityColumns, sheetColumns),
[entityColumns, sheetColumns], [entityColumns, sheetColumns],
); );
return useMemo<Record<string, any>>( return useMemo<Record<string, any>>(
() => () =>
assign( assign(

View File

@@ -73,8 +73,9 @@ export function OneClickDemoPageContent() {
)} )}
{running && ( {running && (
<Text className={style.waitingText}> <Text className={style.waitingText}>
We're preparing temporary environment for trial, It typically We're preparing the temporary environment for trial. It
take few seconds. Do not close or refresh the page. typically takes a few seconds. Do not close or refresh the
page.
</Text> </Text>
)} )}
</Stack> </Stack>

View File

@@ -2,14 +2,17 @@
import React from 'react'; import React from 'react';
import SetupRightSection from './SetupRightSection'; import SetupRightSection from './SetupRightSection';
import SetupLeftSection from './SetupLeftSection'; import SetupLeftSection from './SetupLeftSection';
import EnsureOrganizationIsNotReady from '@/components/Guards/EnsureOrganizationIsNotReady';
import '@/style/pages/Setup/SetupPage.scss'; import '@/style/pages/Setup/SetupPage.scss';
export default function WizardSetupPage() { export default function WizardSetupPage() {
return ( return (
<div class="setup-page"> <EnsureOrganizationIsNotReady>
<SetupLeftSection /> <div class="setup-page">
<SetupRightSection /> <SetupLeftSection />
</div> <SetupRightSection />
</div>
</EnsureOrganizationIsNotReady>
); );
}; }

View File

@@ -1,43 +1,35 @@
// @ts-nocheck // @ts-nocheck
import LazyLoader from '@/components/LazyLoader'; import { lazy } from 'react';
const BASE_URL = '/auth'; const BASE_URL = '/auth';
export default [ export default [
{ {
path: `${BASE_URL}/login`, path: `${BASE_URL}/login`,
component: LazyLoader({ component: lazy(() => import('@/containers/Authentication/Login')),
loader: () => import('@/containers/Authentication/Login'),
}),
}, },
{ {
path: `${BASE_URL}/send_reset_password`, path: `${BASE_URL}/send_reset_password`,
component: LazyLoader({ component: lazy(
loader: () => import('@/containers/Authentication/SendResetPassword'), () => import('@/containers/Authentication/SendResetPassword'),
}), ),
}, },
{ {
path: `${BASE_URL}/reset_password/:token`, path: `${BASE_URL}/reset_password/:token`,
component: LazyLoader({ component: lazy(() => import('@/containers/Authentication/ResetPassword')),
loader: () => import('@/containers/Authentication/ResetPassword'),
}),
}, },
{ {
path: `${BASE_URL}/invite/:token/accept`, path: `${BASE_URL}/invite/:token/accept`,
component: LazyLoader({ component: lazy(() => import('@/containers/Authentication/InviteAccept')),
loader: () => import('@/containers/Authentication/InviteAccept'),
}),
}, },
{ {
path: `${BASE_URL}/register/email_confirmation`, path: `${BASE_URL}/register/email_confirmation`,
component: LazyLoader({ component: lazy(
loader: () => import('@/containers/Authentication/EmailConfirmation'), () => import('@/containers/Authentication/EmailConfirmation'),
}), ),
}, },
{ {
path: `${BASE_URL}/register`, path: `${BASE_URL}/register`,
component: LazyLoader({ component: lazy(() => import('@/containers/Authentication/Register')),
loader: () => import('@/containers/Authentication/Register'),
}),
}, },
]; ];

View File

@@ -1,88 +1,96 @@
// @ts-nocheck // @ts-nocheck
import { lazy } from 'react'; import { lazy } from 'react';
import General from '@/containers/Preferences/General/General';
import Users from '../containers/Preferences/Users/Users';
import Roles from '../containers/Preferences/Users/Roles/RolesForm/RolesFormPage';
import Accountant from '@/containers/Preferences/Accountant/Accountant';
import Currencies from '@/containers/Preferences/Currencies/Currencies';
import Item from '@/containers/Preferences/Item';
// import SMSIntegration from '../containers/Preferences/SMSIntegration';
import DefaultRoute from '../containers/Preferences/DefaultRoute';
import Warehouses from '../containers/Preferences/Warehouses';
import Branches from '../containers/Preferences/Branches';
import Invoices from '../containers/Preferences/Invoices/PreferencesInvoices';
import { PreferencesCreditNotes } from '../containers/Preferences/CreditNotes/PreferencesCreditNotes';
import { PreferencesEstimates } from '@/containers/Preferences/Estimates/PreferencesEstimates';
import { PreferencesReceipts } from '@/containers/Preferences/Receipts/PreferencesReceipts';
import BillingPage from '@/containers/Subscriptions/BillingPage';
const BASE_URL = '/preferences'; const BASE_URL = '/preferences';
export default [ export const getPreferenceRoutes = () => [
{ {
path: `${BASE_URL}/general`, path: `${BASE_URL}/general`,
component: General, component: lazy(() => import('@/containers/Preferences/General/General')),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/users`, path: `${BASE_URL}/users`,
component: Users, component: lazy(() => import('../containers/Preferences/Users/Users')),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/invoices`, path: `${BASE_URL}/invoices`,
component: Invoices, component: lazy(
() => import('../containers/Preferences/Invoices/PreferencesInvoices'),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/credit-notes`, path: `${BASE_URL}/credit-notes`,
component: PreferencesCreditNotes, component: lazy(() =>
import(
'../containers/Preferences/CreditNotes/PreferencesCreditNotes'
).then((module) => ({ default: module.PreferencesCreditNotes })),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/estimates`, path: `${BASE_URL}/estimates`,
component: PreferencesEstimates, component: lazy(() =>
import('@/containers/Preferences/Estimates/PreferencesEstimates').then(
(module) => ({ default: module.PreferencesEstimates }),
),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/receipts`, path: `${BASE_URL}/receipts`,
component: PreferencesReceipts, component: lazy(() =>
import('@/containers/Preferences/Receipts/PreferencesReceipts').then(
(module) => ({ default: module.PreferencesReceipts }),
),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/roles`, path: `${BASE_URL}/roles`,
component: Roles, component: lazy(
() =>
import('../containers/Preferences/Users/Roles/RolesForm/RolesFormPage'),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/roles/:id`, path: `${BASE_URL}/roles/:id`,
component: Roles, component: lazy(
() =>
import('../containers/Preferences/Users/Roles/RolesForm/RolesFormPage'),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/currencies`, path: `${BASE_URL}/currencies`,
component: Currencies, component: lazy(
() => import('@/containers/Preferences/Currencies/Currencies'),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/warehouses`, path: `${BASE_URL}/warehouses`,
component: Warehouses, component: lazy(() => import('../containers/Preferences/Warehouses')),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/branches`, path: `${BASE_URL}/branches`,
component: Branches, component: lazy(() => import('../containers/Preferences/Branches')),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/accountant`, path: `${BASE_URL}/accountant`,
component: Accountant, component: lazy(
() => import('@/containers/Preferences/Accountant/Accountant'),
),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/items`, path: `${BASE_URL}/items`,
component: Item, component: lazy(() => import('@/containers/Preferences/Item')),
exact: true, exact: true,
}, },
// { // {
@@ -92,12 +100,12 @@ export default [
// }, // },
{ {
path: `${BASE_URL}/billing`, path: `${BASE_URL}/billing`,
component: BillingPage, component: lazy(() => import('@/containers/Subscriptions/BillingPage')),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/`, path: `${BASE_URL}/`,
component: DefaultRoute, component: lazy(() => import('../containers/Preferences/DefaultRoute')),
exact: true, exact: true,
}, },
]; ];