mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
Compare commits
1 Commits
fix-gettin
...
date-forma
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e6f1efe30 |
@@ -40,6 +40,7 @@ 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)),
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ 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,35 +155,23 @@ export class Transformer {
|
|||||||
this.dateFormat = format;
|
this.dateFormat = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Format date.
|
|
||||||
* @param {} date
|
|
||||||
* @param {string} format -
|
|
||||||
* @returns {}
|
|
||||||
*/
|
|
||||||
protected formatDate(date, format?: string) {
|
|
||||||
// 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) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param date
|
* @param date
|
||||||
* @returns {}
|
* @returns
|
||||||
*/
|
*/
|
||||||
protected formatDateFromNow(date) {
|
protected formatDate(date) {
|
||||||
|
return date ? moment(date).format(this.dateFormat) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
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 });
|
||||||
@@ -194,7 +181,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, {
|
||||||
|
|||||||
@@ -3,17 +3,12 @@ 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
|
||||||
@@ -22,12 +17,10 @@ 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,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,11 +95,6 @@ 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',
|
||||||
@@ -140,117 +135,116 @@ 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: {
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
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');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,7 +10,6 @@ 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 {
|
||||||
@@ -23,33 +22,13 @@ 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 exportAlsRun(
|
public async export(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
resourceName: string,
|
resourceName: string,
|
||||||
format: ExportFormat = ExportFormat.Csv
|
format: ExportFormat = ExportFormat.Csv
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
export const EXPORT_SIZE_LIMIT = 9999999;
|
export const EXPORT_SIZE_LIMIT = 9999999;
|
||||||
export const EXPORT_DTE_FORMAT = 'YYYY-MM-DD';
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ 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.
|
||||||
@@ -83,7 +82,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 get isImport(): boolean {
|
public isImport(): boolean {
|
||||||
return !!this.getStore()?.get('isImport');
|
return !!this.getStore()?.get('isImport');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +90,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 get isImportCommit(): boolean {
|
public isImportCommit(): boolean {
|
||||||
return !!this.getStore()?.get('isImportCommit');
|
return !!this.getStore()?.get('isImportCommit');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +98,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 get isImportPreview(): boolean {
|
public isImportPreview(): boolean {
|
||||||
return !!this.getStore()?.get('isImportPreview');
|
return !!this.getStore()?.get('isImportPreview');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
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';
|
||||||
@@ -11,7 +12,11 @@ import {
|
|||||||
ImportableContext,
|
ImportableContext,
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import { ServiceError } from '@/exceptions';
|
import { ServiceError } from '@/exceptions';
|
||||||
import { getUniqueImportableValue, trimObject } from './_utils';
|
import {
|
||||||
|
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';
|
||||||
@@ -27,6 +32,21 @@ 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 -
|
||||||
@@ -65,11 +85,14 @@ 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.
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces';
|
import {
|
||||||
|
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';
|
||||||
@@ -24,9 +28,13 @@ 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(importableFields);
|
const YupSchema = convertFieldsToYupValidation(
|
||||||
|
importableFields,
|
||||||
|
mappingSettings
|
||||||
|
);
|
||||||
const _data = { ...data };
|
const _data = { ...data };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -2,14 +2,18 @@ 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 { ERRORS, getUnmappedSheetColumns, readImportFile } from './_utils';
|
import {
|
||||||
|
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 {
|
||||||
@@ -45,10 +49,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 and parse the given buffer to get columns
|
// Read the imported file.
|
||||||
// and sheet data in json format.
|
|
||||||
const buffer = await readImportFile(importFile.filename);
|
const buffer = await readImportFile(importFile.filename);
|
||||||
const [sheetData, sheetColumns] = parseSheetData(buffer);
|
const sheetData = this.importCommon.parseXlsxSheet(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);
|
||||||
@@ -83,7 +87,7 @@ export class ImportFileProcess {
|
|||||||
.flatten()
|
.flatten()
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
const unmappedColumns = getUnmappedSheetColumns(sheetColumns, mapping);
|
const unmappedColumns = getUnmappedSheetColumns(header, mapping);
|
||||||
const totalCount = allData.length;
|
const totalCount = allData.length;
|
||||||
|
|
||||||
const createdCount = successedImport.length;
|
const createdCount = successedImport.length;
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ 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 {
|
||||||
@@ -78,12 +77,14 @@ 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, sheetColumns] = parseSheetData(buffer);
|
const sheetData = this.importFileCommon.parseXlsxSheet(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);
|
||||||
|
|||||||
@@ -17,9 +17,10 @@ import {
|
|||||||
head,
|
head,
|
||||||
split,
|
split,
|
||||||
last,
|
last,
|
||||||
|
set,
|
||||||
} from 'lodash';
|
} from 'lodash';
|
||||||
import pluralize from 'pluralize';
|
import pluralize from 'pluralize';
|
||||||
import { ResourceMetaFieldsMap } from './interfaces';
|
import { ImportMappingAttr, 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';
|
||||||
@@ -58,11 +59,19 @@ export function trimObject(obj: Record<string, string | number>) {
|
|||||||
* @param {ResourceMetaFieldsMap} fields
|
* @param {ResourceMetaFieldsMap} fields
|
||||||
* @returns {Yup}
|
* @returns {Yup}
|
||||||
*/
|
*/
|
||||||
export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
|
export const convertFieldsToYupValidation = (
|
||||||
|
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);
|
||||||
|
|
||||||
@@ -105,13 +114,23 @@ export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
|
|||||||
if (!val) {
|
if (!val) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return moment(val, 'YYYY-MM-DD', true).isValid();
|
const fieldDateFormat =
|
||||||
|
(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(field.fields);
|
const nestedFieldShema = convertFieldsToYupValidation(
|
||||||
|
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)) {
|
||||||
@@ -258,6 +277,7 @@ 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;
|
||||||
@@ -270,6 +290,7 @@ export const getResourceColumns = (resourceColumns: {
|
|||||||
name,
|
name,
|
||||||
required,
|
required,
|
||||||
hint: importHint,
|
hint: importHint,
|
||||||
|
type,
|
||||||
order,
|
order,
|
||||||
...extra,
|
...extra,
|
||||||
};
|
};
|
||||||
@@ -322,6 +343,8 @@ 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);
|
||||||
@@ -433,8 +456,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.
|
||||||
@@ -457,3 +480,19 @@ 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;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
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];
|
|
||||||
}
|
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// @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';
|
||||||
@@ -12,26 +11,25 @@ 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 DashboardPrivatePages = lazy(
|
const EmailConfirmation = LazyLoader({
|
||||||
() => import('@/components/Dashboard/PrivatePages'),
|
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
||||||
);
|
});
|
||||||
const AuthenticationPage = lazy(
|
const RegisterVerify = LazyLoader({
|
||||||
() => import('@/containers/Authentication/AuthenticationPage'),
|
loader: () => import('@/containers/Authentication/RegisterVerify'),
|
||||||
);
|
});
|
||||||
const EmailConfirmation = lazy(
|
const OneClickDemoPage = LazyLoader({
|
||||||
() => import('@/containers/Authentication/EmailConfirmation'),
|
loader: () => import('@/containers/OneClickDemo/OneClickDemoPage'),
|
||||||
);
|
});
|
||||||
const RegisterVerify = lazy(
|
|
||||||
() => import('@/containers/Authentication/RegisterVerify'),
|
|
||||||
);
|
|
||||||
const OneClickDemoPage = lazy(
|
|
||||||
() => import('@/containers/OneClickDemo/OneClickDemoPage'),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App inner.
|
* App inner.
|
||||||
@@ -40,27 +38,36 @@ function AppInsider({ history }) {
|
|||||||
return (
|
return (
|
||||||
<div className="App">
|
<div className="App">
|
||||||
<DashboardThemeProvider>
|
<DashboardThemeProvider>
|
||||||
<Suspense fallback={'Loading...'}>
|
<Router history={history}>
|
||||||
<Router history={history}>
|
<Switch>
|
||||||
<Switch>
|
<Route path={'/one_click_demo'} children={<OneClickDemoPage />} />
|
||||||
<Route path={'/one_click_demo'} children={<OneClickDemoPage />} />
|
<Route path={'/auth/register/verify'}>
|
||||||
<Route path={'/auth/register/verify'}>
|
<EnsureAuthenticated>
|
||||||
<EnsureAuthenticated>
|
<EnsureUserEmailNotVerified>
|
||||||
<EnsureUserEmailNotVerified>
|
<RegisterVerify />
|
||||||
<RegisterVerify />
|
</EnsureUserEmailNotVerified>
|
||||||
</EnsureUserEmailNotVerified>
|
</EnsureAuthenticated>
|
||||||
</EnsureAuthenticated>
|
</Route>
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route
|
<Route path={'/auth/email_confirmation'}>
|
||||||
path={'/auth/email_confirmation'}
|
<EmailConfirmation />
|
||||||
children={<EmailConfirmation />}
|
</Route>
|
||||||
/>
|
|
||||||
<Route path={'/auth'} children={<AuthenticationPage />} />
|
<Route path={'/auth'}>
|
||||||
<Route path={'/'} children={<DashboardPrivatePages />} />
|
<EnsureAuthNotAuthenticated>
|
||||||
</Switch>
|
<Authentication />
|
||||||
</Router>
|
</EnsureAuthNotAuthenticated>
|
||||||
</Suspense>
|
</Route>
|
||||||
|
|
||||||
|
<Route path={'/'}>
|
||||||
|
<EnsureAuthenticated>
|
||||||
|
<EnsureUserEmailVerified>
|
||||||
|
<DashboardPrivatePages />
|
||||||
|
</EnsureUserEmailVerified>
|
||||||
|
</EnsureAuthenticated>
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
|
</Router>
|
||||||
|
|
||||||
<GlobalErrors />
|
<GlobalErrors />
|
||||||
</DashboardThemeProvider>
|
</DashboardThemeProvider>
|
||||||
|
|||||||
@@ -1,37 +1,35 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { lazy } from 'react';
|
import React 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 { PrivatePagesProvider } from './PrivatePagesProvider';
|
|
||||||
import EnsureOrganizationIsReady from '../Guards/EnsureOrganizationIsReady';
|
import EnsureOrganizationIsReady from '../Guards/EnsureOrganizationIsReady';
|
||||||
import { EnsureAuthenticated } from '../Guards/EnsureAuthenticated';
|
import EnsureOrganizationIsNotReady from '../Guards/EnsureOrganizationIsNotReady';
|
||||||
import { EnsureUserEmailVerified } from '../Guards/EnsureUserEmailVerified';
|
import { PrivatePagesProvider } from './PrivatePagesProvider';
|
||||||
|
|
||||||
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 (
|
||||||
<EnsureAuthenticated>
|
<PrivatePagesProvider>
|
||||||
<EnsureUserEmailVerified>
|
<Switch>
|
||||||
<PrivatePagesProvider>
|
<Route path={'/setup'}>
|
||||||
<Switch>
|
<EnsureOrganizationIsNotReady>
|
||||||
<Route path={'/setup'} children={<SetupWizardPage />} />
|
<SetupWizardPage />
|
||||||
<Route path="/">
|
</EnsureOrganizationIsNotReady>
|
||||||
<EnsureOrganizationIsReady>
|
</Route>
|
||||||
<Dashboard />
|
|
||||||
</EnsureOrganizationIsReady>
|
<Route path="/">
|
||||||
</Route>
|
<EnsureOrganizationIsReady>
|
||||||
</Switch>
|
<Dashboard />
|
||||||
</PrivatePagesProvider>
|
</EnsureOrganizationIsReady>
|
||||||
</EnsureUserEmailVerified>
|
</Route>
|
||||||
</EnsureAuthenticated>
|
</Switch>
|
||||||
|
</PrivatePagesProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
22
packages/webapp/src/components/LazyLoader.tsx
Normal file
22
packages/webapp/src/components/LazyLoader.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
// @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;
|
||||||
@@ -1,33 +1,21 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { Suspense } from 'react';
|
import React from 'react';
|
||||||
import { Route, Switch } from 'react-router-dom';
|
import { Route, Switch } from 'react-router-dom';
|
||||||
import { getPreferenceRoutes } from '@/routes/preferences';
|
import preferencesRoutes 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">
|
||||||
<Suspense
|
<Switch>
|
||||||
fallback={
|
{preferencesRoutes.map((route, index) => (
|
||||||
<Box style={{ padding: 20 }}>
|
<Route
|
||||||
<Spinner size={20} />
|
key={index}
|
||||||
</Box>
|
path={`${route.path}`}
|
||||||
}
|
exact={route.exact}
|
||||||
>
|
component={route.component}
|
||||||
<Switch>
|
/>
|
||||||
{preferencesRoutes.map((route, index) => (
|
))}
|
||||||
<Route
|
</Switch>
|
||||||
key={index}
|
|
||||||
path={`${route.path}`}
|
|
||||||
exact={route.exact}
|
|
||||||
component={route.component}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</Switch>
|
|
||||||
</Suspense>
|
|
||||||
</Route>
|
</Route>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,10 @@
|
|||||||
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 { Box, Icon, FormattedMessage as T } from '@/components';
|
import { 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';
|
||||||
@@ -22,15 +20,7 @@ export function Authentication() {
|
|||||||
</AuthLogo>
|
</AuthLogo>
|
||||||
|
|
||||||
<AuthMetaBootProvider>
|
<AuthMetaBootProvider>
|
||||||
<Suspense
|
<AuthenticationRoutes />
|
||||||
fallback={
|
|
||||||
<Box style={{ marginTop: '5rem' }}>
|
|
||||||
<Spinner size={30} />
|
|
||||||
</Box>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<AuthenticationRoutes />
|
|
||||||
</Suspense>
|
|
||||||
</AuthMetaBootProvider>
|
</AuthMetaBootProvider>
|
||||||
</AuthInsider>
|
</AuthInsider>
|
||||||
</AuthPage>
|
</AuthPage>
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { EnsureAuthNotAuthenticated } from "@/components/Guards/EnsureAuthNotAuthenticated";
|
|
||||||
import { Authentication } from "./Authentication";
|
|
||||||
|
|
||||||
export default function AuthenticationPage() {
|
|
||||||
return (
|
|
||||||
<EnsureAuthNotAuthenticated>
|
|
||||||
<Authentication />
|
|
||||||
</EnsureAuthNotAuthenticated>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
th.label,
|
th.label,
|
||||||
td.label{
|
td.label{
|
||||||
width: 32% !important;
|
width: 30% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
thead{
|
thead{
|
||||||
@@ -31,16 +31,14 @@
|
|||||||
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;
|
||||||
|
}
|
||||||
@@ -8,9 +8,12 @@ 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 { ImportFileMapBootProvider } from './ImportFileMappingBoot';
|
import {
|
||||||
|
ImportFileMapBootProvider,
|
||||||
|
useImportFileMapBootContext,
|
||||||
|
} from './ImportFileMappingBoot';
|
||||||
import styles from './ImportFileMapping.module.scss';
|
import styles from './ImportFileMapping.module.scss';
|
||||||
import { getFieldKey } from './_utils';
|
import { getDateFieldKey, getFieldKey } from './_utils';
|
||||||
|
|
||||||
export function ImportFileMapping() {
|
export function ImportFileMapping() {
|
||||||
const { importId, entityColumns } = useImportFileContext();
|
const { importId, entityColumns } = useImportFileContext();
|
||||||
@@ -82,6 +85,7 @@ 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 })),
|
||||||
@@ -95,22 +99,35 @@ 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={4}>
|
<Group spacing={12} noWrap>
|
||||||
<FSelect
|
<FSelect
|
||||||
name={getFieldKey(column.key, column.group)}
|
name={`['${getFieldKey(column.key, column.group)}'].from`}
|
||||||
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],
|
[items, dateFormats],
|
||||||
);
|
);
|
||||||
const columns = useMemo(
|
const columns = useMemo(
|
||||||
() => fields.map(columnMapper),
|
() => fields.map(columnMapper),
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ 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,
|
||||||
@@ -39,14 +42,22 @@ 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}>
|
||||||
{isImportFileLoading ? (
|
{isLoading ? (
|
||||||
<Box style={{ padding: '2rem', textAlign: 'center' }}>
|
<Box style={{ padding: '2rem', textAlign: 'center' }}>
|
||||||
<Spinner size={26} />
|
<Spinner size={26} />
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export type EntityColumnField = {
|
|||||||
required?: boolean;
|
required?: boolean;
|
||||||
hint?: string;
|
hint?: string;
|
||||||
group?: string;
|
group?: string;
|
||||||
|
type: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface EntityColumn {
|
export interface EntityColumn {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 32px 20px;
|
padding: 32px 20px;
|
||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
min-width: 660px;
|
min-width: 800px;
|
||||||
max-width: 760px;
|
max-width: 800px;
|
||||||
width: 75%;
|
width: 75%;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
|||||||
@@ -12,4 +12,13 @@ export interface ImportFileMappingFormProps {
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ImportFileMappingFormValues = Record<string, string | null>;
|
export type ImportFileMappingFormValues = Record<
|
||||||
|
string,
|
||||||
|
{ from: string | null; dateFormat?: string }
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type ImportFileMappingRes = {
|
||||||
|
from: string;
|
||||||
|
to: string;
|
||||||
|
group: string;
|
||||||
|
}[];
|
||||||
|
|||||||
@@ -17,13 +17,15 @@ 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 } from './_types';
|
import { ImportFileMappingFormValues, ImportFileMappingRes } from './_types';
|
||||||
|
|
||||||
export const getFieldKey = (key: string, group = '') => {
|
export const getFieldKey = (key: string, group = '') => {
|
||||||
return group ? `${group}.${key}` : key;
|
return group ? `${group}.${key}` : key;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ImportFileMappingRes = { from: string; to: string; group: string }[];
|
export const getDateFieldKey = (key: string, group: string = '') => {
|
||||||
|
return `${getFieldKey(key, group)}.dateFormat`;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transformes the mapping form values to request.
|
* Transformes the mapping form values to request.
|
||||||
@@ -34,10 +36,10 @@ export const transformValueToReq = (
|
|||||||
value: ImportFileMappingFormValues,
|
value: ImportFileMappingFormValues,
|
||||||
): { mapping: ImportFileMappingRes[] } => {
|
): { mapping: ImportFileMappingRes[] } => {
|
||||||
const mapping = chain(value)
|
const mapping = chain(value)
|
||||||
.thru(deepdash.index)
|
.pickBy((_value, key) => !isEmpty(_value) && _value?.from)
|
||||||
.pickBy((_value, key) => !isEmpty(get(value, key)))
|
.map((_value, key) => ({
|
||||||
.map((from, key) => ({
|
from: _value.from,
|
||||||
from,
|
dateFormat: _value.dateFormat,
|
||||||
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('.')) : '',
|
||||||
}))
|
}))
|
||||||
@@ -52,11 +54,15 @@ 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;
|
||||||
set(acc, path, map.from);
|
const dateFormatObj = map?.dateFormat
|
||||||
|
? { dateFormat: map?.dateFormat }
|
||||||
|
: {};
|
||||||
|
|
||||||
|
set(acc, path, { from: map?.from, ...dateFormatObj });
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
};
|
};
|
||||||
@@ -76,10 +82,10 @@ const getInitialDefaultValues = (
|
|||||||
const _matched = sheetColumns.find(
|
const _matched = sheetColumns.find(
|
||||||
(column) => lowerCase(column) === _name,
|
(column) => lowerCase(column) === _name,
|
||||||
);
|
);
|
||||||
const _key = groupKey ? `${groupKey}.${key}` : key;
|
const path = groupKey ? `['${groupKey}.${key}']` : key;
|
||||||
const _value = _matched ? _matched : '';
|
const from = _matched ? _matched : '';
|
||||||
|
|
||||||
set(acc, _key, _value);
|
set(acc, path, { from });
|
||||||
});
|
});
|
||||||
return acc;
|
return acc;
|
||||||
}, {});
|
}, {});
|
||||||
@@ -102,7 +108,6 @@ 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(
|
||||||
|
|||||||
@@ -73,9 +73,8 @@ export function OneClickDemoPageContent() {
|
|||||||
)}
|
)}
|
||||||
{running && (
|
{running && (
|
||||||
<Text className={style.waitingText}>
|
<Text className={style.waitingText}>
|
||||||
We're preparing the temporary environment for trial. It
|
We're preparing temporary environment for trial, It typically
|
||||||
typically takes a few seconds. Do not close or refresh the
|
take few seconds. Do not close or refresh the page.
|
||||||
page.
|
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -2,17 +2,14 @@
|
|||||||
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 (
|
||||||
<EnsureOrganizationIsNotReady>
|
<div class="setup-page">
|
||||||
<div class="setup-page">
|
<SetupLeftSection />
|
||||||
<SetupLeftSection />
|
<SetupRightSection />
|
||||||
<SetupRightSection />
|
</div>
|
||||||
</div>
|
|
||||||
</EnsureOrganizationIsNotReady>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
@@ -1,35 +1,43 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { lazy } from 'react';
|
import LazyLoader from '@/components/LazyLoader';
|
||||||
|
|
||||||
const BASE_URL = '/auth';
|
const BASE_URL = '/auth';
|
||||||
|
|
||||||
export default [
|
export default [
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/login`,
|
path: `${BASE_URL}/login`,
|
||||||
component: lazy(() => import('@/containers/Authentication/Login')),
|
component: LazyLoader({
|
||||||
|
loader: () => import('@/containers/Authentication/Login'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/send_reset_password`,
|
path: `${BASE_URL}/send_reset_password`,
|
||||||
component: lazy(
|
component: LazyLoader({
|
||||||
() => import('@/containers/Authentication/SendResetPassword'),
|
loader: () => import('@/containers/Authentication/SendResetPassword'),
|
||||||
),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/reset_password/:token`,
|
path: `${BASE_URL}/reset_password/:token`,
|
||||||
component: lazy(() => import('@/containers/Authentication/ResetPassword')),
|
component: LazyLoader({
|
||||||
|
loader: () => import('@/containers/Authentication/ResetPassword'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/invite/:token/accept`,
|
path: `${BASE_URL}/invite/:token/accept`,
|
||||||
component: lazy(() => import('@/containers/Authentication/InviteAccept')),
|
component: LazyLoader({
|
||||||
|
loader: () => import('@/containers/Authentication/InviteAccept'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/register/email_confirmation`,
|
path: `${BASE_URL}/register/email_confirmation`,
|
||||||
component: lazy(
|
component: LazyLoader({
|
||||||
() => import('@/containers/Authentication/EmailConfirmation'),
|
loader: () => import('@/containers/Authentication/EmailConfirmation'),
|
||||||
),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/register`,
|
path: `${BASE_URL}/register`,
|
||||||
component: lazy(() => import('@/containers/Authentication/Register')),
|
component: LazyLoader({
|
||||||
|
loader: () => import('@/containers/Authentication/Register'),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,96 +1,88 @@
|
|||||||
// @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 const getPreferenceRoutes = () => [
|
export default [
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/general`,
|
path: `${BASE_URL}/general`,
|
||||||
component: lazy(() => import('@/containers/Preferences/General/General')),
|
component: General,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/users`,
|
path: `${BASE_URL}/users`,
|
||||||
component: lazy(() => import('../containers/Preferences/Users/Users')),
|
component: Users,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/invoices`,
|
path: `${BASE_URL}/invoices`,
|
||||||
component: lazy(
|
component: Invoices,
|
||||||
() => import('../containers/Preferences/Invoices/PreferencesInvoices'),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/credit-notes`,
|
path: `${BASE_URL}/credit-notes`,
|
||||||
component: lazy(() =>
|
component: PreferencesCreditNotes,
|
||||||
import(
|
|
||||||
'../containers/Preferences/CreditNotes/PreferencesCreditNotes'
|
|
||||||
).then((module) => ({ default: module.PreferencesCreditNotes })),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/estimates`,
|
path: `${BASE_URL}/estimates`,
|
||||||
component: lazy(() =>
|
component: PreferencesEstimates,
|
||||||
import('@/containers/Preferences/Estimates/PreferencesEstimates').then(
|
|
||||||
(module) => ({ default: module.PreferencesEstimates }),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/receipts`,
|
path: `${BASE_URL}/receipts`,
|
||||||
component: lazy(() =>
|
component: PreferencesReceipts,
|
||||||
import('@/containers/Preferences/Receipts/PreferencesReceipts').then(
|
|
||||||
(module) => ({ default: module.PreferencesReceipts }),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/roles`,
|
path: `${BASE_URL}/roles`,
|
||||||
component: lazy(
|
component: Roles,
|
||||||
() =>
|
|
||||||
import('../containers/Preferences/Users/Roles/RolesForm/RolesFormPage'),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/roles/:id`,
|
path: `${BASE_URL}/roles/:id`,
|
||||||
component: lazy(
|
component: Roles,
|
||||||
() =>
|
|
||||||
import('../containers/Preferences/Users/Roles/RolesForm/RolesFormPage'),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/currencies`,
|
path: `${BASE_URL}/currencies`,
|
||||||
component: lazy(
|
component: Currencies,
|
||||||
() => import('@/containers/Preferences/Currencies/Currencies'),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/warehouses`,
|
path: `${BASE_URL}/warehouses`,
|
||||||
component: lazy(() => import('../containers/Preferences/Warehouses')),
|
component: Warehouses,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/branches`,
|
path: `${BASE_URL}/branches`,
|
||||||
component: lazy(() => import('../containers/Preferences/Branches')),
|
component: Branches,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/accountant`,
|
path: `${BASE_URL}/accountant`,
|
||||||
component: lazy(
|
component: Accountant,
|
||||||
() => import('@/containers/Preferences/Accountant/Accountant'),
|
|
||||||
),
|
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/items`,
|
path: `${BASE_URL}/items`,
|
||||||
component: lazy(() => import('@/containers/Preferences/Item')),
|
component: Item,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@@ -100,12 +92,12 @@ export const getPreferenceRoutes = () => [
|
|||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/billing`,
|
path: `${BASE_URL}/billing`,
|
||||||
component: lazy(() => import('@/containers/Subscriptions/BillingPage')),
|
component: BillingPage,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: `${BASE_URL}/`,
|
path: `${BASE_URL}/`,
|
||||||
component: lazy(() => import('../containers/Preferences/DefaultRoute')),
|
component: DefaultRoute,
|
||||||
exact: true,
|
exact: true,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user