Compare commits

..

1 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
f395cce710 fix(script): setup script 2026-01-29 20:49:54 +02:00
39 changed files with 116 additions and 372 deletions

View File

@@ -35,4 +35,4 @@ WORKDIR /app/packages/server
RUN git clone https://github.com/vishnubob/wait-for-it.git RUN git clone https://github.com/vishnubob/wait-for-it.git
# Once we listen the mysql port run the migration task. # Once we listen the mysql port run the migration task.
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "node dist/cli.js system:migrate:latest && node dist/cli.js tenants:migrate:latest" CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "pnpm run system:migrate:latest && pnpm run tenants:migrate:latest"

View File

@@ -75,9 +75,6 @@ COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/i18n ./packag
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/public ./packages/server/public COPY --from=builder --chown=nodejs:nodejs /app/packages/server/public ./packages/server/public
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/static ./packages/server/static COPY --from=builder --chown=nodejs:nodejs /app/packages/server/static ./packages/server/static
# Copy database migration files (needed for running migrations)
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/database ./packages/server/src/database
# Copy built shared packages (dist folders and package.json for module resolution) # Copy built shared packages (dist folders and package.json for module resolution)
COPY --from=builder --chown=nodejs:nodejs /app/shared/bigcapital-utils/dist ./shared/bigcapital-utils/dist COPY --from=builder --chown=nodejs:nodejs /app/shared/bigcapital-utils/dist ./shared/bigcapital-utils/dist
COPY --from=builder --chown=nodejs:nodejs /app/shared/pdf-templates/dist ./shared/pdf-templates/dist COPY --from=builder --chown=nodejs:nodejs /app/shared/pdf-templates/dist ./shared/pdf-templates/dist

View File

@@ -2,23 +2,10 @@
"$schema": "https://json.schemastore.org/nest-cli", "$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics", "collection": "@nestjs/schematics",
"sourceRoot": "src", "sourceRoot": "src",
"entryFile": "main",
"compilerOptions": { "compilerOptions": {
"deleteOutDir": true, "deleteOutDir": true,
"assets": [ "assets": [
{ "include": "i18n/**/*", "watchAssets": true }, { "include": "i18n/**/*", "watchAssets": true }
{ "include": "database/**/*", "exclude": "**/*.ts", "watchAssets": true }
] ]
},
"projects": {
"cli": {
"type": "application",
"root": "src",
"entryFile": "cli",
"sourceRoot": "src",
"compilerOptions": {
"tsConfigPath": "tsconfig.json"
}
}
} }
} }

View File

@@ -1,6 +1,10 @@
import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
import { SendResetPasswordMailQueue } from '../Auth.constants'; import {
SendResetPasswordMailJob,
SendResetPasswordMailQueue,
} from '../Auth.constants';
import { Process } from '@nestjs/bull';
import { Job } from 'bullmq'; import { Job } from 'bullmq';
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice'; import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
@@ -19,6 +23,7 @@ export class SendResetPasswordMailProcessor extends WorkerHost {
super(); super();
} }
@Process(SendResetPasswordMailJob)
async process(job: Job<SendResetPasswordMailJobPayload>) { async process(job: Job<SendResetPasswordMailJobPayload>) {
try { try {
await this.authMailMesssages.sendResetPasswordMail( await this.authMailMesssages.sendResetPasswordMail(

View File

@@ -1,7 +1,11 @@
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
import { Job } from 'bullmq'; import { Job } from 'bullmq';
import { Process } from '@nestjs/bull';
import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Processor, WorkerHost } from '@nestjs/bullmq';
import { SendSignupVerificationMailQueue } from '../Auth.constants'; import {
SendSignupVerificationMailJob,
SendSignupVerificationMailQueue,
} from '../Auth.constants';
import { MailTransporter } from '@/modules/Mail/MailTransporter.service'; import { MailTransporter } from '@/modules/Mail/MailTransporter.service';
import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice'; import { AuthenticationMailMesssages } from '../AuthMailMessages.esrvice';
@@ -17,6 +21,7 @@ export class SendSignupVerificationMailProcessor extends WorkerHost {
super(); super();
} }
@Process(SendSignupVerificationMailJob)
async process(job: Job<SendSignupVerificationMailJobPayload>) { async process(job: Job<SendSignupVerificationMailJobPayload>) {
try { try {
await this.authMailMesssages.sendSignupVerificationMail( await this.authMailMesssages.sendSignupVerificationMail(

View File

@@ -1,9 +1,11 @@
import { Process } from '@nestjs/bull';
import { UseCls } from 'nestjs-cls'; import { UseCls } from 'nestjs-cls';
import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
import { Job } from 'bullmq'; import { Job } from 'bullmq';
import { import {
PlaidFetchTransitonsEventPayload, PlaidFetchTransitonsEventPayload,
UpdateBankingPlaidTransitionsJob,
UpdateBankingPlaidTransitionsQueueJob, UpdateBankingPlaidTransitionsQueueJob,
} from '../types/BankingPlaid.types'; } from '../types/BankingPlaid.types';
import { PlaidUpdateTransactions } from '../command/PlaidUpdateTransactions'; import { PlaidUpdateTransactions } from '../command/PlaidUpdateTransactions';
@@ -26,6 +28,7 @@ export class PlaidFetchTransactionsProcessor extends WorkerHost {
/** /**
* Triggers the function. * Triggers the function.
*/ */
@Process(UpdateBankingPlaidTransitionsJob)
@UseCls() @UseCls()
async process(job: Job<PlaidFetchTransitonsEventPayload>) { async process(job: Job<PlaidFetchTransitonsEventPayload>) {
const { plaidItemId } = job.data; const { plaidItemId } = job.data;

View File

@@ -7,6 +7,7 @@ import {
RecognizeUncategorizedTransactionsJobPayload, RecognizeUncategorizedTransactionsJobPayload,
RecognizeUncategorizedTransactionsQueue, RecognizeUncategorizedTransactionsQueue,
} from '../_types'; } from '../_types';
import { Process } from '@nestjs/bull';
@Processor({ @Processor({
name: RecognizeUncategorizedTransactionsQueue, name: RecognizeUncategorizedTransactionsQueue,
@@ -27,6 +28,7 @@ export class RegonizeTransactionsPrcessor extends WorkerHost {
/** /**
* Triggers sending invoice mail. * Triggers sending invoice mail.
*/ */
@Process(RecognizeUncategorizedTransactionsQueue)
@UseCls() @UseCls()
async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) { async process(job: Job<RecognizeUncategorizedTransactionsJobPayload>) {
const { ruleId, transactionsCriteria } = job.data; const { ruleId, transactionsCriteria } = job.data;

View File

@@ -22,7 +22,6 @@ export abstract class BaseCommand extends CommandRunner {
}, },
migrations: { migrations: {
directory: this.configService.get('systemDatabase.migrationDir'), directory: this.configService.get('systemDatabase.migrationDir'),
loadExtensions: ['.js'],
}, },
seeds: { seeds: {
directory: this.configService.get('systemDatabase.seedsDir'), directory: this.configService.get('systemDatabase.seedsDir'),
@@ -44,7 +43,6 @@ export abstract class BaseCommand extends CommandRunner {
}, },
migrations: { migrations: {
directory: this.configService.get('tenantDatabase.migrationsDir') || './src/database/migrations', directory: this.configService.get('tenantDatabase.migrationsDir') || './src/database/migrations',
loadExtensions: ['.js'],
}, },
seeds: { seeds: {
directory: this.configService.get('tenantDatabase.seedsDir') || './src/database/seeds/core', directory: this.configService.get('tenantDatabase.seedsDir') || './src/database/seeds/core',

View File

@@ -24,14 +24,14 @@ export class BalanceSheetQueryDto extends FinancialSheetBranchesQueryDto {
displayColumnsType: 'total' | 'date_periods' = 'total'; displayColumnsType: 'total' | 'date_periods' = 'total';
@ApiProperty({ @ApiProperty({
enum: ['day', 'month', 'year', 'quarter'], enum: ['day', 'month', 'year'],
default: 'year', default: 'year',
description: 'Time period for column display', description: 'Time period for column display',
}) })
@IsString() @IsString()
@IsOptional() @IsOptional()
@IsEnum(['day', 'month', 'year', 'quarter']) @IsEnum(['day', 'month', 'year'])
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year'; displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@ApiProperty({ @ApiProperty({
description: 'Start date for the balance sheet period', description: 'Start date for the balance sheet period',

View File

@@ -34,13 +34,13 @@ export class CashFlowStatementQueryDto extends FinancialSheetBranchesQueryDto {
@ApiProperty({ @ApiProperty({
description: 'Display columns by time period', description: 'Display columns by time period',
required: false, required: false,
enum: ['day', 'month', 'year', 'quarter'], enum: ['day', 'month', 'year'],
default: 'year', default: 'year',
}) })
@IsString() @IsString()
@IsOptional() @IsOptional()
@IsEnum(['day', 'month', 'year', 'quarter']) @IsEnum(['day', 'month', 'year'])
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year'; displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@ApiProperty({ @ApiProperty({
description: 'Type of column display', description: 'Type of column display',

View File

@@ -64,10 +64,10 @@ export class ProfitLossSheetQueryDto extends FinancialSheetBranchesQueryDto {
displayColumnsType: 'total' | 'date_periods'; displayColumnsType: 'total' | 'date_periods';
@IsString() @IsString()
@IsEnum(['day', 'month', 'year', 'quarter']) @IsEnum(['day', 'month', 'year'])
@IsOptional() @IsOptional()
@ApiProperty({ description: 'How to display columns' }) @ApiProperty({ description: 'How to display columns' })
displayColumnsBy: 'day' | 'month' | 'year' | 'quarter' = 'year'; displayColumnsBy: 'day' | 'month' | 'year' = 'year';
@Transform(({ value }) => parseBoolean(value, false)) @Transform(({ value }) => parseBoolean(value, false))
@IsBoolean() @IsBoolean()

View File

@@ -7,7 +7,11 @@ import * as moment from 'moment';
import { TenantJobPayload } from '@/interfaces/Tenant'; import { TenantJobPayload } from '@/interfaces/Tenant';
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service'; import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
import { events } from '@/common/events/events'; import { events } from '@/common/events/events';
import { ComputeItemCostQueue } from '../types/InventoryCost.types'; import {
ComputeItemCostQueue,
ComputeItemCostQueueJob,
} from '../types/InventoryCost.types';
import { Process } from '@nestjs/bull';
interface ComputeItemCostJobPayload extends TenantJobPayload { interface ComputeItemCostJobPayload extends TenantJobPayload {
itemId: number; itemId: number;
@@ -35,6 +39,7 @@ export class ComputeItemCostProcessor extends WorkerHost {
* Process the compute item cost job. * Process the compute item cost job.
* @param {Job<ComputeItemCostJobPayload>} job - The job to process * @param {Job<ComputeItemCostJobPayload>} job - The job to process
*/ */
@Process(ComputeItemCostQueueJob)
@UseCls() @UseCls()
async process(job: Job<ComputeItemCostJobPayload>) { async process(job: Job<ComputeItemCostJobPayload>) {
const { itemId, startingDate, organizationId, userId } = job.data; const { itemId, startingDate, organizationId, userId } = job.data;

View File

@@ -1,4 +1,8 @@
import { WriteInventoryTransactionsGLEntriesQueue } from '../types/InventoryCost.types'; import { Process } from '@nestjs/bull';
import {
WriteInventoryTransactionsGLEntriesQueue,
WriteInventoryTransactionsGLEntriesQueueJob,
} from '../types/InventoryCost.types';
import { Processor, WorkerHost } from '@nestjs/bullmq'; import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
@@ -11,5 +15,6 @@ export class WriteInventoryTransactionsGLEntriesProcessor extends WorkerHost {
super(); super();
} }
@Process(WriteInventoryTransactionsGLEntriesQueueJob)
async process() {} async process() {}
} }

View File

@@ -34,7 +34,6 @@ import {
BulkDeleteItemsDto, BulkDeleteItemsDto,
ValidateBulkDeleteItemsResponseDto, ValidateBulkDeleteItemsResponseDto,
} from './dtos/BulkDeleteItems.dto'; } from './dtos/BulkDeleteItems.dto';
import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
@Controller('/items') @Controller('/items')
@ApiTags('Items') @ApiTags('Items')
@@ -46,7 +45,6 @@ import { ItemApiErrorResponseDto } from './dtos/ItemErrorResponse.dto';
@ApiExtraModels(ItemEstimatesResponseDto) @ApiExtraModels(ItemEstimatesResponseDto)
@ApiExtraModels(ItemReceiptsResponseDto) @ApiExtraModels(ItemReceiptsResponseDto)
@ApiExtraModels(ValidateBulkDeleteItemsResponseDto) @ApiExtraModels(ValidateBulkDeleteItemsResponseDto)
@ApiExtraModels(ItemApiErrorResponseDto)
@ApiCommonHeaders() @ApiCommonHeaders()
export class ItemsController extends TenantController { export class ItemsController extends TenantController {
constructor(private readonly itemsApplication: ItemsApplicationService) { constructor(private readonly itemsApplication: ItemsApplicationService) {
@@ -149,13 +147,6 @@ export class ItemsController extends TenantController {
status: 200, status: 200,
description: 'The item has been successfully updated.', description: 'The item has been successfully updated.',
}) })
@ApiResponse({
status: 400,
description: 'Validation error. Possible error types: ITEM_NAME_EXISTS, INVENTORY_ACCOUNT_CANNOT_MODIFIED, TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
@ApiResponse({ status: 404, description: 'The item not found.' }) @ApiResponse({ status: 404, description: 'The item not found.' })
@ApiParam({ @ApiParam({
name: 'id', name: 'id',
@@ -213,13 +204,6 @@ export class ItemsController extends TenantController {
status: 200, status: 200,
description: 'The item has been successfully created.', description: 'The item has been successfully created.',
}) })
@ApiResponse({
status: 400,
description: 'Validation error. Possible error types: ITEM_NAME_EXISTS, ITEM_CATEOGRY_NOT_FOUND, COST_ACCOUNT_NOT_COGS, SELL_ACCOUNT_NOT_INCOME, INVENTORY_ACCOUNT_NOT_INVENTORY, INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM, COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
// @UsePipes(new ZodValidationPipe(createItemSchema)) // @UsePipes(new ZodValidationPipe(createItemSchema))
async createItem( async createItem(
@Body() createItemDto: CreateItemDto, @Body() createItemDto: CreateItemDto,
@@ -235,13 +219,6 @@ export class ItemsController extends TenantController {
status: 200, status: 200,
description: 'The item has been successfully deleted.', description: 'The item has been successfully deleted.',
}) })
@ApiResponse({
status: 400,
description: 'Cannot delete item. Possible error types: ITEM_HAS_ASSOCIATED_TRANSACTINS, ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT, etc.',
schema: {
$ref: getSchemaPath(ItemApiErrorResponseDto),
},
})
@ApiResponse({ status: 404, description: 'The item not found.' }) @ApiResponse({ status: 404, description: 'The item not found.' })
@ApiParam({ @ApiParam({
name: 'id', name: 'id',

View File

@@ -1,112 +0,0 @@
import { ApiProperty } from '@nestjs/swagger';
/**
* Item API Error Types
* These error types are returned when item operations fail validation
*/
export enum ItemErrorType {
/** Item name already exists in the system */
ItemNameExists = 'ITEM_NAME_EXISTS',
/** Item category was not found */
ItemCategoryNotFound = 'ITEM_CATEOGRY_NOT_FOUND',
/** Cost account is not a Cost of Goods Sold account */
CostAccountNotCogs = 'COST_ACCOUNT_NOT_COGS',
/** Cost account was not found */
CostAccountNotFound = 'COST_ACCOUNT_NOT_FOUMD',
/** Sell account was not found */
SellAccountNotFound = 'SELL_ACCOUNT_NOT_FOUND',
/** Sell account is not an income account */
SellAccountNotIncome = 'SELL_ACCOUNT_NOT_INCOME',
/** Inventory account was not found */
InventoryAccountNotFound = 'INVENTORY_ACCOUNT_NOT_FOUND',
/** Account is not an inventory type account */
InventoryAccountNotInventory = 'INVENTORY_ACCOUNT_NOT_INVENTORY',
/** Multiple items have associated transactions */
ItemsHaveAssociatedTransactions = 'ITEMS_HAVE_ASSOCIATED_TRANSACTIONS',
/** Item has associated transactions (singular) */
ItemHasAssociatedTransactions = 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
/** Item has associated inventory adjustments */
ItemHasAssociatedInventoryAdjustment = 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
/** Cannot change item type to inventory */
ItemCannotChangeInventoryType = 'ITEM_CANNOT_CHANGE_INVENTORY_TYPE',
/** Cannot change type when item has transactions */
TypeCannotChangeWithItemHasTransactions = 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
/** Inventory account cannot be modified */
InventoryAccountCannotModified = 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
/** Purchase tax rate was not found */
PurchaseTaxRateNotFound = 'PURCHASE_TAX_RATE_NOT_FOUND',
/** Sell tax rate was not found */
SellTaxRateNotFound = 'SELL_TAX_RATE_NOT_FOUND',
/** Income account is required for sellable items */
IncomeAccountRequiredWithSellableItem = 'INCOME_ACCOUNT_REQUIRED_WITH_SELLABLE_ITEM',
/** Cost account is required for purchasable items */
CostAccountRequiredWithPurchasableItem = 'COST_ACCOUNT_REQUIRED_WITH_PURCHASABLE_ITEM',
/** Item not found */
NotFound = 'NOT_FOUND',
/** Items not found */
ItemsNotFound = 'ITEMS_NOT_FOUND',
}
/**
* Item API Error Response
* Returned when an item operation fails
*/
export class ItemErrorResponseDto {
@ApiProperty({
description: 'HTTP status code',
example: 400,
})
statusCode: number;
@ApiProperty({
description: 'Error type identifier',
enum: ItemErrorType,
example: ItemErrorType.ItemNameExists,
})
type: ItemErrorType;
@ApiProperty({
description: 'Human-readable error message',
example: 'The item name is already exist.',
required: false,
nullable: true,
})
message: string | null;
@ApiProperty({
description: 'Additional error payload data',
required: false,
nullable: true,
})
payload: any;
}
/**
* Item API Error Response Wrapper
*/
export class ItemApiErrorResponseDto {
@ApiProperty({
description: 'Array of error details',
type: [ItemErrorResponseDto],
})
errors: ItemErrorResponseDto[];
}

View File

@@ -2,8 +2,10 @@ import { Processor, WorkerHost } from '@nestjs/bullmq';
import { Scope } from '@nestjs/common'; import { Scope } from '@nestjs/common';
import { Job } from 'bullmq'; import { Job } from 'bullmq';
import { ClsService, UseCls } from 'nestjs-cls'; import { ClsService, UseCls } from 'nestjs-cls';
import { Process } from '@nestjs/bull';
import { import {
OrganizationBuildQueue, OrganizationBuildQueue,
OrganizationBuildQueueJob,
OrganizationBuildQueueJobPayload, OrganizationBuildQueueJobPayload,
} from '../Organization.types'; } from '../Organization.types';
import { BuildOrganizationService } from '../commands/BuildOrganization.service'; import { BuildOrganizationService } from '../commands/BuildOrganization.service';
@@ -20,6 +22,7 @@ export class OrganizationBuildProcessor extends WorkerHost {
super(); super();
} }
@Process(OrganizationBuildQueueJob)
@UseCls() @UseCls()
async process(job: Job<OrganizationBuildQueueJobPayload>) { async process(job: Job<OrganizationBuildQueueJobPayload>) {
console.log('Processing organization build job:', job.id); console.log('Processing organization build job:', job.id);

View File

@@ -22,7 +22,6 @@ const providers = [
}, },
migrations: { migrations: {
directory: configService.get('systemDatabase.migrationDir'), directory: configService.get('systemDatabase.migrationDir'),
loadExtensions: ['.js'],
}, },
seeds: { seeds: {
directory: configService.get('systemDatabase.seedsDir'), directory: configService.get('systemDatabase.seedsDir'),

View File

@@ -33,7 +33,6 @@ export const TenancyDatabaseProxyProvider = ClsModule.forFeatureAsync({
}, },
migrations: { migrations: {
directory: configService.get('tenantDatabase.migrationsDir'), directory: configService.get('tenantDatabase.migrationsDir'),
loadExtensions: ['.js'],
}, },
seeds: { seeds: {
directory: configService.get('tenantDatabase.seedsDir'), directory: configService.get('tenantDatabase.seedsDir'),

View File

@@ -4,12 +4,7 @@ import styled from 'styled-components';
import { DataTable } from '../Datatable'; import { DataTable } from '../Datatable';
export const ReportDataTable = styled(DataTable)` export const ReportDataTable = styled(DataTable)`
--x-table-no-results-border-color: #ddd;
.bp4-dark & {
--x-table-no-results-border-color: var(--color-dark-gray5);
}
.table .tbody .tr.no-results:last-of-type .td { .table .tbody .tr.no-results:last-of-type .td {
border-bottom: 1px solid var(--x-table-no-results-border-color); border-bottom: 1px solid #ddd;
} }
`; `;

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core'; import { Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
import { useHistory, useLocation } from 'react-router-dom'; import { useHistory, useLocation } from 'react-router-dom';
import { FormattedMessage as T } from '@/components'; import { FormattedMessage as T } from '@/components';
import { PreferencesMenu } from '@/constants/preferencesMenu'; import preferencesMenu from '@/constants/preferencesMenu';
import PreferencesSidebarContainer from './PreferencesSidebarContainer'; import PreferencesSidebarContainer from './PreferencesSidebarContainer';
import '@/style/pages/Preferences/Sidebar.scss'; import '@/style/pages/Preferences/Sidebar.scss';
@@ -15,7 +15,7 @@ export default function PreferencesSidebar() {
const history = useHistory(); const history = useHistory();
const location = useLocation(); const location = useLocation();
const items = PreferencesMenu.map((item) => const items = preferencesMenu.map((item) =>
item.divider ? ( item.divider ? (
<MenuDivider title={item.title} /> <MenuDivider title={item.title} />
) : ( ) : (

View File

@@ -1,7 +1,7 @@
// @ts-nocheck // @ts-nocheck
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
export const AllocateLandedCostType = [ export default [
{ name: intl.get('bills'), value: 'Bill' }, { name: intl.get('bills'), value: 'Bill' },
{ name: intl.get('expenses'), value: 'Expense' }, { name: intl.get('expenses'), value: 'Expense' },
]; ];

View File

@@ -1,5 +1,5 @@
// @ts-nocheck // @ts-nocheck
export const App = { export default {
"app_name": "BigCapital", "app_name": "BigCapital",
"app_version": "0.0.1 (build 12344)", "app_version": "0.0.1 (build 12344)",
} }

View File

@@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
export const ContactsOptions = [ export default [
{ name: intl.get('customer'), path: 'customers' }, { name: intl.get('customer'), path: 'customers' },
{ name: intl.get('vendor'), path: 'vendors' }, { name: intl.get('vendor'), path: 'vendors' },
]; ];

View File

@@ -16,7 +16,7 @@ import {
VendorAction, VendorAction,
} from './abilityOption'; } from './abilityOption';
export const KeyboardShortcutsOptions = [ export default [
{ {
shortcut_key: 'Shift + I', shortcut_key: 'Shift + I',
description: intl.get('jump_to_the_invoices'), description: intl.get('jump_to_the_invoices'),

View File

@@ -2,7 +2,7 @@
import React from 'react'; import React from 'react';
import { FormattedMessage as T } from '@/components'; import { FormattedMessage as T } from '@/components';
export const PreferencesMenu = [ export default [
{ {
text: <T id={'general'} />, text: <T id={'general'} />,
disabled: false, disabled: false,
@@ -13,10 +13,10 @@ export const PreferencesMenu = [
disabled: false, disabled: false,
href: '/preferences/branding', href: '/preferences/branding',
}, },
// { {
// text: 'Billing', text: 'Billing',
// href: '/preferences/billing', href: '/preferences/billing',
// }, },
{ {
text: <T id={'users'} />, text: <T id={'users'} />,
href: '/preferences/users', href: '/preferences/users',
@@ -63,11 +63,11 @@ export const PreferencesMenu = [
disabled: false, disabled: false,
href: '/preferences/items', href: '/preferences/items',
}, },
// { {
// text: 'Integrations', text: 'Integrations',
// disabled: false, disabled: false,
// href: '/preferences/integrations' href: '/preferences/integrations'
// }, },
{ {
text: 'API Keys', text: 'API Keys',
disabled: false, disabled: false,

View File

@@ -8,11 +8,6 @@ import { If, AppToaster } from '@/components';
import { NormalCell, BalanceCell, BankBalanceCell } from './components'; import { NormalCell, BalanceCell, BankBalanceCell } from './components';
import { transformTableStateToQuery, isBlank } from '@/utils'; import { transformTableStateToQuery, isBlank } from '@/utils';
export const DeleteAccountTypeError = {
AccountPredefined: 'account_predefined',
AccountHasAssociatedTransactions: 'account_has_associated_transactions',
};
/** /**
* Account name accessor. * Account name accessor.
*/ */
@@ -31,13 +26,13 @@ export const accountNameAccessor = (account) => {
* Handle delete errors in bulk and singular. * Handle delete errors in bulk and singular.
*/ */
export const handleDeleteErrors = (errors) => { export const handleDeleteErrors = (errors) => {
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountPredefined)) { if (errors.find((e) => e.type === 'ACCOUNT.PREDEFINED')) {
AppToaster.show({ AppToaster.show({
message: intl.get('cannot_delete_predefined_accounts'), message: intl.get('cannot_delete_predefined_accounts'),
intent: Intent.DANGER, intent: Intent.DANGER,
}); });
} }
if (errors.find((e) => e.type === DeleteAccountTypeError.AccountHasAssociatedTransactions)) { if (errors.find((e) => e.type === 'ACCOUNT.HAS.ASSOCIATED.TRANSACTIONS')) {
AppToaster.show({ AppToaster.show({
message: intl.get('cannot_delete_account_has_associated_transactions'), message: intl.get('cannot_delete_account_has_associated_transactions'),
intent: Intent.DANGER, intent: Intent.DANGER,

View File

@@ -16,7 +16,7 @@ import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGrou
import { handleStringChange } from '@/utils'; import { handleStringChange } from '@/utils';
import { FieldRequiredHint } from '@/components'; import { FieldRequiredHint } from '@/components';
import { CLASSES } from '@/constants/classes'; import { CLASSES } from '@/constants/classes';
import { AllocateLandedCostType } from '@/constants/allocateLandedCostType'; import allocateLandedCostType from '@/constants/allocateLandedCostType';
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody'; import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
import { import {
@@ -81,7 +81,7 @@ export default function AllocateLandedCostFormFields() {
> >
<FSelect <FSelect
name={'transaction_type'} name={'transaction_type'}
items={AllocateLandedCostType} items={allocateLandedCostType}
onItemChange={handleTransactionTypeChange} onItemChange={handleTransactionTypeChange}
filterable={false} filterable={false}
valueAccessor={'value'} valueAccessor={'value'}

View File

@@ -9,7 +9,7 @@ import { FormattedMessage as T } from '@/components';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { useContactDuplicateFromContext } from './ContactDuplicateProvider'; import { useContactDuplicateFromContext } from './ContactDuplicateProvider';
import { ContactsOptions } from '@/constants/contactsOptions'; import Contacts from '@/constants/contactsOptions';
import { withDialogActions } from '@/containers/Dialog/withDialogActions'; import { withDialogActions } from '@/containers/Dialog/withDialogActions';
import { compose } from '@/utils'; import { compose } from '@/utils';
@@ -66,7 +66,7 @@ function ContactDuplicateForm({
> >
<FSelect <FSelect
name={'contact_type'} name={'contact_type'}
items={ContactsOptions} items={Contacts}
placeholder={<T id={'select_contact'} />} placeholder={<T id={'select_contact'} />}
textAccessor={'name'} textAccessor={'name'}
valueAccessor={'path'} valueAccessor={'path'}

View File

@@ -1,20 +1,8 @@
// @ts-nocheck // @ts-nocheck
import React from 'react';
import { getColumnWidth } from '@/utils'; import { getColumnWidth } from '@/utils';
import * as R from 'ramda'; import * as R from 'ramda';
import { useGeneralLedgerContext } from './GeneralLedgerProvider'; import { useGeneralLedgerContext } from './GeneralLedgerProvider';
import { Align, CLASSES } from '@/constants'; import { Align } from '@/constants';
/**
* Description cell wraps value in a div with muted text class.
*/
function DescriptionCell({ cell: { value } }) {
return React.createElement(
'div',
{ className: `cell ${CLASSES.TEXT_MUTED}` },
value,
);
}
const getTableCellValueAccessor = (index) => `cells[${index}].value`; const getTableCellValueAccessor = (index) => `cells[${index}].value`;
@@ -87,16 +75,6 @@ const transactionIdColumnAccessor = (column) => {
}; };
}; };
/**
* Description column accessor (muted text in wrapped cell).
*/
const descriptionColumnAccessor = (column) => {
return {
...column,
Cell: DescriptionCell,
};
};
const dynamiColumnMapper = R.curry((data, column) => { const dynamiColumnMapper = R.curry((data, column) => {
const _numericColumnAccessor = numericColumnAccessor(data); const _numericColumnAccessor = numericColumnAccessor(data);
@@ -110,7 +88,6 @@ const dynamiColumnMapper = R.curry((data, column) => {
R.pathEq(['key'], 'reference_number'), R.pathEq(['key'], 'reference_number'),
transactionIdColumnAccessor, transactionIdColumnAccessor,
), ),
R.when(R.pathEq(['key'], 'description'), descriptionColumnAccessor),
R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor), R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor), R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'amount'), _numericColumnAccessor), R.when(R.pathEq(['key'], 'amount'), _numericColumnAccessor),

View File

@@ -1,21 +1,9 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import { Align } from '@/constants';
import { Align, CLASSES } from '@/constants';
import { getColumnWidth } from '@/utils'; import { getColumnWidth } from '@/utils';
import * as R from 'ramda'; import * as R from 'ramda';
import { useJournalSheetContext } from './JournalProvider'; import { useJournalSheetContext } from './JournalProvider';
/**
* Description cell wraps value in a div with muted text class.
*/
function DescriptionCell({ cell: { value } }) {
return React.createElement(
'span',
{ className: `cell ${CLASSES.TEXT_MUTED}` },
value,
);
}
const getTableCellValueAccessor = (index) => `cells[${index}].value`; const getTableCellValueAccessor = (index) => `cells[${index}].value`;
const getReportColWidth = (data, accessor, headerText) => { const getReportColWidth = (data, accessor, headerText) => {
@@ -98,16 +86,6 @@ const accountCodeColumnAccessor = (column) => {
}; };
}; };
/**
* Description column accessor (muted text in wrapped cell).
*/
const descriptionColumnAccessor = (column) => {
return {
...column,
Cell: DescriptionCell,
};
};
/** /**
* Dynamic column mapper. * Dynamic column mapper.
* @param {} data - * @param {} data -
@@ -127,7 +105,6 @@ const dynamicColumnMapper = R.curry((data, column) => {
R.pathEq(['key'], 'transaction_number'), R.pathEq(['key'], 'transaction_number'),
transactionNumberColumnAccessor, transactionNumberColumnAccessor,
), ),
R.when(R.pathEq(['key'], 'description'), descriptionColumnAccessor),
R.when(R.pathEq(['key'], 'account_code'), accountCodeColumnAccessor), R.when(R.pathEq(['key'], 'account_code'), accountCodeColumnAccessor),
R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor), R.when(R.pathEq(['key'], 'credit'), _numericColumnAccessor),
R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor), R.when(R.pathEq(['key'], 'debit'), _numericColumnAccessor),

View File

@@ -14,18 +14,6 @@ import { useSettingsSelector } from '@/hooks/state';
import { transformItemFormData } from './ItemForm.schema'; import { transformItemFormData } from './ItemForm.schema';
import { useWatch } from '@/hooks/utils'; import { useWatch } from '@/hooks/utils';
/**
* Error types for item operations.
*/
export const ItemErrorType = {
ItemNameExists: 'ITEM_NAME_EXISTS',
InventoryAccountCannotModified: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED',
TypeCannotChangeWithItemHasTransactions: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
ItemHasAssociatedTransactions: 'ITEM_HAS_ASSOCIATED_TRANSACTINS',
ItemHasAssociatedInventoryAdjustment: 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
ItemHasAssociatedTransactionsPlural: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS',
} as const;
const defaultInitialValues = { const defaultInitialValues = {
active: 1, active: 1,
name: '', name: '',
@@ -86,7 +74,7 @@ export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
// handle delete errors. // handle delete errors.
export const handleDeleteErrors = (errors) => { export const handleDeleteErrors = (errors) => {
if ( if (
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactions) errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTINS')
) { ) {
AppToaster.show({ AppToaster.show({
message: intl.get('the_item_has_associated_transactions'), message: intl.get('the_item_has_associated_transactions'),
@@ -96,7 +84,7 @@ export const handleDeleteErrors = (errors) => {
if ( if (
errors.find( errors.find(
(error) => error.type === ItemErrorType.ItemHasAssociatedInventoryAdjustment, (error) => error.type === 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT',
) )
) { ) {
AppToaster.show({ AppToaster.show({
@@ -108,7 +96,7 @@ export const handleDeleteErrors = (errors) => {
} }
if ( if (
errors.find( errors.find(
(error) => error.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions, (error) => error.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
) )
) { ) {
AppToaster.show({ AppToaster.show({
@@ -119,7 +107,7 @@ export const handleDeleteErrors = (errors) => {
}); });
} }
if ( if (
errors.find((error) => error.type === ItemErrorType.ItemHasAssociatedTransactionsPlural) errors.find((error) => error.type === 'ITEM_HAS_ASSOCIATED_TRANSACTIONS')
) { ) {
AppToaster.show({ AppToaster.show({
message: intl.get('item.error.you_could_not_delete_item_has_associated'), message: intl.get('item.error.you_could_not_delete_item_has_associated'),
@@ -226,10 +214,10 @@ export const transformSubmitRequestErrors = (error) => {
} = error; } = error;
const fields = {}; const fields = {};
if (errors.find((e) => e.type === ItemErrorType.ItemNameExists)) { if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
fields.name = intl.get('the_name_used_before'); fields.name = intl.get('the_name_used_before');
} }
if (errors.find((e) => e.type === ItemErrorType.InventoryAccountCannotModified)) { if (errors.find((e) => e.type === 'INVENTORY_ACCOUNT_CANNOT_MODIFIED')) {
AppToaster.show({ AppToaster.show({
message: intl.get('cannot_change_item_inventory_account'), message: intl.get('cannot_change_item_inventory_account'),
intent: Intent.DANGER, intent: Intent.DANGER,
@@ -237,7 +225,7 @@ export const transformSubmitRequestErrors = (error) => {
} }
if ( if (
errors.find( errors.find(
(e) => e.type === ItemErrorType.TypeCannotChangeWithItemHasTransactions, (e) => e.type === 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS',
) )
) { ) {
AppToaster.show({ AppToaster.show({

View File

@@ -87,7 +87,7 @@ export function WarehousesGridItemBox({
<WarehouseBoxRoot> <WarehouseBoxRoot>
<WarehouseHeader> <WarehouseHeader>
<WarehouseTitle> <WarehouseTitle>
{title} {primary ? <Icon icon={'star-18dp'} iconSize={16} /> : null} {title} {primary && <Icon icon={'star-18dp'} iconSize={16} />}
</WarehouseTitle> </WarehouseTitle>
<WarehouseCode>{code}</WarehouseCode> <WarehouseCode>{code}</WarehouseCode>
<WarehouseIcon> <WarehouseIcon>
@@ -118,21 +118,12 @@ export const WarehousesList = styled.div`
`; `;
export const WarehouseBoxRoot = styled.div` export const WarehouseBoxRoot = styled.div`
--x-box-border-color: #c8cad0;
--x-box-background-color: #fff;
--x-box-hover-border-color: #0153cc;
.bp4-dark & {
--x-box-border-color: rgba(255, 255, 255, 0.2);
--x-box-background-color: var(--color-dark-gray3);
--x-box-hover-border-color: #0153cc;
}
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 0;
border-radius: 5px; border-radius: 5px;
border: 1px solid var(--x-box-border-color); border: 1px solid #c8cad0;
background: var(--x-box-background-color); background: #fff;
margin: 5px 5px 8px; margin: 5px 5px 8px;
width: 200px; width: 200px;
height: 160px; height: 160px;
@@ -141,7 +132,7 @@ export const WarehouseBoxRoot = styled.div`
position: relative; position: relative;
&:hover { &:hover {
border-color: var(--x-box-hover-border-color); border-color: #0153cc;
} }
`; `;
@@ -152,16 +143,9 @@ export const WarehouseHeader = styled.div`
`; `;
export const WarehouseTitle = styled.div` export const WarehouseTitle = styled.div`
--x-title-color: #000;
--x-title-icon-color: #e1b31d;
.bp4-dark & {
--x-title-color: var(--color-light-gray5);
--x-title-icon-color: #e1b31d;
}
font-size: 14px; font-size: 14px;
font-style: inherit; font-style: inherit;
color: var(--x-title-color); color: #000;
white-space: nowrap; white-space: nowrap;
font-weight: 500; font-weight: 500;
line-height: 1; line-height: 1;
@@ -170,19 +154,14 @@ export const WarehouseTitle = styled.div`
margin: 0; margin: 0;
margin-left: 2px; margin-left: 2px;
vertical-align: top; vertical-align: top;
color: var(--x-title-icon-color); color: #e1b31d;
} }
`; `;
const WarehouseCode = styled.div` const WarehouseCode = styled.div`
--x-code-color: #6b7176;
.bp4-dark & {
--x-code-color: var(--color-muted-text);
}
display: block; display: block;
font-size: 11px; font-size: 11px;
color: var(--x-code-color); color: #6b7176;
margin-top: 4px; margin-top: 4px;
`; `;
@@ -199,13 +178,8 @@ const WarehouseContent = styled.div`
`; `;
const WarehouseItem = styled.div` const WarehouseItem = styled.div`
--x-item-color: #000;
.bp4-dark & {
--x-item-color: var(--color-light-gray1);
}
font-size: 11px; font-size: 11px;
color: var(--x-item-color); color: #000;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden; overflow: hidden;

View File

@@ -1,6 +1,6 @@
// @ts-nocheck // @ts-nocheck
import React from 'react'; import React from 'react';
import { KeyboardShortcutsOptions } from '@/constants/keyboardShortcutsOptions'; import keyboardShortcuts from '@/constants/keyboardShortcutsOptions';
import { useAbilitiesFilter } from '../utils/useAbilityContext'; import { useAbilitiesFilter } from '../utils/useAbilityContext';
/** /**
@@ -10,7 +10,7 @@ export const useKeywordShortcuts = () => {
const abilitiesFilter = useAbilitiesFilter(); const abilitiesFilter = useAbilitiesFilter();
return React.useMemo( return React.useMemo(
() => abilitiesFilter(KeyboardShortcutsOptions), () => abilitiesFilter(keyboardShortcuts),
[abilitiesFilter], [abilitiesFilter],
); );
}; };

View File

@@ -3,7 +3,6 @@ import { useQuery } from 'react-query';
import { castArray, defaultTo } from 'lodash'; import { castArray, defaultTo } from 'lodash';
import { useAuthOrganizationId } from './state'; import { useAuthOrganizationId } from './state';
import useApiRequest from './useRequest'; import useApiRequest from './useRequest';
import { normalizeApiPath } from '../utils';
import { useRef } from 'react'; import { useRef } from 'react';
/** /**
@@ -20,11 +19,7 @@ export function useRequestQuery(query, axios, props) {
const states = useQuery( const states = useQuery(
query, query,
() => () => apiRequest.http({ ...axios, url: `/api/${axios.url}` }),
apiRequest.http({
...axios,
url: `/api/${normalizeApiPath(axios.url)}`,
}),
props, props,
); );
// Momerize the default data. // Momerize the default data.

View File

@@ -7,7 +7,7 @@ import {
useSetGlobalErrors, useSetGlobalErrors,
useAuthToken, useAuthToken,
} from './state'; } from './state';
import { getCookie, normalizeApiPath } from '../utils'; import { getCookie } from '../utils';
export default function useApiRequest() { export default function useApiRequest() {
const setGlobalErrors = useSetGlobalErrors(); const setGlobalErrors = useSetGlobalErrors();
@@ -93,27 +93,27 @@ export default function useApiRequest() {
http, http,
get(resource, params) { get(resource, params) {
return http.get(`/api/${normalizeApiPath(resource)}`, params); return http.get(`/api/${resource}`, params);
}, },
post(resource, params, config) { post(resource, params, config) {
return http.post(`/api/${normalizeApiPath(resource)}`, params, config); return http.post(`/api/${resource}`, params, config);
}, },
update(resource, slug, params) { update(resource, slug, params) {
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params); return http.put(`/api/${resource}/${slug}`, params);
}, },
put(resource, params) { put(resource, params) {
return http.put(`/api/${normalizeApiPath(resource)}`, params); return http.put(`/api/${resource}`, params);
}, },
patch(resource, params, config) { patch(resource, params, config) {
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config); return http.patch(`/api/${resource}`, params, config);
}, },
delete(resource, params) { delete(resource, params) {
return http.delete(`/api/${normalizeApiPath(resource)}`, params); return http.delete(`/api/${resource}`, params);
}, },
}), }),
[http], [http],
@@ -130,22 +130,22 @@ export function useAuthApiRequest() {
() => ({ () => ({
http, http,
get(resource, params) { get(resource, params) {
return http.get(`/api/${normalizeApiPath(resource)}`, params); return http.get(`/api/${resource}`, params);
}, },
post(resource, params, config) { post(resource, params, config) {
return http.post(`/api/${normalizeApiPath(resource)}`, params, config); return http.post(`/api/${resource}`, params, config);
}, },
update(resource, slug, params) { update(resource, slug, params) {
return http.put(`/api/${normalizeApiPath(resource)}/${slug}`, params); return http.put(`/api/${resource}/${slug}`, params);
}, },
put(resource, params) { put(resource, params) {
return http.put(`/api/${normalizeApiPath(resource)}`, params); return http.put(`/api/${resource}`, params);
}, },
patch(resource, params, config) { patch(resource, params, config) {
return http.patch(`/api/${normalizeApiPath(resource)}`, params, config); return http.patch(`/api/${resource}`, params, config);
}, },
delete(resource, params) { delete(resource, params) {
return http.delete(`/api/${normalizeApiPath(resource)}`, params); return http.delete(`/api/${resource}`, params);
}, },
}), }),
[http], [http],

View File

@@ -11,10 +11,7 @@ export const getPreferenceRoutes = () => [
}, },
{ {
path: `${BASE_URL}/branding`, path: `${BASE_URL}/branding`,
component: lazy( component: lazy(() => import('../containers/Preferences/Branding/PreferencesBrandingPage')),
() =>
import('../containers/Preferences/Branding/PreferencesBrandingPage'),
),
exact: true, exact: true,
}, },
{ {
@@ -32,20 +29,14 @@ export const getPreferenceRoutes = () => [
{ {
path: `${BASE_URL}/payment-methods`, path: `${BASE_URL}/payment-methods`,
component: lazy( component: lazy(
() => () => import('../containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage'),
import(
'../containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage'
),
), ),
exact: true, exact: true,
}, },
{ {
path: `${BASE_URL}/payment-methods/stripe/callback`, path: `${BASE_URL}/payment-methods/stripe/callback`,
component: lazy( component: lazy(
() => () => import('../containers/Preferences/PaymentMethods/PreferencesStripeCallback'),
import(
'../containers/Preferences/PaymentMethods/PreferencesStripeCallback'
),
), ),
exact: true, exact: true,
}, },
@@ -121,6 +112,16 @@ export const getPreferenceRoutes = () => [
component: lazy(() => import('@/containers/Preferences/Item')), component: lazy(() => import('@/containers/Preferences/Item')),
exact: true, exact: true,
}, },
// {
// path: `${BASE_URL}/sms-message`,
// component: SMSIntegration,
// exact: true,
// },
{
path: `${BASE_URL}/billing`,
component: lazy(() => import('@/containers/Subscriptions/BillingPage')),
exact: true,
},
{ {
path: `${BASE_URL}/api-keys`, path: `${BASE_URL}/api-keys`,
component: lazy(() => import('@/containers/Preferences/ApiKeys/ApiKeys')), component: lazy(() => import('@/containers/Preferences/ApiKeys/ApiKeys')),

View File

@@ -196,6 +196,7 @@ $ns: bp4;
--color-preferences-sidebar-head-border: #bbcbd0; --color-preferences-sidebar-head-border: #bbcbd0;
--color-preferences-sidebar-head-text: #3b3b4c; --color-preferences-sidebar-head-text: #3b3b4c;
// Preferences - Topbar. // Preferences - Topbar.
--color-preferences-topbar-background: #fff; --color-preferences-topbar-background: #fff;
--color-preferences-topbar-border: #d2dde2; --color-preferences-topbar-border: #d2dde2;
@@ -208,7 +209,7 @@ $ns: bp4;
--color-financial-sheet-title-text: rgb(31, 50, 85); --color-financial-sheet-title-text: rgb(31, 50, 85);
--color-financial-sheet-type-text: rgb(31, 50, 85); --color-financial-sheet-type-text: rgb(31, 50, 85);
--color-financial-sheet-date-text: rgb(31, 50, 85); --color-financial-sheet-date-text: rgb(31, 50, 85);
--color-financial-sheet-footer-text: var(--color-muted-text); --color-financial-sheet-footer-text: rgb(31, 50, 85);
--color-financial-sheet-minimal-title-text: #333; --color-financial-sheet-minimal-title-text: #333;
// Transaction locking. // Transaction locking.
@@ -513,7 +514,7 @@ body.bp4-dark {
--color-financial-sheet-title-text: var(--color-light-gray1); --color-financial-sheet-title-text: var(--color-light-gray1);
--color-financial-sheet-type-text: var(--color-light-gray1); --color-financial-sheet-type-text: var(--color-light-gray1);
--color-financial-sheet-date-text: var(--color-light-gray1); --color-financial-sheet-date-text: var(--color-light-gray1);
--color-financial-sheet-footer-text: var(--color-muted-text); --color-financial-sheet-footer-text: var(--color-light-gray1);
--color-financial-sheet-minimal-title-text: var(--color-white); --color-financial-sheet-minimal-title-text: var(--color-white);
// Transaction locking. // Transaction locking.

View File

@@ -375,32 +375,3 @@
} }
} }
} }
// Sticky header: blurred transparent background so body rows don't show through
.bigcapital-datatable.has-sticky,
.bigcapital-datatable.has-sticky-header {
&.table-constrant .table .thead .th,
&.table--constrant .table .thead .th {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
body.bp4-dark & {
background: rgba(28, 33, 39, 0.6);
}
}
}
// Sticky cells in table body: blurred transparent background so content doesn't show through
.bigcapital-datatable.has-sticky {
&.table-constrant .table .tbody .tr:not(:hover) .td[data-sticky-td],
&.table--constrant .table .tbody .tr:not(:hover) .td[data-sticky-td] {
background: rgba(255, 255, 255, 0.25);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
body.bp4-dark & {
background: rgba(28, 33, 39, 0.6);
}
}
}

View File

@@ -13,9 +13,6 @@ import jsCookie from 'js-cookie';
import { deepMapKeys } from './map-key-deep'; import { deepMapKeys } from './map-key-deep';
export * from './deep'; export * from './deep';
/** Strips leading slash from a path segment to avoid double slashes when joining with a base (e.g. `/api/` + path). */
export const normalizeApiPath = (path) => (path || '').replace(/^\//, '');
export const getCookie = (name, defaultValue) => export const getCookie = (name, defaultValue) =>
_.defaultTo(jsCookie.get(name), defaultValue); _.defaultTo(jsCookie.get(name), defaultValue);