mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 12:50:38 +00:00
@@ -211,7 +211,7 @@ export class InventoryValuationSheet extends FinancialSheet {
|
||||
* Detarmines whether the items post filter is active.
|
||||
*/
|
||||
private isItemsPostFilter = (): boolean => {
|
||||
return isEmpty(this.query.itemsIds);
|
||||
return !isEmpty(this.query.itemsIds);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ export class InventoryValuationSheetService {
|
||||
private readonly inventoryValuationMeta: InventoryValuationMetaInjectable,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
private readonly inventoryValuationSheetRepository: InventoryValuationSheetRepository,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Inventory valuation sheet.
|
||||
|
||||
@@ -172,7 +172,10 @@ export class TrialBalanceSheet extends FinancialSheet {
|
||||
private filterNoneTransactions = (
|
||||
accountNode: ITrialBalanceAccount
|
||||
): boolean => {
|
||||
return false === this.repository.totalAccountsLedger.isEmpty();
|
||||
const accountLedger = this.repository.totalAccountsLedger.whereAccountId(
|
||||
accountNode.id,
|
||||
);
|
||||
return !accountLedger.isEmpty();
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -93,7 +93,7 @@ export class InventoryComputeCostService {
|
||||
*/
|
||||
async scheduleComputeItemCost(itemId: number, startingDate: Date | string) {
|
||||
const debounceKey = `inventory-cost-compute-debounce:${itemId}`;
|
||||
const debounceTime = 1000 * 60; // 1 minute
|
||||
const debounceTime = 1000 * 10; // 10 seconds
|
||||
|
||||
// Generate a unique job ID or use a custom identifier
|
||||
const jobId = `task-${Date.now()}-${Math.random().toString(36).substring(2)}`;
|
||||
|
||||
@@ -2,7 +2,8 @@ import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { Processor, WorkerHost } from '@nestjs/bullmq';
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { Job } from 'bullmq';
|
||||
import { ClsService } from 'nestjs-cls';
|
||||
import { ClsService, UseCls } from 'nestjs-cls';
|
||||
import * as moment from 'moment';
|
||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||
import { InventoryComputeCostService } from '../commands/InventoryComputeCost.service';
|
||||
import { events } from '@/common/events/events';
|
||||
@@ -14,7 +15,7 @@ import { Process } from '@nestjs/bull';
|
||||
|
||||
interface ComputeItemCostJobPayload extends TenantJobPayload {
|
||||
itemId: number;
|
||||
startingDate: Date;
|
||||
startingDate: Date | string;
|
||||
}
|
||||
@Processor({
|
||||
name: ComputeItemCostQueue,
|
||||
@@ -39,28 +40,34 @@ export class ComputeItemCostProcessor extends WorkerHost {
|
||||
* @param {Job<ComputeItemCostJobPayload>} job - The job to process
|
||||
*/
|
||||
@Process(ComputeItemCostQueueJob)
|
||||
@UseCls()
|
||||
async process(job: Job<ComputeItemCostJobPayload>) {
|
||||
const { itemId, startingDate, organizationId, userId } = job.data;
|
||||
|
||||
console.log(`Compute item cost for item ${itemId} started`);
|
||||
// Parse startingDate using moment to handle both Date and string formats
|
||||
const startingDateObj = moment(startingDate).toDate();
|
||||
|
||||
console.log(`[info] Compute item cost for item ${itemId} started`, {
|
||||
payload: job.data,
|
||||
jobId: job.id
|
||||
});
|
||||
this.clsService.set('organizationId', organizationId);
|
||||
this.clsService.set('userId', userId);
|
||||
|
||||
try {
|
||||
await this.inventoryComputeCostService.computeItemCost(
|
||||
startingDate,
|
||||
startingDateObj,
|
||||
itemId,
|
||||
);
|
||||
// Emit job completed event
|
||||
await this.eventEmitter.emitAsync(
|
||||
events.inventory.onComputeItemCostJobCompleted,
|
||||
{ startingDate, itemId, organizationId, userId },
|
||||
{ startingDate: startingDateObj, itemId, organizationId, userId },
|
||||
);
|
||||
|
||||
console.log(`Compute item cost for item ${itemId} completed`);
|
||||
console.log(`[info] Compute item cost for item ${itemId} completed successfully`);
|
||||
} catch (error) {
|
||||
console.error('Error computing item cost:', error);
|
||||
console.error(`[error] Error computing item cost for item ${itemId}:`, error);
|
||||
console.error('Error stack:', error instanceof Error ? error.stack : 'No stack trace');
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export class SaleInvoiceCostGLEntries {
|
||||
private readonly inventoryCostLotTracker: TenantModelProxy<
|
||||
typeof InventoryCostLotTracker
|
||||
>,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Writes journal entries from sales invoices.
|
||||
|
||||
@@ -10,7 +10,7 @@ import { events } from '@/common/events/events';
|
||||
|
||||
@Injectable()
|
||||
export class InvoiceGLEntriesSubscriber {
|
||||
constructor(public readonly saleInvoiceGLEntries: SaleInvoiceGLEntries) {}
|
||||
constructor(public readonly saleInvoiceGLEntries: SaleInvoiceGLEntries) { }
|
||||
|
||||
/**
|
||||
* Records journal entries of the non-inventory invoice.
|
||||
|
||||
@@ -26,7 +26,7 @@ export class TransactionsLockingService {
|
||||
constructor(
|
||||
private readonly transactionsLockingRepo: TransactionsLockingRepository,
|
||||
private readonly eventPublisher: EventEmitter2,
|
||||
) {}
|
||||
) { }
|
||||
|
||||
/**
|
||||
* Enable/disable all transacations locking.
|
||||
|
||||
@@ -20,12 +20,3 @@ export function BranchSelect({ branches, ...rest }) {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function BranchSelectButton({ label, ...rest }) {
|
||||
return <Button text={label} {...rest} />;
|
||||
}
|
||||
|
||||
@@ -171,6 +171,15 @@ export const financialReportMenus = [
|
||||
subject: AbilitySubject.Report,
|
||||
ability: ReportsAction.READ_INVENTORY_ITEM_DETAILS,
|
||||
},
|
||||
{
|
||||
title: <T id={'inventory_valuation'} />,
|
||||
desc: (
|
||||
<T id={'summerize_your_transactions_for_each_inventory_item'} />
|
||||
),
|
||||
link: '/financial-reports/inventory-valuation',
|
||||
subject: AbilitySubject.Report,
|
||||
ability: ReportsAction.READ_INVENTORY_VALUATION_SUMMARY,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FastField, Field, ErrorMessage } from 'formik';
|
||||
import { ErrorMessage, useFormikContext } from 'formik';
|
||||
import {
|
||||
Classes,
|
||||
FormGroup,
|
||||
@@ -10,15 +10,14 @@ import {
|
||||
InputGroup,
|
||||
} from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage as T, If } from '@/components';
|
||||
import { inputIntent, handleStringChange } from '@/utils';
|
||||
import { FieldRequiredHint, ListSelect } from '@/components';
|
||||
import { FormattedMessage as T, If, FFormGroup, FSelect, FRadioGroup, FInputGroup } from '@/components';
|
||||
import { handleStringChange } from '@/utils';
|
||||
import { FieldRequiredHint } from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import allocateLandedCostType from '@/constants/allocateLandedCostType';
|
||||
|
||||
import AllocateLandedCostFormBody from './AllocateLandedCostFormBody';
|
||||
import {
|
||||
transactionsSelectShouldUpdate,
|
||||
allocateCostToEntries,
|
||||
resetAllocatedCostEntries,
|
||||
} from './utils';
|
||||
@@ -32,196 +31,157 @@ export default function AllocateLandedCostFormFields() {
|
||||
const { costTransactionEntries, landedCostTransactions } =
|
||||
useAllocateLandedConstDialogContext();
|
||||
|
||||
const { values, setFieldValue, form } = useFormikContext();
|
||||
|
||||
// Handle transaction type select change.
|
||||
const handleTransactionTypeChange = (type) => {
|
||||
const { items } = values;
|
||||
|
||||
setFieldValue('transaction_type', type.value);
|
||||
setFieldValue('transaction_id', '');
|
||||
setFieldValue('transaction_entry_id', '');
|
||||
setFieldValue('amount', '');
|
||||
setFieldValue('items', resetAllocatedCostEntries(items));
|
||||
};
|
||||
|
||||
// Handle transaction select change.
|
||||
const handleTransactionChange = (transaction) => {
|
||||
const { items } = values;
|
||||
setFieldValue('transaction_id', transaction.id);
|
||||
setFieldValue('transaction_entry_id', '');
|
||||
setFieldValue('amount', '');
|
||||
setFieldValue('items', resetAllocatedCostEntries(items));
|
||||
};
|
||||
|
||||
// Handle transaction entry select change.
|
||||
const handleTransactionEntryChange = (entry) => {
|
||||
const { id, unallocated_cost_amount: unallocatedAmount } = entry;
|
||||
const { items, allocation_method } = values;
|
||||
|
||||
setFieldValue('amount', unallocatedAmount);
|
||||
setFieldValue('transaction_entry_id', id);
|
||||
setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(unallocatedAmount, allocation_method, items),
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
{/*------------Transaction type -----------*/}
|
||||
<FastField
|
||||
<FFormGroup
|
||||
name={'transaction_type'}
|
||||
transactions={allocateLandedCostType}
|
||||
shouldUpdate={transactionsSelectShouldUpdate}
|
||||
label={<T id={'transaction_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
inline
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FormGroup
|
||||
label={<T id={'transaction_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
helperText={<ErrorMessage name="transaction_type" />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
inline={true}
|
||||
className={classNames(CLASSES.FILL, 'form-group--transaction_type')}
|
||||
>
|
||||
<ListSelect
|
||||
items={allocateLandedCostType}
|
||||
onItemSelect={(type) => {
|
||||
const { items } = values;
|
||||
|
||||
setFieldValue('transaction_type', type.value);
|
||||
setFieldValue('transaction_id', '');
|
||||
setFieldValue('transaction_entry_id', '');
|
||||
|
||||
setFieldValue('amount', '');
|
||||
setFieldValue('items', resetAllocatedCostEntries(items));
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'value'}
|
||||
textProp={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FSelect
|
||||
name={'transaction_type'}
|
||||
items={allocateLandedCostType}
|
||||
onItemChange={handleTransactionTypeChange}
|
||||
filterable={false}
|
||||
valueAccessor={'value'}
|
||||
textAccessor={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Transaction -----------*/}
|
||||
<Field
|
||||
<FFormGroup
|
||||
name={'transaction_id'}
|
||||
transactions={landedCostTransactions}
|
||||
shouldUpdate={transactionsSelectShouldUpdate}
|
||||
label={<T id={'transaction_id'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
inline
|
||||
fill
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'transaction_id'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="transaction_id" />}
|
||||
className={classNames(CLASSES.FILL, 'form-group--transaction_id')}
|
||||
inline={true}
|
||||
>
|
||||
<ListSelect
|
||||
items={landedCostTransactions}
|
||||
onItemSelect={({ id }) => {
|
||||
const { items } = form.values;
|
||||
form.setFieldValue('transaction_id', id);
|
||||
form.setFieldValue('transaction_entry_id', '');
|
||||
|
||||
form.setFieldValue('amount', '');
|
||||
form.setFieldValue('items', resetAllocatedCostEntries(items));
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'id'}
|
||||
textProp={'name'}
|
||||
labelProp={'formatted_unallocated_cost_amount'}
|
||||
defaultText={intl.get(
|
||||
'landed_cost.dialog.label_select_transaction',
|
||||
)}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FSelect
|
||||
name={'transaction_id'}
|
||||
items={landedCostTransactions}
|
||||
onItemChange={handleTransactionChange}
|
||||
filterable={false}
|
||||
valueAccessor={'id'}
|
||||
textAccessor={'name'}
|
||||
labelAccessor={'formatted_unallocated_cost_amount'}
|
||||
placeholder={intl.get('landed_cost.dialog.label_select_transaction')}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Transaction line -----------*/}
|
||||
<If condition={costTransactionEntries.length > 0}>
|
||||
<Field
|
||||
<FFormGroup
|
||||
name={'transaction_entry_id'}
|
||||
transactions={costTransactionEntries}
|
||||
shouldUpdate={transactionsSelectShouldUpdate}
|
||||
label={<T id={'transaction_line'} />}
|
||||
inline
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'transaction_line'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="transaction_entry_id" />}
|
||||
className={classNames(
|
||||
CLASSES.FILL,
|
||||
'form-group--transaction_entry_id',
|
||||
)}
|
||||
inline={true}
|
||||
>
|
||||
<ListSelect
|
||||
items={costTransactionEntries}
|
||||
onItemSelect={(entry) => {
|
||||
const { id, unallocated_cost_amount: unallocatedAmount } =
|
||||
entry;
|
||||
const { items, allocation_method } = form.values;
|
||||
|
||||
form.setFieldValue('amount', unallocatedAmount);
|
||||
form.setFieldValue('transaction_entry_id', id);
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(
|
||||
unallocatedAmount,
|
||||
allocation_method,
|
||||
items,
|
||||
),
|
||||
);
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'id'}
|
||||
textProp={'name'}
|
||||
labelProp={'formatted_unallocated_cost_amount'}
|
||||
defaultText={intl.get(
|
||||
'landed_cost.dialog.label_select_transaction_entry',
|
||||
)}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FSelect
|
||||
name={'transaction_entry_id'}
|
||||
items={costTransactionEntries}
|
||||
onItemChange={handleTransactionEntryChange}
|
||||
filterable={false}
|
||||
valueAccessor={'id'}
|
||||
textAccessor={'name'}
|
||||
labelAccessor={'formatted_unallocated_cost_amount'}
|
||||
placeholder={intl.get(
|
||||
'landed_cost.dialog.label_select_transaction_entry',
|
||||
)}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
</If>
|
||||
|
||||
{/*------------ Amount -----------*/}
|
||||
<FastField name={'amount'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'amount'} />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="amount" />}
|
||||
className={'form-group--amount'}
|
||||
inline={true}
|
||||
>
|
||||
<InputGroup
|
||||
{...field}
|
||||
onBlur={(e) => {
|
||||
const amount = e.target.value;
|
||||
const { allocation_method, items } = form.values;
|
||||
<FFormGroup
|
||||
name={'amount'}
|
||||
label={<T id={'amount'} />}
|
||||
inline={true}
|
||||
fastField
|
||||
>
|
||||
<FInputGroup
|
||||
name={'amount'}
|
||||
onBlur={(e) => {
|
||||
const amount = e.target.value;
|
||||
const { allocation_method, items } = values;
|
||||
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, allocation_method, items),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, allocation_method, items),
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Allocation method -----------*/}
|
||||
<Field name={'allocation_method'}>
|
||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||
<FormGroup
|
||||
medium={true}
|
||||
label={<T id={'allocation_method'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={'form-group--allocation_method'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name="allocation_method" />}
|
||||
inline={true}
|
||||
>
|
||||
<RadioGroup
|
||||
onChange={handleStringChange((_value) => {
|
||||
const { amount, items } = form.values;
|
||||
<FFormGroup
|
||||
name={'allocation_method'}
|
||||
label={<T id={'allocation_method'} />}
|
||||
medium
|
||||
inline
|
||||
fastField
|
||||
>
|
||||
<FRadioGroup
|
||||
name={'allocation_method'}
|
||||
onChange={handleStringChange((_value) => {
|
||||
const { amount, items } = values;
|
||||
|
||||
form.setFieldValue('allocation_method', _value);
|
||||
form.setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, _value, items),
|
||||
);
|
||||
})}
|
||||
selectedValue={value}
|
||||
inline={true}
|
||||
>
|
||||
<Radio label={<T id={'quantity'} />} value="quantity" />
|
||||
<Radio label={<T id={'valuation'} />} value="value" />
|
||||
</RadioGroup>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
setFieldValue('allocation_method', _value);
|
||||
setFieldValue(
|
||||
'items',
|
||||
allocateCostToEntries(amount, _value, items),
|
||||
);
|
||||
})}
|
||||
inline={true}
|
||||
>
|
||||
<Radio label={<T id={'quantity'} />} value="quantity" />
|
||||
<Radio label={<T id={'valuation'} />} value="value" />
|
||||
</FRadioGroup>
|
||||
</FFormGroup>
|
||||
|
||||
{/*------------ Allocate Landed cost Table -----------*/}
|
||||
<AllocateLandedCostFormBody />
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as Yup from 'yup';
|
||||
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
||||
import { inputIntent } from '@/utils';
|
||||
import { ListSelect, FieldRequiredHint } from '@/components';
|
||||
import { Button, FormGroup, Intent, Classes } from '@blueprintjs/core';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { FFormGroup, FSelect, FieldRequiredHint } from '@/components';
|
||||
import { Button, Intent, Classes } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { useContactDuplicateFromContext } from './ContactDuplicateProvider';
|
||||
@@ -60,29 +59,22 @@ function ContactDuplicateForm({
|
||||
</p>
|
||||
|
||||
{/*------------ Contact Type -----------*/}
|
||||
<Field name={'contact_type'}>
|
||||
{({ form, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'contact_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
intent={inputIntent({ error, touched })}
|
||||
className={'form-group--select-list'}
|
||||
helperText={<ErrorMessage name="contact_type" />}
|
||||
>
|
||||
<ListSelect
|
||||
items={Contacts}
|
||||
onItemSelect={({ path }) =>
|
||||
form.setFieldValue('contact_type', path)
|
||||
}
|
||||
defaultText={<T id={'select_contact'} />}
|
||||
textProp={'name'}
|
||||
selectedItemProp={'name'}
|
||||
filterable={false}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FFormGroup
|
||||
name={'contact_type'}
|
||||
label={<T id={'contact_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={'form-group--select-list'}
|
||||
>
|
||||
<FSelect
|
||||
name={'contact_type'}
|
||||
items={Contacts}
|
||||
placeholder={<T id={'select_contact'} />}
|
||||
textAccessor={'name'}
|
||||
valueAccessor={'path'}
|
||||
filterable={false}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
<div className={Classes.DIALOG_FOOTER}>
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import classNames from 'classnames';
|
||||
import { FastField, ErrorMessage, Field } from 'formik';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { Classes, FormGroup, Position } from '@blueprintjs/core';
|
||||
import {
|
||||
FFormGroup,
|
||||
@@ -11,20 +11,19 @@ import {
|
||||
FDateInput,
|
||||
FInputGroup,
|
||||
FTextArea,
|
||||
FSelect,
|
||||
} from '@/components';
|
||||
import { useAutofocus } from '@/hooks';
|
||||
import {
|
||||
ListSelect,
|
||||
FieldRequiredHint,
|
||||
Col,
|
||||
Row,
|
||||
FeatureCan,
|
||||
BranchSelect,
|
||||
WarehouseSelect,
|
||||
BranchSelectButton,
|
||||
FAccountsSuggestField,
|
||||
} from '@/components';
|
||||
import { inputIntent, momentFormatter, toSafeNumber } from '@/utils';
|
||||
import { momentFormatter, toSafeNumber } from '@/utils';
|
||||
import { Features, CLASSES } from '@/constants';
|
||||
|
||||
import { useInventoryAdjContext } from './InventoryAdjustmentFormProvider';
|
||||
@@ -52,6 +51,7 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
|
||||
// Inventory adjustment dialog context.
|
||||
const { accounts, branches, warehouses } = useInventoryAdjContext();
|
||||
const { values, setFieldValue } = useFormikContext();
|
||||
|
||||
// Sets the primary warehouse to form.
|
||||
useSetPrimaryWarehouseToForm();
|
||||
@@ -59,6 +59,17 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
// Sets the primary branch to form.
|
||||
useSetPrimaryBranchToForm();
|
||||
|
||||
// Handle adjustment type change.
|
||||
const handleAdjustmentTypeChange = (type) => {
|
||||
const result = diffQuantity(
|
||||
toSafeNumber(values.quantity),
|
||||
toSafeNumber(values.quantity_on_hand),
|
||||
type.value,
|
||||
);
|
||||
setFieldValue('type', type.value);
|
||||
setFieldValue('new_quantity', result);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={Classes.DIALOG_BODY}>
|
||||
<Row>
|
||||
@@ -66,12 +77,11 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'branch'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
fill
|
||||
>
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
@@ -81,7 +91,7 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
<Col xs={5}>
|
||||
<FormGroup
|
||||
label={<T id={'warehouse'} />}
|
||||
className={classNames('form-group--select-list', Classes.FILL)}
|
||||
fill
|
||||
>
|
||||
<WarehouseSelect
|
||||
name={'warehouse_id'}
|
||||
@@ -122,39 +132,24 @@ export default function InventoryAdjustmentFormDialogFields() {
|
||||
|
||||
<Col xs={5}>
|
||||
{/*------------ Adjustment type -----------*/}
|
||||
<Field name={'type'}>
|
||||
{({
|
||||
form: { values, setFieldValue },
|
||||
field: { value },
|
||||
meta: { error, touched },
|
||||
}) => (
|
||||
<FFormGroup
|
||||
name={'type'}
|
||||
label={<T id={'adjustment_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fill
|
||||
>
|
||||
<ListSelect
|
||||
items={adjustmentTypes}
|
||||
onItemSelect={(type) => {
|
||||
const result = diffQuantity(
|
||||
toSafeNumber(values.quantity),
|
||||
toSafeNumber(values.quantity_on_hand),
|
||||
type.value,
|
||||
);
|
||||
setFieldValue('type', type.value);
|
||||
setFieldValue('new_quantity', result);
|
||||
}}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'value'}
|
||||
textProp={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
intent={inputIntent({ error, touched })}
|
||||
/>
|
||||
</FFormGroup>
|
||||
)}
|
||||
</Field>
|
||||
<FFormGroup
|
||||
name={'type'}
|
||||
label={<T id={'adjustment_type'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
fill
|
||||
fastField
|
||||
>
|
||||
<FSelect
|
||||
name={'type'}
|
||||
items={adjustmentTypes}
|
||||
onItemChange={handleAdjustmentTypeChange}
|
||||
filterable={false}
|
||||
valueAccessor={'value'}
|
||||
textAccessor={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ function RefundCreditNoteFormFields({
|
||||
<BranchSelect
|
||||
name={'branch_id'}
|
||||
branches={branches}
|
||||
input={BranchSelectButton}
|
||||
popoverProps={{ minimal: true }}
|
||||
/>
|
||||
</FormGroup>
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
Intent,
|
||||
Classes,
|
||||
Button,
|
||||
} from '@blueprintjs/core';
|
||||
import { FastField, Form, useFormikContext, ErrorMessage } from 'formik';
|
||||
import { Form, useFormikContext } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { FFormGroup, FInputGroup, FormattedMessage as T } from '@/components';
|
||||
import { FFormGroup, FInputGroup, FSelect, FormattedMessage as T } from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { inputIntent } from '@/utils';
|
||||
import { ListSelect, FieldRequiredHint } from '@/components';
|
||||
import { FieldRequiredHint } from '@/components';
|
||||
import { useUserFormContext } from './UserFormProvider';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import { compose } from '@/utils';
|
||||
@@ -68,31 +65,21 @@ function UserFormContent({
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- Role name ----------- */}
|
||||
<FastField name={'role_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'roles.label.role_name'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
helperText={<ErrorMessage name="role_id" />}
|
||||
className={classNames(CLASSES.FILL, 'form-group--role_name')}
|
||||
intent={inputIntent({ error, touched })}
|
||||
>
|
||||
<ListSelect
|
||||
items={roles}
|
||||
onItemSelect={({ id }) => {
|
||||
form.setFieldValue('role_id', id);
|
||||
}}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'id'}
|
||||
textProp={'name'}
|
||||
// labelProp={'id '}
|
||||
popoverProps={{ minimal: true }}
|
||||
intent={inputIntent({ error, touched })}
|
||||
disabled={isAuth}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'role_id'}
|
||||
label={<T id={'roles.label.role_name'} />}
|
||||
labelInfo={<FieldRequiredHint />}
|
||||
className={classNames(CLASSES.FILL, 'form-group--role_name')}
|
||||
>
|
||||
<FSelect
|
||||
name={'role_id'}
|
||||
items={roles}
|
||||
valueAccessor={'id'}
|
||||
textAccessor={'name'}
|
||||
popoverProps={{ minimal: true }}
|
||||
disabled={isAuth}
|
||||
/>
|
||||
</FFormGroup>
|
||||
</div>
|
||||
|
||||
<div className={CLASSES.DIALOG_FOOTER}>
|
||||
|
||||
@@ -105,11 +105,6 @@ function APAgingSummaryActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -106,11 +106,6 @@ function ARAgingSummaryActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -104,18 +104,6 @@ function BalanceSheetActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
|
||||
@@ -107,18 +107,6 @@ function CashFlowStatementActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -104,18 +104,6 @@ function CustomersBalanceSummaryActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -106,18 +106,6 @@ function CustomersTransactionsActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -77,19 +77,6 @@ function GeneralLedgerActionsBar({
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -102,18 +102,6 @@ function InventoryItemDetailsActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -103,18 +103,6 @@ function InventoryValuationActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -55,17 +55,29 @@ const InventoryValuationSheet = styled(FinancialSheet)`
|
||||
`;
|
||||
|
||||
const InventoryValuationDataTable = styled(ReportDataTable)`
|
||||
--color-table-text-color: #252a31;
|
||||
--color-table-total-text-color: #000;
|
||||
--color-table-total-border: #bbb;
|
||||
|
||||
.bp4-dark & {
|
||||
--color-table-text-color: var(--color-light-gray1);
|
||||
--color-table-total-text-color: var(--color-light-gray4);
|
||||
--color-table-total-border: var(--color-dark-gray5);
|
||||
}
|
||||
|
||||
.table {
|
||||
.tbody {
|
||||
.tr .td {
|
||||
border-bottom: 0;
|
||||
padding-top: 0.4rem;
|
||||
padding-bottom: 0.4rem;
|
||||
color: var(--color-table-text-color);
|
||||
}
|
||||
.tr.row_type--TOTAL .td {
|
||||
border-top: 1px solid #bbb;
|
||||
border-top: 1px solid var(--color-table-total-border);
|
||||
border-bottom: 3px double var(--color-table-total-border);
|
||||
font-weight: 500;
|
||||
border-bottom: 3px double #000;
|
||||
color: var(--color-table-total-text-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,19 +78,6 @@ function JournalActionsBar({
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -103,18 +103,6 @@ function ProfitLossActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -93,17 +93,6 @@ function ProjectProfitabilitySummaryActionsBar({
|
||||
icon={<Icon icon="numbers" width={23} height={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
|
||||
@@ -100,17 +100,6 @@ function PurchasesByItemsActionsBar({
|
||||
icon={<Icon icon="numbers" width={23} height={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
|
||||
@@ -91,11 +91,6 @@ function RealizedGainOrLossActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -102,18 +102,6 @@ function SalesByItemsActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -104,18 +104,6 @@ function SalesTaxLiabilitySummaryActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -103,18 +103,6 @@ function TrialBalanceActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" iconSize={16} />}
|
||||
|
||||
@@ -52,6 +52,7 @@ function TrialBalanceSheetHeader({
|
||||
fromDate: moment().toDate(),
|
||||
toDate: moment().toDate(),
|
||||
branchesIds: [],
|
||||
filterByOption: 'with-transactions',
|
||||
};
|
||||
|
||||
// Initial values.
|
||||
|
||||
@@ -92,11 +92,6 @@ function UnrealizedGainOrLossActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -105,11 +105,6 @@ function VendorsBalanceSummaryActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -106,18 +106,6 @@ function VendorsTransactionsActionsBar({
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<Popover
|
||||
// content={}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
>
|
||||
<Button
|
||||
className={classNames(Classes.MINIMAL, 'button--filter')}
|
||||
text={<T id={'filter'} />}
|
||||
icon={<Icon icon="filter-16" iconSize={16} />}
|
||||
/>
|
||||
</Popover>
|
||||
|
||||
<NavbarDivider />
|
||||
|
||||
<Button
|
||||
|
||||
@@ -6,7 +6,8 @@ import classNames from 'classnames';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
ListSelect,
|
||||
FFormGroup,
|
||||
FSelect,
|
||||
FieldRequiredHint,
|
||||
FormattedMessage as T,
|
||||
} from '@/components';
|
||||
@@ -16,29 +17,23 @@ import { inputIntent } from '@/utils';
|
||||
export default function NotifyViaSMSFormFields({ notificationTypes }) {
|
||||
return (
|
||||
<NotifyViaSMSFormFieldsRoot>
|
||||
<FastField name={'notification_key'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'notify_via_sms.dialog.notification_type'} />}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'customer_name'} />}
|
||||
>
|
||||
<ListSelect
|
||||
items={notificationTypes}
|
||||
selectedItemProp={'key'}
|
||||
selectedItem={value}
|
||||
textProp={'label'}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
onItemSelect={(notification) => {
|
||||
form.setFieldValue('notification_key', notification.key);
|
||||
}}
|
||||
disabled={notificationTypes.length < 2}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FFormGroup
|
||||
name={'notification_key'}
|
||||
label={<T id={'notify_via_sms.dialog.notification_type'} />}
|
||||
className={classNames(CLASSES.FILL)}
|
||||
fastField
|
||||
>
|
||||
<FSelect
|
||||
name={'notification_key'}
|
||||
items={notificationTypes}
|
||||
valueAccessor={'key'}
|
||||
textAccessor={'label'}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
disabled={notificationTypes.length < 2}
|
||||
fastField
|
||||
/>
|
||||
</FFormGroup>
|
||||
|
||||
{/* ----------- Send Notification to ----------- */}
|
||||
<FastField name={'customer_name'}>
|
||||
|
||||
98
packages/webapp/src/utils/withConnectHOC.tsx
Normal file
98
packages/webapp/src/utils/withConnectHOC.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { ComponentType } from 'react';
|
||||
import { Dispatch } from 'redux';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
|
||||
/**
|
||||
* Creates a simple dispatch HOC that injects action props into a component.
|
||||
* This is the DRY utility for Pattern 1: Action HOCs.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* export interface WithAlertActionsProps {
|
||||
* openAlert: (name: string) => void;
|
||||
* }
|
||||
*
|
||||
* export const mapDispatchToProps = (dispatch: Dispatch): WithAlertActionsProps => ({
|
||||
* openAlert: (name) => dispatch({ type: 'OPEN_ALERT', name }),
|
||||
* });
|
||||
*
|
||||
* export const withAlertActions = createActionHOC<WithAlertActionsProps>(mapDispatchToProps);
|
||||
* ```
|
||||
*/
|
||||
export function createActionHOC<TInjectedProps extends object>(
|
||||
mapDispatchToProps: (dispatch: Dispatch) => TInjectedProps,
|
||||
) {
|
||||
return function withHOC<P extends TInjectedProps>(
|
||||
WrappedComponent: ComponentType<P>,
|
||||
): ComponentType<Omit<P, keyof TInjectedProps>> {
|
||||
const Connected = connect(null, mapDispatchToProps)(
|
||||
WrappedComponent as ComponentType<any>,
|
||||
);
|
||||
return Connected as unknown as ComponentType<Omit<P, keyof TInjectedProps>>;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a state factory HOC that accepts an optional mapState function.
|
||||
* This is the DRY utility for Pattern 2: State Factory HOCs.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* export interface WithDrawersProps {
|
||||
* isOpen: boolean;
|
||||
* payload: Record<string, unknown>;
|
||||
* }
|
||||
*
|
||||
* export const withDrawers = createStateFactoryHOC<WithDrawersProps>((state, props) => ({
|
||||
* isOpen: isDrawerOpen(state, props),
|
||||
* payload: getDrawerPayload(state, props),
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
export function createStateFactoryHOC<TInjectedProps extends object>(
|
||||
createMapState: () => (state: any, props: any) => TInjectedProps,
|
||||
) {
|
||||
type MapStateFn<T> = (mapped: TInjectedProps, state?: unknown, props?: unknown) => T;
|
||||
|
||||
return function withHOC<P, T = TInjectedProps>(mapState?: MapStateFn<T>) {
|
||||
const mapStateToProps = createMapState();
|
||||
const wrappedMapState = (state: any, props: any) => {
|
||||
const mapped = mapStateToProps(state, props);
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
return function <C extends ComponentType<P>>(WrappedComponent: C) {
|
||||
return connect(wrappedMapState)(WrappedComponent as ComponentType<any>) as unknown as ComponentType<
|
||||
Omit<P, keyof (T extends TInjectedProps ? T : TInjectedProps)>
|
||||
>;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a simple state HOC that directly connects state to component props.
|
||||
* This is for HOCs that don't use the factory pattern.
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
* export interface WithCurrencyDetailProps {
|
||||
* currency: Record<string, unknown> | null;
|
||||
* }
|
||||
*
|
||||
* export const withCurrencyDetail = createStateHOC<WithCurrencyDetailProps>((state, props) => ({
|
||||
* currency: getCurrencyByCode(state, props),
|
||||
* }));
|
||||
* ```
|
||||
*/
|
||||
export function createStateHOC<TInjectedProps extends object>(
|
||||
mapStateToProps: (state: any, props: any) => TInjectedProps,
|
||||
) {
|
||||
return function withHOC<P extends TInjectedProps>(
|
||||
WrappedComponent: ComponentType<P>,
|
||||
): ComponentType<Omit<P, keyof TInjectedProps>> {
|
||||
const Connected = connect(mapStateToProps)(
|
||||
WrappedComponent as ComponentType<any>,
|
||||
);
|
||||
return Connected as unknown as ComponentType<Omit<P, keyof TInjectedProps>>;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user