mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 14:20:31 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e73a34fef | ||
|
|
ea7f987fe3 | ||
|
|
d55503f0c7 | ||
|
|
f59b8166b6 | ||
|
|
2c735d7edf | ||
|
|
5b5ab9fe1e | ||
|
|
28ac9b2d90 | ||
|
|
3300a6a499 | ||
|
|
152a22baa0 | ||
|
|
e873198238 | ||
|
|
68a0db91ee | ||
|
|
ddea7be24a | ||
|
|
b7b86bb0c5 | ||
|
|
817ef906dc | ||
|
|
863c7693fa | ||
|
|
cf78255220 | ||
|
|
f9aa6abdd7 | ||
|
|
0a5e40a0d8 | ||
|
|
4aa445fe35 | ||
|
|
1a1095c99b | ||
|
|
d92b46aa7b | ||
|
|
682d40cbf8 | ||
|
|
b62f3b3fa6 | ||
|
|
e76d3b15ce | ||
|
|
9edfb83221 | ||
|
|
bbdfe00c7a | ||
|
|
e3942551cd | ||
|
|
a0c1a21983 | ||
|
|
3358ce58bc |
@@ -12,6 +12,9 @@
|
|||||||
<a href="https://github.com/bigcapitalhq/bigcapital/commits/develop">
|
<a href="https://github.com/bigcapitalhq/bigcapital/commits/develop">
|
||||||
<img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" />
|
<img src="https://img.shields.io/github/commit-activity/m/bigcapitalhq/bigcapital/develop" />
|
||||||
</a>
|
</a>
|
||||||
|
<a href="https://hub.docker.com/u/bigcapitalhq">
|
||||||
|
<img src="https://img.shields.io/docker/pulls/bigcapitalhq/webapp" />
|
||||||
|
</a>
|
||||||
<a href="https://discord.com/invite/c8nPBJafeb">
|
<a href="https://discord.com/invite/c8nPBJafeb">
|
||||||
<img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" />
|
<img src="https://img.shields.io/discord/1066514716752625725?label=Discord" alt="" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ block head
|
|||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
.#{prefix}-big-title {
|
.#{prefix}-big-title {
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.#{prefix}-logo-wrap img {
|
.#{prefix}-logo-wrap img {
|
||||||
height: auto;
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
max-width: 400px;
|
max-width: 260px;
|
||||||
max-height: 160px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
.#{prefix}-terms-list {
|
.#{prefix}-terms-list {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ block head
|
|||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
.#{prefix}-big-title {
|
.#{prefix}-big-title {
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.#{prefix}-logo-wrap img {
|
.#{prefix}-logo-wrap img {
|
||||||
height: auto;
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
max-width: 400px;
|
max-width: 260px;
|
||||||
max-height: 160px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
.#{prefix}-terms {
|
.#{prefix}-terms {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -30,17 +30,17 @@ block head
|
|||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
.#{prefix}-big-title {
|
.#{prefix}-big-title {
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.#{prefix}-logo-wrap img {
|
.#{prefix}-logo-wrap img {
|
||||||
height: auto;
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
max-width: 400px;
|
max-width: 260px;
|
||||||
max-height: 160px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
.#{prefix}-details {
|
.#{prefix}-details {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -31,17 +31,17 @@ block head
|
|||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
.#{prefix}-big-title{
|
.#{prefix}-big-title{
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
.#{prefix}-logo-wrap img {
|
.#{prefix}-logo-wrap img {
|
||||||
height: auto;
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
max-width: 400px;
|
max-width: 260px;
|
||||||
max-height: 160px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
.#{prefix}-terms-list{
|
.#{prefix}-terms-list{
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -28,13 +28,13 @@ block head
|
|||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
.#{prefix}-logo-wrap img {
|
.#{prefix}-logo-wrap img {
|
||||||
height: auto;
|
width: 100%;
|
||||||
width: auto;
|
height: 100%;
|
||||||
max-width: 400px;
|
max-width: 260px;
|
||||||
max-height: 160px;
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
.#{prefix}-big-title {
|
.#{prefix}-big-title {
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ export const SALE_INVOICE_CREATED = 'Sale invoice created';
|
|||||||
export const SALE_INVOICE_EDITED = 'Sale invoice edited';
|
export const SALE_INVOICE_EDITED = 'Sale invoice edited';
|
||||||
export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
|
export const SALE_INVOICE_DELETED = 'Sale invoice deleted';
|
||||||
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
|
export const SALE_INVOICE_MAIL_DELIVERED = 'Sale invoice mail delivered';
|
||||||
|
export const SALE_INVOICE_VIEWED = 'Sale invoice viewed';
|
||||||
|
|
||||||
export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
|
export const SALE_ESTIMATE_CREATED = 'Sale estimate created';
|
||||||
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
|
export const SALE_ESTIMATE_EDITED = 'Sale estimate edited';
|
||||||
@@ -26,10 +27,12 @@ export const EXPENSE_DELETED = 'Expense deleted';
|
|||||||
export const ACCOUNT_CREATED = 'Account created';
|
export const ACCOUNT_CREATED = 'Account created';
|
||||||
export const ACCOUNT_EDITED = 'Account Edited';
|
export const ACCOUNT_EDITED = 'Account Edited';
|
||||||
export const ACCOUNT_DELETED = 'Account deleted';
|
export const ACCOUNT_DELETED = 'Account deleted';
|
||||||
|
export const ACCOUNT_VIEWED = 'Account viewed';
|
||||||
|
|
||||||
export const ITEM_EVENT_CREATED = 'Item created';
|
export const ITEM_EVENT_CREATED = 'Item created';
|
||||||
export const ITEM_EVENT_EDITED = 'Item edited';
|
export const ITEM_EVENT_EDITED = 'Item edited';
|
||||||
export const ITEM_EVENT_DELETED = 'Item deleted';
|
export const ITEM_EVENT_DELETED = 'Item deleted';
|
||||||
|
export const ITEM_EVENT_VIEWED = 'Item viewed';
|
||||||
|
|
||||||
export const AUTH_SIGNED_UP = 'Auth Signed-up';
|
export const AUTH_SIGNED_UP = 'Auth Signed-up';
|
||||||
export const AUTH_RESET_PASSWORD = 'Auth reset password';
|
export const AUTH_RESET_PASSWORD = 'Auth reset password';
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import I18nService from '@/services/I18n/I18nService';
|
|||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { AccountTransformer } from './AccountTransform';
|
import { AccountTransformer } from './AccountTransform';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetAccount {
|
export class GetAccount {
|
||||||
@@ -15,6 +17,9 @@ export class GetAccount {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private transformer: TransformerInjectable;
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the given account details.
|
* Retrieve the given account details.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -39,6 +44,13 @@ export class GetAccount {
|
|||||||
new AccountTransformer(),
|
new AccountTransformer(),
|
||||||
{ accountsGraph }
|
{ accountsGraph }
|
||||||
);
|
);
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
accountId,
|
||||||
|
};
|
||||||
|
// Triggers `onAccountViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.accounts.onViewed, eventPayload);
|
||||||
|
|
||||||
return this.i18nService.i18nApply(
|
return this.i18nService.i18nApply(
|
||||||
[['accountTypeLabel'], ['accountNormalFormatted']],
|
[['accountTypeLabel'], ['accountNormalFormatted']],
|
||||||
transformed,
|
transformed,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ACCOUNT_CREATED,
|
ACCOUNT_CREATED,
|
||||||
ACCOUNT_EDITED,
|
ACCOUNT_EDITED,
|
||||||
ACCOUNT_DELETED,
|
ACCOUNT_DELETED,
|
||||||
|
ACCOUNT_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -31,6 +32,7 @@ export class AccountEventsTracker extends EventSubscriber {
|
|||||||
events.accounts.onDeleted,
|
events.accounts.onDeleted,
|
||||||
this.handleTrackDeletedAccountEvent
|
this.handleTrackDeletedAccountEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(events.accounts.onViewed, this.handleTrackAccountViewedEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackAccountCreatedEvent = ({
|
private handleTrackAccountCreatedEvent = ({
|
||||||
@@ -62,4 +64,12 @@ export class AccountEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackAccountViewedEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: ACCOUNT_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
ITEM_EVENT_CREATED,
|
ITEM_EVENT_CREATED,
|
||||||
ITEM_EVENT_EDITED,
|
ITEM_EVENT_EDITED,
|
||||||
ITEM_EVENT_DELETED,
|
ITEM_EVENT_DELETED,
|
||||||
|
ITEM_EVENT_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -25,6 +26,7 @@ export class ItemEventsTracker extends EventSubscriber {
|
|||||||
bus.subscribe(events.item.onCreated, this.handleTrackItemCreatedEvent);
|
bus.subscribe(events.item.onCreated, this.handleTrackItemCreatedEvent);
|
||||||
bus.subscribe(events.item.onEdited, this.handleTrackEditedItemEvent);
|
bus.subscribe(events.item.onEdited, this.handleTrackEditedItemEvent);
|
||||||
bus.subscribe(events.item.onDeleted, this.handleTrackDeletedItemEvent);
|
bus.subscribe(events.item.onDeleted, this.handleTrackDeletedItemEvent);
|
||||||
|
bus.subscribe(events.item.onViewed, this.handleTrackViewedItemEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackItemCreatedEvent = ({
|
private handleTrackItemCreatedEvent = ({
|
||||||
@@ -56,4 +58,14 @@ export class ItemEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackViewedItemEvent = ({
|
||||||
|
tenantId,
|
||||||
|
}: IItemEventDeletedPayload) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: ITEM_EVENT_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
SALE_INVOICE_CREATED,
|
SALE_INVOICE_CREATED,
|
||||||
SALE_INVOICE_DELETED,
|
SALE_INVOICE_DELETED,
|
||||||
SALE_INVOICE_EDITED,
|
SALE_INVOICE_EDITED,
|
||||||
|
SALE_INVOICE_VIEWED,
|
||||||
} from '@/constants/event-tracker';
|
} from '@/constants/event-tracker';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
@@ -33,6 +34,10 @@ export class SaleInvoiceEventsTracker extends EventSubscriber {
|
|||||||
events.saleInvoice.onDeleted,
|
events.saleInvoice.onDeleted,
|
||||||
this.handleTrackDeletedInvoiceEvent
|
this.handleTrackDeletedInvoiceEvent
|
||||||
);
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onViewed,
|
||||||
|
this.handleTrackViewedInvoiceEvent
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleTrackInvoiceCreatedEvent = ({
|
private handleTrackInvoiceCreatedEvent = ({
|
||||||
@@ -64,4 +69,12 @@ export class SaleInvoiceEventsTracker extends EventSubscriber {
|
|||||||
properties: {},
|
properties: {},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private handleTrackViewedInvoiceEvent = ({ tenantId }) => {
|
||||||
|
this.posthog.trackEvent({
|
||||||
|
distinctId: `tenant-${tenantId}`,
|
||||||
|
event: SALE_INVOICE_VIEWED,
|
||||||
|
properties: {},
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import { IItem } from '@/interfaces';
|
|||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import ItemTransformer from './ItemTransformer';
|
import ItemTransformer from './ItemTransformer';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
export class GetItem {
|
export class GetItem {
|
||||||
@@ -12,6 +14,9 @@ export class GetItem {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private transformer: TransformerInjectable;
|
private transformer: TransformerInjectable;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the item details of the given id with associated details.
|
* Retrieve the item details of the given id with associated details.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
@@ -31,6 +36,16 @@ export class GetItem {
|
|||||||
.withGraphFetched('purchaseTaxRate')
|
.withGraphFetched('purchaseTaxRate')
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
return this.transformer.transform(tenantId, item, new ItemTransformer());
|
const transformed = await this.transformer.transform(
|
||||||
|
tenantId,
|
||||||
|
item,
|
||||||
|
new ItemTransformer()
|
||||||
|
);
|
||||||
|
const eventPayload = { tenantId, itemId };
|
||||||
|
|
||||||
|
// Triggers the `onItemViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(events.item.onViewed, eventPayload);
|
||||||
|
|
||||||
|
return transformed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import * as R from 'ramda';
|
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
|
||||||
import { Inject, Service } from 'typedi';
|
import { Inject, Service } from 'typedi';
|
||||||
import { isEmpty } from 'lodash';
|
import { isNil } from 'lodash';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class BrandingTemplateDTOTransformer {
|
export class BrandingTemplateDTOTransformer {
|
||||||
@@ -22,11 +21,12 @@ export class BrandingTemplateDTOTransformer {
|
|||||||
const { PdfTemplate } = this.tenancy.models(tenantId);
|
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||||
const attributeName = 'pdfTemplateId';
|
const attributeName = 'pdfTemplateId';
|
||||||
|
|
||||||
const defaultTemplate = await PdfTemplate.query().findOne({
|
const defaultTemplate = await PdfTemplate.query()
|
||||||
resource,
|
.modify('default')
|
||||||
default: true,
|
.findOne({ resource });
|
||||||
});
|
|
||||||
if (!defaultTemplate || !isEmpty(object[attributeName])) {
|
// If the default template is not found OR the given object has no defined template id.
|
||||||
|
if (!defaultTemplate || !isNil(object[attributeName])) {
|
||||||
return object;
|
return object;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import { SaleInvoiceTransformer } from './SaleInvoiceTransformer';
|
|||||||
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
|
||||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||||
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
|
import { CommandSaleInvoiceValidators } from './CommandSaleInvoiceValidators';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class GetSaleInvoice {
|
export class GetSaleInvoice {
|
||||||
@@ -16,6 +18,9 @@ export class GetSaleInvoice {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private validators: CommandSaleInvoiceValidators;
|
private validators: CommandSaleInvoiceValidators;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private eventPublisher: EventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve sale invoice with associated entries.
|
* Retrieve sale invoice with associated entries.
|
||||||
* @param {Number} saleInvoiceId -
|
* @param {Number} saleInvoiceId -
|
||||||
@@ -41,10 +46,20 @@ export class GetSaleInvoice {
|
|||||||
// Validates the given sale invoice existance.
|
// Validates the given sale invoice existance.
|
||||||
this.validators.validateInvoiceExistance(saleInvoice);
|
this.validators.validateInvoiceExistance(saleInvoice);
|
||||||
|
|
||||||
return this.transformer.transform(
|
const transformed = await this.transformer.transform(
|
||||||
tenantId,
|
tenantId,
|
||||||
saleInvoice,
|
saleInvoice,
|
||||||
new SaleInvoiceTransformer()
|
new SaleInvoiceTransformer()
|
||||||
);
|
);
|
||||||
|
const eventPayload = {
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceId,
|
||||||
|
};
|
||||||
|
// Triggers the `onSaleInvoiceItemViewed` event.
|
||||||
|
await this.eventPublisher.emitAsync(
|
||||||
|
events.saleInvoice.onViewed,
|
||||||
|
eventPayload
|
||||||
|
);
|
||||||
|
return transformed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ export default {
|
|||||||
* Accounts service.
|
* Accounts service.
|
||||||
*/
|
*/
|
||||||
accounts: {
|
accounts: {
|
||||||
|
onViewed: 'onAccountViewed',
|
||||||
|
onListViewed: 'onAccountsListViewed',
|
||||||
|
|
||||||
onCreating: 'onAccountCreating',
|
onCreating: 'onAccountCreating',
|
||||||
onCreated: 'onAccountCreated',
|
onCreated: 'onAccountCreated',
|
||||||
|
|
||||||
@@ -127,6 +130,9 @@ export default {
|
|||||||
* Sales invoices service.
|
* Sales invoices service.
|
||||||
*/
|
*/
|
||||||
saleInvoice: {
|
saleInvoice: {
|
||||||
|
onViewed: 'onSaleInvoiceItemViewed',
|
||||||
|
onListViewed: 'onSaleInvoiceListViewed',
|
||||||
|
|
||||||
onCreate: 'onSaleInvoiceCreate',
|
onCreate: 'onSaleInvoiceCreate',
|
||||||
onCreating: 'onSaleInvoiceCreating',
|
onCreating: 'onSaleInvoiceCreating',
|
||||||
onCreated: 'onSaleInvoiceCreated',
|
onCreated: 'onSaleInvoiceCreated',
|
||||||
@@ -338,6 +344,8 @@ export default {
|
|||||||
* Items service.
|
* Items service.
|
||||||
*/
|
*/
|
||||||
item: {
|
item: {
|
||||||
|
onViewed: 'onItemViewed',
|
||||||
|
|
||||||
onCreated: 'onItemCreated',
|
onCreated: 'onItemCreated',
|
||||||
onCreating: 'onItemCreating',
|
onCreating: 'onItemCreating',
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
"@casl/ability": "^5.4.3",
|
"@casl/ability": "^5.4.3",
|
||||||
"@casl/react": "^2.3.0",
|
"@casl/react": "^2.3.0",
|
||||||
"@craco/craco": "^5.9.0",
|
"@craco/craco": "^5.9.0",
|
||||||
|
"@emotion/css": "^11.13.4",
|
||||||
|
"@emotion/react": "^11.13.3",
|
||||||
"@reduxjs/toolkit": "^1.2.5",
|
"@reduxjs/toolkit": "^1.2.5",
|
||||||
"@stripe/connect-js": "^3.3.12",
|
"@stripe/connect-js": "^3.3.12",
|
||||||
"@stripe/react-connect-js": "^3.3.13",
|
"@stripe/react-connect-js": "^3.3.13",
|
||||||
@@ -38,6 +40,7 @@
|
|||||||
"@types/react": "^16.14.28",
|
"@types/react": "^16.14.28",
|
||||||
"@types/react-body-classname": "^1.1.7",
|
"@types/react-body-classname": "^1.1.7",
|
||||||
"@types/react-dom": "^16.9.16",
|
"@types/react-dom": "^16.9.16",
|
||||||
|
"@types/react-helmet": "^6.1.11",
|
||||||
"@types/react-redux": "^7.1.24",
|
"@types/react-redux": "^7.1.24",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/react-transition-group": "^4.4.5",
|
"@types/react-transition-group": "^4.4.5",
|
||||||
@@ -47,6 +50,7 @@
|
|||||||
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
"@typescript-eslint/eslint-plugin": "^2.10.0",
|
||||||
"@typescript-eslint/parser": "^2.10.0",
|
"@typescript-eslint/parser": "^2.10.0",
|
||||||
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
|
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
|
||||||
|
"@xstyled/emotion": "^3.8.1",
|
||||||
"accounting": "^0.4.1",
|
"accounting": "^0.4.1",
|
||||||
"axios": "^1.6.0",
|
"axios": "^1.6.0",
|
||||||
"basscss": "^8.0.2",
|
"basscss": "^8.0.2",
|
||||||
@@ -60,6 +64,7 @@
|
|||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
"flat": "^5.0.2",
|
"flat": "^5.0.2",
|
||||||
"formik": "^2.2.5",
|
"formik": "^2.2.5",
|
||||||
|
"helmet": "^3.21.0",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"http-proxy-middleware": "^1.0.0",
|
"http-proxy-middleware": "^1.0.0",
|
||||||
"jest": "24.9.0",
|
"jest": "24.9.0",
|
||||||
@@ -88,6 +93,7 @@
|
|||||||
"react-dropzone-esm": "^15.0.1",
|
"react-dropzone-esm": "^15.0.1",
|
||||||
"react-error-boundary": "^3.0.2",
|
"react-error-boundary": "^3.0.2",
|
||||||
"react-error-overlay": "^6.0.9",
|
"react-error-overlay": "^6.0.9",
|
||||||
|
"react-helmet": "^6.1.0",
|
||||||
"react-hotkeys-hook": "^3.0.3",
|
"react-hotkeys-hook": "^3.0.3",
|
||||||
"react-intl-universal": "^2.4.7",
|
"react-intl-universal": "^2.4.7",
|
||||||
"react-loadable": "^5.5.0",
|
"react-loadable": "^5.5.0",
|
||||||
@@ -121,6 +127,7 @@
|
|||||||
"style-loader": "0.23.1",
|
"style-loader": "0.23.1",
|
||||||
"styled-components": "^5.3.1",
|
"styled-components": "^5.3.1",
|
||||||
"stylis-rtlcss": "^2.1.1",
|
"stylis-rtlcss": "^2.1.1",
|
||||||
|
"theme-ui": "^0.16.2",
|
||||||
"typescript": "^4.8.3",
|
"typescript": "^4.8.3",
|
||||||
"yup": "^0.28.1"
|
"yup": "^0.28.1"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ const OneClickDemoPage = lazy(
|
|||||||
const PaymentPortalPage = lazy(
|
const PaymentPortalPage = lazy(
|
||||||
() => import('@/containers/PaymentPortal/PaymentPortalPage'),
|
() => import('@/containers/PaymentPortal/PaymentPortalPage'),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App inner.
|
* App inner.
|
||||||
*/
|
*/
|
||||||
@@ -59,7 +60,10 @@ function AppInsider({ history }) {
|
|||||||
children={<EmailConfirmation />}
|
children={<EmailConfirmation />}
|
||||||
/>
|
/>
|
||||||
<Route path={'/auth'} children={<AuthenticationPage />} />
|
<Route path={'/auth'} children={<AuthenticationPage />} />
|
||||||
<Route path={'/payment/:linkId'} children={<PaymentPortalPage />} />
|
<Route
|
||||||
|
path={'/payment/:linkId'}
|
||||||
|
children={<PaymentPortalPage />}
|
||||||
|
/>
|
||||||
<Route path={'/'} children={<DashboardPrivatePages />} />
|
<Route path={'/'} children={<DashboardPrivatePages />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { createContext } from 'react';
|
import React, { createContext } from 'react';
|
||||||
|
|
||||||
const AppIntlContext = createContext();
|
interface AppIntlContextValue {
|
||||||
|
currentLocale: string;
|
||||||
|
direction: 'rtl' | 'ltr';
|
||||||
|
isRTL: boolean;
|
||||||
|
isLTR: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AppIntlContext = createContext<AppIntlContextValue>(
|
||||||
|
{} as AppIntlContextValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
interface AppIntlProviderProps {
|
||||||
|
currentLocale: string;
|
||||||
|
isRTL: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application intl provider.
|
* Application intl provider.
|
||||||
*/
|
*/
|
||||||
function AppIntlProvider({ currentLocale, isRTL, children }) {
|
function AppIntlProvider({
|
||||||
|
currentLocale,
|
||||||
|
isRTL,
|
||||||
|
children,
|
||||||
|
}: AppIntlProviderProps) {
|
||||||
const provider = {
|
const provider = {
|
||||||
currentLocale,
|
currentLocale,
|
||||||
isRTL,
|
isRTL,
|
||||||
@@ -21,6 +40,7 @@ function AppIntlProvider({ currentLocale, isRTL, children }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const useAppIntlContext = () => React.useContext(AppIntlContext);
|
const useAppIntlContext = () =>
|
||||||
|
React.useContext<AppIntlContextValue>(AppIntlContext);
|
||||||
|
|
||||||
export { AppIntlProvider, useAppIntlContext };
|
export { AppIntlProvider, useAppIntlContext };
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { LoadingIndicator } from '../Indicator';
|
import { LoadingIndicator } from '../Indicator';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
export function DashboardInsider({
|
export function DashboardInsider({
|
||||||
loading,
|
loading,
|
||||||
@@ -9,6 +10,7 @@ export function DashboardInsider({
|
|||||||
name,
|
name,
|
||||||
mount = false,
|
mount = false,
|
||||||
className,
|
className,
|
||||||
|
style
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -17,9 +19,11 @@ export function DashboardInsider({
|
|||||||
dashboard__insider: true,
|
dashboard__insider: true,
|
||||||
'dashboard__insider--loading': loading,
|
'dashboard__insider--loading': loading,
|
||||||
[`dashboard__insider--${name}`]: !!name,
|
[`dashboard__insider--${name}`]: !!name,
|
||||||
|
|
||||||
},
|
},
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
|
style={style}
|
||||||
>
|
>
|
||||||
<LoadingIndicator loading={loading} mount={mount}>
|
<LoadingIndicator loading={loading} mount={mount}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,9 +1,20 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { ThemeProvider, StyleSheetManager } from 'styled-components';
|
import {
|
||||||
|
ThemeProvider as StyleComponentsThemeProvider,
|
||||||
|
StyleSheetManager,
|
||||||
|
} from 'styled-components';
|
||||||
import rtlcss from 'stylis-rtlcss';
|
import rtlcss from 'stylis-rtlcss';
|
||||||
|
import {
|
||||||
|
defaultTheme,
|
||||||
|
ThemeProvider as XStyledEmotionThemeProvider,
|
||||||
|
} from '@xstyled/emotion';
|
||||||
import { useAppIntlContext } from '../AppIntlProvider';
|
import { useAppIntlContext } from '../AppIntlProvider';
|
||||||
|
|
||||||
|
const theme = {
|
||||||
|
...defaultTheme,
|
||||||
|
bpPrefix: 'bp4',
|
||||||
|
};
|
||||||
|
|
||||||
interface DashboardThemeProviderProps {
|
interface DashboardThemeProviderProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
@@ -17,7 +28,11 @@ export function DashboardThemeProvider({
|
|||||||
<StyleSheetManager
|
<StyleSheetManager
|
||||||
{...(direction === 'rtl' ? { stylisPlugins: [rtlcss] } : {})}
|
{...(direction === 'rtl' ? { stylisPlugins: [rtlcss] } : {})}
|
||||||
>
|
>
|
||||||
<ThemeProvider theme={{ dir: direction }}>{children}</ThemeProvider>
|
<StyleComponentsThemeProvider theme={{ dir: direction }}>
|
||||||
|
<XStyledEmotionThemeProvider theme={theme}>
|
||||||
|
{children}
|
||||||
|
</XStyledEmotionThemeProvider>
|
||||||
|
</StyleComponentsThemeProvider>
|
||||||
</StyleSheetManager>
|
</StyleSheetManager>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import React, { forwardRef, Ref } from 'react';
|
import React, { forwardRef, Ref } from 'react';
|
||||||
import { HTMLDivProps, Props } from '@blueprintjs/core';
|
import { HTMLDivProps, Props } from '@blueprintjs/core';
|
||||||
|
import { SystemProps, x } from '@xstyled/emotion';
|
||||||
|
|
||||||
export interface BoxProps extends Props, HTMLDivProps {
|
export interface BoxProps
|
||||||
className?: string;
|
extends SystemProps,
|
||||||
}
|
Props,
|
||||||
|
Omit<HTMLDivProps, 'color'> {}
|
||||||
|
|
||||||
export const Box = forwardRef(
|
export const Box = forwardRef(
|
||||||
({ className, ...rest }: BoxProps, ref: Ref<HTMLDivElement>) => {
|
({ className, ...rest }: BoxProps, ref: Ref<HTMLDivElement>) => {
|
||||||
const Element = 'div';
|
const Element = x.div;
|
||||||
|
|
||||||
return <Element className={className} ref={ref} {...rest} />;
|
return <Element className={className} ref={ref} {...rest} />;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import { SystemProps } from '@xstyled/emotion';
|
||||||
import { Box } from '../Box';
|
import { Box } from '../Box';
|
||||||
import { filterFalsyChildren } from './_utils';
|
import { filterFalsyChildren } from './_utils';
|
||||||
|
|
||||||
@@ -12,7 +12,9 @@ export const GROUP_POSITIONS = {
|
|||||||
apart: 'space-between',
|
apart: 'space-between',
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface GroupProps extends React.ComponentPropsWithoutRef<'div'> {
|
export interface GroupProps
|
||||||
|
extends SystemProps,
|
||||||
|
Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
||||||
/** Defines justify-content property */
|
/** Defines justify-content property */
|
||||||
position?: GroupPosition;
|
position?: GroupPosition;
|
||||||
|
|
||||||
@@ -27,34 +29,30 @@ export interface GroupProps extends React.ComponentPropsWithoutRef<'div'> {
|
|||||||
|
|
||||||
/** Defines align-items css property */
|
/** Defines align-items css property */
|
||||||
align?: React.CSSProperties['alignItems'];
|
align?: React.CSSProperties['alignItems'];
|
||||||
|
|
||||||
flex?: React.CSSProperties['flex'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<GroupProps> = {
|
export function Group({
|
||||||
position: 'left',
|
position = 'left',
|
||||||
spacing: 20,
|
spacing = 20,
|
||||||
flex: 'none'
|
align = 'center',
|
||||||
};
|
noWrap,
|
||||||
|
children,
|
||||||
export function Group({ children, ...props }: GroupProps) {
|
...props
|
||||||
const groupProps = {
|
}: GroupProps) {
|
||||||
...defaultProps,
|
|
||||||
...props,
|
|
||||||
};
|
|
||||||
const filteredChildren = filterFalsyChildren(children);
|
const filteredChildren = filterFalsyChildren(children);
|
||||||
|
|
||||||
return <GroupStyled {...groupProps}>{filteredChildren}</GroupStyled>;
|
return (
|
||||||
|
<Box
|
||||||
|
boxSizing={'border-box'}
|
||||||
|
display={'flex'}
|
||||||
|
flexDirection={'row'}
|
||||||
|
alignItems={align}
|
||||||
|
flexWrap={noWrap ? 'nowrap' : 'wrap'}
|
||||||
|
justifyContent={GROUP_POSITIONS[position]}
|
||||||
|
gap={`${spacing}px`}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{filteredChildren}
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const GroupStyled = styled(Box)`
|
|
||||||
box-sizing: border-box;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
flex: ${(props: GroupProps) => (props.flex)};
|
|
||||||
align-items: ${(props: GroupProps) => (props.align || 'center')};
|
|
||||||
flex-wrap: ${(props: GroupProps) => (props.noWrap ? 'nowrap' : 'wrap')};
|
|
||||||
justify-content: ${(props: GroupProps) =>
|
|
||||||
GROUP_POSITIONS[props.position || 'left']};
|
|
||||||
gap: ${(props: GroupProps) => props.spacing}px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import { x, SystemProps } from '@xstyled/emotion';
|
||||||
import { Box } from '../Box';
|
|
||||||
|
|
||||||
export interface StackProps extends React.ComponentPropsWithoutRef<'div'> {
|
export interface StackProps
|
||||||
|
extends SystemProps,
|
||||||
|
Omit<React.ComponentPropsWithoutRef<'div'>, 'color'> {
|
||||||
/** Key of theme.spacing or number to set gap in px */
|
/** Key of theme.spacing or number to set gap in px */
|
||||||
spacing?: number;
|
spacing?: number;
|
||||||
|
|
||||||
@@ -11,30 +12,22 @@ export interface StackProps extends React.ComponentPropsWithoutRef<'div'> {
|
|||||||
|
|
||||||
/** justify-content CSS property */
|
/** justify-content CSS property */
|
||||||
justify?: React.CSSProperties['justifyContent'];
|
justify?: React.CSSProperties['justifyContent'];
|
||||||
|
|
||||||
flex?: React.CSSProperties['flex'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultProps: Partial<StackProps> = {
|
export function Stack({
|
||||||
spacing: 20,
|
spacing = 20,
|
||||||
align: 'stretch',
|
align = 'stretch',
|
||||||
justify: 'top',
|
justify = 'top',
|
||||||
flex: 'none',
|
...restProps
|
||||||
};
|
}: StackProps) {
|
||||||
|
return (
|
||||||
export function Stack(props: StackProps) {
|
<x.div
|
||||||
const stackProps = {
|
display={'flex'}
|
||||||
...defaultProps,
|
flexDirection="column"
|
||||||
...props,
|
justifyContent="justify"
|
||||||
};
|
gap={`${spacing}px`}
|
||||||
return <StackStyled {...stackProps} />;
|
alignItems={align}
|
||||||
|
{...restProps}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const StackStyled = styled(Box)`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: ${(props: StackProps) => props.align};
|
|
||||||
justify-content: justify;
|
|
||||||
gap: ${(props: StackProps) => props.spacing}px;
|
|
||||||
flex: ${(props: StackProps) => props.flex};
|
|
||||||
`;
|
|
||||||
|
|||||||
91
packages/webapp/src/components/PageForm/PageForm.tsx
Normal file
91
packages/webapp/src/components/PageForm/PageForm.tsx
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import React, { FC } from 'react';
|
||||||
|
import clsx from 'classnames';
|
||||||
|
import { x, SystemProps } from '@xstyled/emotion';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { Group, GroupProps } from '@/components';
|
||||||
|
|
||||||
|
interface PageFormProps extends SystemProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page form layout.
|
||||||
|
* @returns {React.ReactNode}
|
||||||
|
*/
|
||||||
|
export const PageForm = ({ children, ...props }: PageFormProps) => {
|
||||||
|
return (
|
||||||
|
<x.div display="flex" flexDirection={'column'} overflow="hidden" {...props}>
|
||||||
|
{children}
|
||||||
|
</x.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
PageForm.displayName = 'PageFormBody';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page form body layout, by default the content body is scrollable.
|
||||||
|
* @returns {React.ReactNode}
|
||||||
|
*/
|
||||||
|
const PageFormBody: FC<{ children: React.ReactNode } & SystemProps> = ({
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<x.div flex="1" overflow="auto" {...props}>
|
||||||
|
{children}
|
||||||
|
</x.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
PageFormBody.displayName = 'PageFormBody';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page form footer.
|
||||||
|
* @returns {React.ReactNode}
|
||||||
|
*/
|
||||||
|
const PageFormFooter: FC<{ children: React.ReactNode } & SystemProps> = ({ children }) => {
|
||||||
|
return <x.div>{children} </x.div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
PageFormFooter.displayName = 'PageFormFooter';
|
||||||
|
|
||||||
|
const footerActionsStyle = `
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
padding: 14px 20px;
|
||||||
|
border-top: 1px solid rgb(210, 221, 226);
|
||||||
|
box-shadow: 0px -1px 4px 0px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
|
.bp4-button-group{
|
||||||
|
.bp4-button{
|
||||||
|
&:not(:last-child),
|
||||||
|
&.bp4-popover-wrapper:not(:last-child) {
|
||||||
|
border-right: 1px solid rgba(92, 112, 127, 0.3);
|
||||||
|
margin-right: 0;
|
||||||
|
|
||||||
|
&.bp4-intent-primary{
|
||||||
|
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PageFormFooterActions: FC<GroupProps> = ({
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
...restProps
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Group
|
||||||
|
spacing={20}
|
||||||
|
{...restProps}
|
||||||
|
className={clsx(css(footerActionsStyle), className)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
PageFormFooterActions.displayName = 'PageFormFooterActions';
|
||||||
|
|
||||||
|
PageForm.Body = PageFormBody;
|
||||||
|
PageForm.Footer = PageFormFooter;
|
||||||
|
PageForm.FooterActions = PageFormFooterActions;
|
||||||
@@ -2,3 +2,4 @@
|
|||||||
export * from './FormTopbar';
|
export * from './FormTopbar';
|
||||||
export * from './FormTopbarSelectInputs';
|
export * from './FormTopbarSelectInputs';
|
||||||
export * from './PageFormBigNumber';
|
export * from './PageFormBigNumber';
|
||||||
|
export * from './PageForm';
|
||||||
@@ -1,13 +1,20 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import { x, SystemProps } from '@xstyled/emotion';
|
||||||
|
|
||||||
export function Paper({ children, className }) {
|
interface PaperProps extends SystemProps {
|
||||||
return <PaperRoot className={className}>{children}</PaperRoot>;
|
children: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaperRoot = styled.div`
|
export const Paper = ({ children, ...props }: PaperProps) => {
|
||||||
border: 1px solid #d2dce2;
|
return (
|
||||||
background: #fff;
|
<x.div
|
||||||
padding: 10px;
|
background={'white'}
|
||||||
`;
|
p={'10px'}
|
||||||
|
border={'1px solid #d2dce2'}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</x.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
Paper.displayName = 'Paper';
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { Row, Col, Paper } from '@/components';
|
import { Row, Col, Paper } from '@/components';
|
||||||
@@ -12,7 +11,7 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
export default function MakeJournalFormFooter() {
|
export default function MakeJournalFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<MakeJournalFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<MakeJournalFormFooterLeft />
|
<MakeJournalFormFooterLeft />
|
||||||
@@ -23,10 +22,7 @@ export default function MakeJournalFormFooter() {
|
|||||||
<MakeJournalFormFooterRight />
|
<MakeJournalFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</MakeJournalFooterPaper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const MakeJournalFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
|
import { Spinner } from '@blueprintjs/core';
|
||||||
import {
|
import {
|
||||||
GetPdfTemplateBrandingStateResponse,
|
GetPdfTemplateBrandingStateResponse,
|
||||||
GetPdfTemplateResponse,
|
GetPdfTemplateResponse,
|
||||||
useGetPdfTemplate,
|
useGetPdfTemplate,
|
||||||
useGetPdfTemplateBrandingState,
|
useGetPdfTemplateBrandingState,
|
||||||
} from '@/hooks/query/pdf-templates';
|
} from '@/hooks/query/pdf-templates';
|
||||||
import { Spinner } from '@blueprintjs/core';
|
|
||||||
|
|
||||||
interface PdfTemplateContextValue {
|
interface PdfTemplateContextValue {
|
||||||
templateId: number | string;
|
templateId: number | string;
|
||||||
|
|
||||||
|
// Pdf template.
|
||||||
pdfTemplate: GetPdfTemplateResponse | undefined;
|
pdfTemplate: GetPdfTemplateResponse | undefined;
|
||||||
isPdfTemplateLoading: boolean;
|
isPdfTemplateLoading: boolean;
|
||||||
|
|
||||||
// Branding state.
|
// Branding state.
|
||||||
brandingTemplateState: GetPdfTemplateBrandingStateResponse | undefined;
|
brandingTemplateState: GetPdfTemplateBrandingStateResponse;
|
||||||
isBrandingTemplateLoading: boolean;
|
isBrandingTemplateLoading: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,10 +36,17 @@ export const BrandingTemplateBoot = ({
|
|||||||
useGetPdfTemplate(templateId, {
|
useGetPdfTemplate(templateId, {
|
||||||
enabled: !!templateId,
|
enabled: !!templateId,
|
||||||
});
|
});
|
||||||
// Retreives the branding template state.
|
// Retrieves the branding template state.
|
||||||
const { data: brandingTemplateState, isLoading: isBrandingTemplateLoading } =
|
const { data: brandingTemplateState, isLoading: isBrandingTemplateLoading } =
|
||||||
useGetPdfTemplateBrandingState();
|
useGetPdfTemplateBrandingState();
|
||||||
|
|
||||||
|
const isLoading = isPdfTemplateLoading ||
|
||||||
|
isBrandingTemplateLoading ||
|
||||||
|
!brandingTemplateState;
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <Spinner size={20} />;
|
||||||
|
}
|
||||||
const value = {
|
const value = {
|
||||||
templateId,
|
templateId,
|
||||||
pdfTemplate,
|
pdfTemplate,
|
||||||
@@ -47,11 +56,6 @@ export const BrandingTemplateBoot = ({
|
|||||||
isBrandingTemplateLoading,
|
isBrandingTemplateLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading = isPdfTemplateLoading || isBrandingTemplateLoading;
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <Spinner size={20} />;
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<PdfTemplateContext.Provider value={value}>
|
<PdfTemplateContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
transformToEditRequest,
|
transformToEditRequest,
|
||||||
transformToNewRequest,
|
transformToNewRequest,
|
||||||
|
useBrandingState,
|
||||||
useBrandingTemplateFormInitialValues,
|
useBrandingTemplateFormInitialValues,
|
||||||
} from './_utils';
|
} from './_utils';
|
||||||
import { AppToaster } from '@/components';
|
import { AppToaster } from '@/components';
|
||||||
@@ -17,31 +18,40 @@ import {
|
|||||||
useEditPdfTemplate,
|
useEditPdfTemplate,
|
||||||
} from '@/hooks/query/pdf-templates';
|
} from '@/hooks/query/pdf-templates';
|
||||||
import { FormikHelpers } from 'formik';
|
import { FormikHelpers } from 'formik';
|
||||||
import { BrandingTemplateValues } from './types';
|
import { BrandingTemplateValues, BrandingState } from './types';
|
||||||
import { useUploadAttachments } from '@/hooks/query/attachments';
|
import { useUploadAttachments } from '@/hooks/query/attachments';
|
||||||
import { excludePrivateProps } from '@/utils';
|
import { excludePrivateProps } from '@/utils';
|
||||||
|
|
||||||
interface BrandingTemplateFormProps<T> extends ElementCustomizeProps<T> {
|
interface BrandingTemplateFormProps<
|
||||||
|
T extends BrandingTemplateValues,
|
||||||
|
Y extends BrandingState
|
||||||
|
> extends ElementCustomizeProps<T, Y> {
|
||||||
resource: string;
|
resource: string;
|
||||||
templateId?: number;
|
templateId?: number;
|
||||||
onSuccess?: () => void;
|
onSuccess?: () => void;
|
||||||
onError?: () => void;
|
onError?: () => void;
|
||||||
|
|
||||||
|
/* The default values hold the initial values of the form and the values being sent to the server. */
|
||||||
defaultValues?: T;
|
defaultValues?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function BrandingTemplateForm<T extends BrandingTemplateValues>({
|
export function BrandingTemplateForm<
|
||||||
|
T extends BrandingTemplateValues,
|
||||||
|
Y extends BrandingState,
|
||||||
|
>({
|
||||||
templateId,
|
templateId,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onError,
|
onError,
|
||||||
defaultValues,
|
defaultValues,
|
||||||
resource,
|
resource,
|
||||||
...props
|
...props
|
||||||
}: BrandingTemplateFormProps<T>) {
|
}: BrandingTemplateFormProps<T, Y>) {
|
||||||
|
// Create/edit pdf template mutators.
|
||||||
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
|
const { mutateAsync: createPdfTemplate } = useCreatePdfTemplate();
|
||||||
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
|
const { mutateAsync: editPdfTemplate } = useEditPdfTemplate();
|
||||||
|
|
||||||
const initialValues = useBrandingTemplateFormInitialValues<T>(defaultValues);
|
const initialValues = useBrandingTemplateFormInitialValues<T>(defaultValues);
|
||||||
const [isUploading, setIsLoading] = useState<boolean>(false);
|
const [, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
// Uploads the attachments.
|
// Uploads the attachments.
|
||||||
const { mutateAsync: uploadAttachments } = useUploadAttachments({
|
const { mutateAsync: uploadAttachments } = useUploadAttachments({
|
||||||
@@ -122,7 +132,7 @@ export function BrandingTemplateForm<T extends BrandingTemplateValues>({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElementCustomize<T>
|
<ElementCustomize<T, Y>
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
validationSchema={validationSchema}
|
validationSchema={validationSchema}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Button } from '@blueprintjs/core';
|
import { Button, ButtonProps } from '@blueprintjs/core';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { FFormGroup } from '@/components';
|
import { FFormGroup, Icon } from '@/components';
|
||||||
|
|
||||||
export const BrandingThemeFormGroup = styled(FFormGroup)`
|
export const BrandingThemeFormGroup = styled(FFormGroup)`
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@@ -14,33 +14,21 @@ export const BrandingThemeFormGroup = styled(FFormGroup)`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const BrandingThemeSelectButton = styled(Button)`
|
export const BrandingThemeSelectButton = (props: ButtonProps) => {
|
||||||
position: relative;
|
return (
|
||||||
padding-right: 26px;
|
<Button
|
||||||
|
text={props?.text || 'Brand Theme'}
|
||||||
|
rightIcon={<Icon icon="arrow-drop-up-16" iconSize={20} />}
|
||||||
|
minimal
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
&::after {
|
export const convertBrandingTemplatesToOptions = (
|
||||||
content: '';
|
brandingTemplates: Array<any>,
|
||||||
display: inline-block;
|
) => {
|
||||||
width: 0;
|
|
||||||
height: 0;
|
|
||||||
|
|
||||||
border-left: 4px solid transparent;
|
|
||||||
border-right: 4px solid transparent;
|
|
||||||
border-top: 5px solid #98a1ae;
|
|
||||||
|
|
||||||
position: absolute;
|
|
||||||
right: -2px;
|
|
||||||
top: 50%;
|
|
||||||
margin-top: -2px;
|
|
||||||
margin-right: 12px;
|
|
||||||
border-radius: 1px;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
|
|
||||||
export const convertBrandingTemplatesToOptions = (brandingTemplates: Array<any>) => {
|
|
||||||
return brandingTemplates?.map(
|
return brandingTemplates?.map(
|
||||||
(template) =>
|
(template) => ({ text: template.template_name, value: template.id } || []),
|
||||||
({ text: template.template_name, value: template.id } || []),
|
);
|
||||||
)
|
};
|
||||||
}
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
} from '@/hooks/query/pdf-templates';
|
} from '@/hooks/query/pdf-templates';
|
||||||
import { useBrandingTemplateBoot } from './BrandingTemplateBoot';
|
import { useBrandingTemplateBoot } from './BrandingTemplateBoot';
|
||||||
import { transformToForm } from '@/utils';
|
import { transformToForm } from '@/utils';
|
||||||
import { BrandingTemplateValues } from './types';
|
import { BrandingState, BrandingTemplateValues } from './types';
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
const commonExcludedAttrs = ['templateName', 'companyLogoUri'];
|
const commonExcludedAttrs = ['templateName', 'companyLogoUri'];
|
||||||
@@ -44,11 +44,10 @@ export const useBrandingTemplateFormInitialValues = <
|
|||||||
>(
|
>(
|
||||||
initialValues = {},
|
initialValues = {},
|
||||||
) => {
|
) => {
|
||||||
const { pdfTemplate, brandingTemplateState } = useBrandingTemplateBoot();
|
const { pdfTemplate } = useBrandingTemplateBoot();
|
||||||
|
|
||||||
const brandingAttributes = {
|
const brandingAttributes = {
|
||||||
templateName: pdfTemplate?.templateName,
|
templateName: pdfTemplate?.templateName,
|
||||||
...brandingTemplateState,
|
|
||||||
...pdfTemplate?.attributes,
|
...pdfTemplate?.attributes,
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
@@ -57,6 +56,15 @@ export const useBrandingTemplateFormInitialValues = <
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useBrandingState = (state?: Partial<BrandingState>): BrandingState => {
|
||||||
|
const { brandingTemplateState } = useBrandingTemplateBoot();
|
||||||
|
|
||||||
|
return {
|
||||||
|
...brandingTemplateState,
|
||||||
|
...state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const getCustomizeDrawerNameFromResource = (resource: string) => {
|
export const getCustomizeDrawerNameFromResource = (resource: string) => {
|
||||||
const pairs = {
|
const pairs = {
|
||||||
SaleInvoice: DRAWERS.INVOICE_CUSTOMIZE,
|
SaleInvoice: DRAWERS.INVOICE_CUSTOMIZE,
|
||||||
|
|||||||
@@ -7,3 +7,17 @@ export interface BrandingTemplateValues {
|
|||||||
companyLogoKey?: string;
|
companyLogoKey?: string;
|
||||||
companyLogoUri?: string;
|
companyLogoUri?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BrandingState extends ElementPreviewState {
|
||||||
|
companyName: string;
|
||||||
|
companyAddress: string;
|
||||||
|
|
||||||
|
companyLogoKey: string;
|
||||||
|
companyLogoUri: string;
|
||||||
|
|
||||||
|
primaryColor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ElementPreviewState {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,17 +9,23 @@ import { ElementCustomizeTabsControllerProvider } from './ElementCustomizeTabsCo
|
|||||||
import { ElementCustomizeFields } from './ElementCustomizeFields';
|
import { ElementCustomizeFields } from './ElementCustomizeFields';
|
||||||
import { ElementCustomizePreview } from './ElementCustomizePreview';
|
import { ElementCustomizePreview } from './ElementCustomizePreview';
|
||||||
import { extractChildren } from '@/utils/extract-children';
|
import { extractChildren } from '@/utils/extract-children';
|
||||||
|
import { ElementPreviewState } from '../BrandingTemplates/types';
|
||||||
|
import { TabProps } from '@blueprintjs/core';
|
||||||
|
import { useBrandingState } from '../BrandingTemplates/_utils';
|
||||||
|
|
||||||
export interface ElementCustomizeProps<T> extends ElementCustomizeFormProps<T> {
|
export interface ElementCustomizeProps<T, Y>
|
||||||
|
extends ElementCustomizeFormProps<T, Y> {
|
||||||
|
brandingState?: Y;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ElementCustomize<T>({
|
export interface ElementCustomizeContentProps {
|
||||||
initialValues,
|
children?: React.ReactNode;
|
||||||
validationSchema,
|
}
|
||||||
onSubmit,
|
|
||||||
|
export function ElementCustomizeContent({
|
||||||
children,
|
children,
|
||||||
}: ElementCustomizeProps<T>) {
|
}: ElementCustomizeContentProps) {
|
||||||
const PaperTemplate = React.useMemo(
|
const PaperTemplate = React.useMemo(
|
||||||
() => extractChildren(children, ElementCustomize.PaperTemplate),
|
() => extractChildren(children, ElementCustomize.PaperTemplate),
|
||||||
[children],
|
[children],
|
||||||
@@ -28,15 +34,10 @@ export function ElementCustomize<T>({
|
|||||||
() => extractChildren(children, ElementCustomize.FieldsTab),
|
() => extractChildren(children, ElementCustomize.FieldsTab),
|
||||||
[children],
|
[children],
|
||||||
);
|
);
|
||||||
|
const brandingState = useBrandingState();
|
||||||
const value = { PaperTemplate, CustomizeTabs };
|
const value = { PaperTemplate, CustomizeTabs, brandingState };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ElementCustomizeForm
|
|
||||||
initialValues={initialValues}
|
|
||||||
validationSchema={validationSchema}
|
|
||||||
onSubmit={onSubmit}
|
|
||||||
>
|
|
||||||
<ElementCustomizeTabsControllerProvider>
|
<ElementCustomizeTabsControllerProvider>
|
||||||
<ElementCustomizeProvider value={value}>
|
<ElementCustomizeProvider value={value}>
|
||||||
<Group spacing={0} align="stretch">
|
<Group spacing={0} align="stretch">
|
||||||
@@ -45,6 +46,22 @@ export function ElementCustomize<T>({
|
|||||||
</Group>
|
</Group>
|
||||||
</ElementCustomizeProvider>
|
</ElementCustomizeProvider>
|
||||||
</ElementCustomizeTabsControllerProvider>
|
</ElementCustomizeTabsControllerProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ElementCustomize<T, Y extends ElementPreviewState>({
|
||||||
|
initialValues,
|
||||||
|
validationSchema,
|
||||||
|
onSubmit,
|
||||||
|
children,
|
||||||
|
}: ElementCustomizeProps<T, Y>) {
|
||||||
|
return (
|
||||||
|
<ElementCustomizeForm
|
||||||
|
initialValues={initialValues}
|
||||||
|
validationSchema={validationSchema}
|
||||||
|
onSubmit={onSubmit}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
</ElementCustomizeForm>
|
</ElementCustomizeForm>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -59,16 +76,17 @@ ElementCustomize.PaperTemplate = ({
|
|||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface ElementCustomizeContentProps {
|
export interface ElementCustomizeFieldsTabProps {
|
||||||
id: string;
|
id: string;
|
||||||
label: string;
|
label: string;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
|
tabProps?: Partial<TabProps>;
|
||||||
}
|
}
|
||||||
|
|
||||||
ElementCustomize.FieldsTab = ({
|
ElementCustomize.FieldsTab = ({
|
||||||
id,
|
id,
|
||||||
label,
|
label,
|
||||||
children,
|
children,
|
||||||
}: ElementCustomizeContentProps) => {
|
}: ElementCustomizeFieldsTabProps) => {
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import React, { createContext, useContext } from 'react';
|
import React, { createContext, useContext } from 'react';
|
||||||
|
import { ElementPreviewState } from '../BrandingTemplates/types';
|
||||||
|
|
||||||
interface ElementCustomizeValue {
|
interface ElementCustomizeValue {
|
||||||
PaperTemplate?: React.ReactNode;
|
PaperTemplate?: React.ReactNode;
|
||||||
CustomizeTabs: React.ReactNode;
|
CustomizeTabs: React.ReactNode;
|
||||||
|
brandingState?: ElementPreviewState;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ElementCustomizeContext = createContext<ElementCustomizeValue>(
|
const ElementCustomizeContext = createContext<ElementCustomizeValue>(
|
||||||
{} as ElementCustomizeValue,
|
{} as ElementCustomizeValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ElementCustomizeProvider: React.FC<{
|
interface ElementCustomizeProviderProps {
|
||||||
value: ElementCustomizeValue;
|
value: ElementCustomizeValue;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}> = ({ value, children }) => {
|
}
|
||||||
|
|
||||||
|
export const ElementCustomizeProvider = ({ value, children }: ElementCustomizeProviderProps) => {
|
||||||
return (
|
return (
|
||||||
<ElementCustomizeContext.Provider value={{ ...value }}>
|
<ElementCustomizeContext.Provider value={{ ...value }}>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Box, Stack } from '@/components';
|
import { Box, Stack } from '@/components';
|
||||||
import { Tab, Tabs } from '@blueprintjs/core';
|
import { Tab, TabProps, Tabs } from '@blueprintjs/core';
|
||||||
import { ElementCustomizeHeader } from './ElementCustomizeHeader';
|
import { ElementCustomizeHeader } from './ElementCustomizeHeader';
|
||||||
import {
|
import {
|
||||||
ElementCustomizeTabsEnum,
|
ElementCustomizeTabsEnum,
|
||||||
@@ -11,7 +11,6 @@ import styles from './ElementCustomizeTabs.module.scss';
|
|||||||
|
|
||||||
export function ElementCustomizeTabs() {
|
export function ElementCustomizeTabs() {
|
||||||
const { setCurrentTabId } = useElementCustomizeTabsController();
|
const { setCurrentTabId } = useElementCustomizeTabsController();
|
||||||
|
|
||||||
const { CustomizeTabs } = useElementCustomizeContext();
|
const { CustomizeTabs } = useElementCustomizeContext();
|
||||||
|
|
||||||
const tabItems = React.Children.map(CustomizeTabs, (node) => ({
|
const tabItems = React.Children.map(CustomizeTabs, (node) => ({
|
||||||
@@ -32,9 +31,19 @@ export function ElementCustomizeTabs() {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
className={styles.tabsList}
|
className={styles.tabsList}
|
||||||
>
|
>
|
||||||
{tabItems?.map(({ id, label }: { id: string; label: string }) => (
|
{tabItems?.map(
|
||||||
<Tab id={id} key={id} title={label} />
|
({
|
||||||
))}
|
id,
|
||||||
|
label,
|
||||||
|
tabProps,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
tabProps?: TabProps;
|
||||||
|
}) => (
|
||||||
|
<Tab id={id} key={id} title={label} {...tabProps} />
|
||||||
|
),
|
||||||
|
)}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Formik, Form, FormikHelpers } from 'formik';
|
import { Formik, Form, FormikHelpers } from 'formik';
|
||||||
|
|
||||||
export interface ElementCustomizeFormProps<T> {
|
export interface ElementCustomizeFormProps<T, Y> {
|
||||||
initialValues?: T;
|
initialValues?: T;
|
||||||
validationSchema?: any;
|
validationSchema?: any;
|
||||||
onSubmit?: (values: T, formikHelpers: FormikHelpers<T>) => void;
|
onSubmit?: (values: T, formikHelpers: FormikHelpers<T>) => void;
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ export function BrandingCompanyLogoUploadField() {
|
|||||||
onChange={(file) => {
|
onChange={(file) => {
|
||||||
const imageUrl = file ? URL.createObjectURL(file) : '';
|
const imageUrl = file ? URL.createObjectURL(file) : '';
|
||||||
|
|
||||||
|
// Reset the logo key since it is changed.
|
||||||
|
setFieldValue('companyLogoKey', '');
|
||||||
|
|
||||||
setFieldValue('_companyLogoFile', file);
|
setFieldValue('_companyLogoFile', file);
|
||||||
setFieldValue('companyLogoUri', imageUrl);
|
setFieldValue('companyLogoUri', imageUrl);
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import { Row, Col, Paper } from '@/components';
|
import { Row, Col, Paper } from '@/components';
|
||||||
@@ -12,7 +11,7 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
export default function ExpenseFormFooter() {
|
export default function ExpenseFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<ExpensesFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<ExpenseFormFooterLeft />
|
<ExpenseFormFooterLeft />
|
||||||
@@ -23,11 +22,7 @@ export default function ExpenseFormFooter() {
|
|||||||
<ExpenseFormFooterRight />
|
<ExpenseFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</ExpensesFooterPaper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ExpensesFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { PaymentPortal } from './PaymentPortal';
|
import { Helmet } from 'react-helmet';
|
||||||
import { PaymentPortalBoot } from './PaymentPortalBoot';
|
|
||||||
import BodyClassName from 'react-body-classname';
|
import BodyClassName from 'react-body-classname';
|
||||||
import styles from './PaymentPortal.module.scss';
|
import { PaymentPortal } from './PaymentPortal';
|
||||||
|
import { PaymentPortalBoot, usePaymentPortalBoot } from './PaymentPortalBoot';
|
||||||
import { PaymentInvoicePreviewDrawer } from './drawers/PaymentInvoicePreviewDrawer/PaymentInvoicePreviewDrawer';
|
import { PaymentInvoicePreviewDrawer } from './drawers/PaymentInvoicePreviewDrawer/PaymentInvoicePreviewDrawer';
|
||||||
import { DRAWERS } from '@/constants/drawers';
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
import styles from './PaymentPortal.module.scss';
|
||||||
|
|
||||||
export default function PaymentPortalPage() {
|
export default function PaymentPortalPage() {
|
||||||
const { linkId } = useParams<{ linkId: string }>();
|
const { linkId } = useParams<{ linkId: string }>();
|
||||||
@@ -12,9 +13,26 @@ export default function PaymentPortalPage() {
|
|||||||
return (
|
return (
|
||||||
<BodyClassName className={styles.rootBodyPage}>
|
<BodyClassName className={styles.rootBodyPage}>
|
||||||
<PaymentPortalBoot linkId={linkId}>
|
<PaymentPortalBoot linkId={linkId}>
|
||||||
|
<PaymentPortalHelmet />
|
||||||
<PaymentPortal />
|
<PaymentPortal />
|
||||||
<PaymentInvoicePreviewDrawer name={DRAWERS.PAYMENT_INVOICE_PREVIEW} />
|
<PaymentInvoicePreviewDrawer name={DRAWERS.PAYMENT_INVOICE_PREVIEW} />
|
||||||
</PaymentPortalBoot>
|
</PaymentPortalBoot>
|
||||||
</BodyClassName>
|
</BodyClassName>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the document title of the current payment page.
|
||||||
|
* @returns {React.ReactNode}
|
||||||
|
*/
|
||||||
|
function PaymentPortalHelmet() {
|
||||||
|
const { sharableLinkMeta } = usePaymentPortalBoot();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Helmet>
|
||||||
|
<title>
|
||||||
|
{sharableLinkMeta?.invoiceNo} | {sharableLinkMeta?.organization?.name}
|
||||||
|
</title>
|
||||||
|
</Helmet>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export function StripePaymentMethod() {
|
|||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Button small icon={<MoreIcon size={16} />} />
|
<Button small icon={<MoreIcon height={10} width={10} />} />
|
||||||
</Popover>
|
</Popover>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
export default function BillFormFooter() {
|
export default function BillFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<BillFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<BillFormFooterLeft />
|
<BillFormFooterLeft />
|
||||||
@@ -24,11 +24,7 @@ export default function BillFormFooter() {
|
|||||||
<BillFormFooterRight />
|
<BillFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</BillFooterPaper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const BillFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
export default function VendorCreditNoteFormFooter() {
|
export default function VendorCreditNoteFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div class={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<VendorCreditNoteFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<VendorCreditNoteFormFooterLeft />
|
<VendorCreditNoteFormFooterLeft />
|
||||||
@@ -26,11 +26,7 @@ export default function VendorCreditNoteFormFooter() {
|
|||||||
<VendorCreditNoteFormFooterRight />
|
<VendorCreditNoteFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</VendorCreditNoteFooterPaper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const VendorCreditNoteFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
export default function PaymentMadeFooter() {
|
export default function PaymentMadeFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
||||||
<PaymentReceiveFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<PaymentMadeFormFooterLeft />
|
<PaymentMadeFormFooterLeft />
|
||||||
@@ -26,11 +26,7 @@ export default function PaymentMadeFooter() {
|
|||||||
<PaymentMadeFormFooterRight />
|
<PaymentMadeFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</PaymentReceiveFooterPaper>
|
</Paper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaymentReceiveFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
import {
|
||||||
|
ElementCustomize,
|
||||||
|
ElementCustomizeContent,
|
||||||
|
} from '../../../ElementCustomize/ElementCustomize';
|
||||||
import { CreditNoteCustomizeGeneralField } from './CreditNoteCustomizeGeneralFields';
|
import { CreditNoteCustomizeGeneralField } from './CreditNoteCustomizeGeneralFields';
|
||||||
import { CreditNoteCustomizeContentFields } from './CreditNoteCutomizeContentFields';
|
import { CreditNoteCustomizeContentFields } from './CreditNoteCutomizeContentFields';
|
||||||
import { CreditNotePaperTemplate } from './CreditNotePaperTemplate';
|
import {
|
||||||
import { CreditNoteCustomizeValues } from './types';
|
CreditNotePaperTemplate,
|
||||||
|
CreditNotePaperTemplateProps,
|
||||||
|
} from './CreditNotePaperTemplate';
|
||||||
|
import { CreditNoteBrandingState, CreditNoteCustomizeValues } from './types';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
|
||||||
export function CreditNoteCustomizeContent() {
|
export function CreditNoteCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -20,12 +28,22 @@ export function CreditNoteCustomizeContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrandingTemplateForm<CreditNoteCustomizeValues>
|
<BrandingTemplateForm<CreditNoteCustomizeValues, CreditNoteBrandingState>
|
||||||
resource={'CreditNote'}
|
resource={'CreditNote'}
|
||||||
templateId={templateId}
|
templateId={templateId}
|
||||||
defaultValues={initialValues}
|
defaultValues={initialValues}
|
||||||
onSuccess={handleSuccess}
|
onSuccess={handleSuccess}
|
||||||
>
|
>
|
||||||
|
<CreditNoteCustomizeFormContent />
|
||||||
|
</BrandingTemplateForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CreditNoteCustomizeFormContent() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<CreditNotePaperTemplateFormConnected />
|
<CreditNotePaperTemplateFormConnected />
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
@@ -34,15 +52,25 @@ export function CreditNoteCustomizeContent() {
|
|||||||
<CreditNoteCustomizeGeneralField />
|
<CreditNoteCustomizeGeneralField />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
<ElementCustomize.FieldsTab
|
||||||
|
id={'content'}
|
||||||
|
label={'Content'}
|
||||||
|
tabProps={{ disabled: !isTemplateNameFilled }}
|
||||||
|
>
|
||||||
<CreditNoteCustomizeContentFields />
|
<CreditNoteCustomizeContentFields />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
</BrandingTemplateForm>
|
</ElementCustomizeContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function CreditNotePaperTemplateFormConnected() {
|
function CreditNotePaperTemplateFormConnected() {
|
||||||
const { values } = useFormikContext<CreditNoteCustomizeValues>();
|
const { values } = useFormikContext<CreditNoteCustomizeValues>();
|
||||||
|
const { brandingState } = useElementCustomizeContext();
|
||||||
|
|
||||||
return <CreditNotePaperTemplate {...values} />;
|
const mergedProps: CreditNotePaperTemplateProps = {
|
||||||
|
...brandingState,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <CreditNotePaperTemplate {...mergedProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export const initialValues = {
|
|||||||
// Address
|
// Address
|
||||||
showCustomerAddress: true,
|
showCustomerAddress: true,
|
||||||
showCompanyAddress: true,
|
showCompanyAddress: true,
|
||||||
companyAddress: '',
|
|
||||||
billedToLabel: 'Billed To',
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
import { BrandingState, BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
||||||
|
|
||||||
|
export interface CreditNoteBrandingState extends BrandingState {}
|
||||||
|
|
||||||
export interface CreditNoteCustomizeValues extends BrandingTemplateValues {
|
export interface CreditNoteCustomizeValues extends BrandingTemplateValues {
|
||||||
// Colors
|
// Colors
|
||||||
|
|||||||
@@ -12,21 +12,30 @@ import {
|
|||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
import {
|
||||||
import { CLASSES } from '@/constants/classes';
|
If,
|
||||||
import classNames from 'classnames';
|
Icon,
|
||||||
|
FormattedMessage as T,
|
||||||
|
Group,
|
||||||
|
FSelect,
|
||||||
|
PageForm,
|
||||||
|
} from '@/components';
|
||||||
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
||||||
import {
|
import {
|
||||||
BrandingThemeFormGroup,
|
BrandingThemeFormGroup,
|
||||||
BrandingThemeSelectButton,
|
BrandingThemeSelectButton,
|
||||||
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
import { useCreditNoteFormBrandingTemplatesOptions } from './utils';
|
import { useCreditNoteFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import { MoreIcon } from '@/icons/More';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note floating actions.
|
* Credit note floating actions.
|
||||||
*/
|
*/
|
||||||
export default function CreditNoteFloatingActions() {
|
export default function CreditNoteFloatingActions() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { openDrawer } = useDrawerActions();
|
||||||
|
|
||||||
// Formik context.
|
// Formik context.
|
||||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
@@ -79,13 +88,16 @@ export default function CreditNoteFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles the credit note customize button click.
|
||||||
|
const handleCustomizeBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'CreditNote' });
|
||||||
|
};
|
||||||
|
|
||||||
const brandingTemplatesOptions = useCreditNoteFormBrandingTemplatesOptions();
|
const brandingTemplatesOptions = useCreditNoteFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<PageForm.FooterActions position={'apart'} spacing={20}>
|
||||||
spacing={10}
|
<Group spacing={10}>
|
||||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
|
||||||
>
|
|
||||||
{/* ----------- Save And Open ----------- */}
|
{/* ----------- Save And Open ----------- */}
|
||||||
<If condition={!creditNote || !creditNote?.is_open}>
|
<If condition={!creditNote || !creditNote?.is_open}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -197,7 +209,9 @@ export default function CreditNoteFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group spacing={0}>
|
||||||
{/* ----------- Branding Template Select ----------- */}
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
<BrandingThemeFormGroup
|
<BrandingThemeFormGroup
|
||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
@@ -216,6 +230,27 @@ export default function CreditNoteFloatingActions() {
|
|||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</BrandingThemeFormGroup>
|
</BrandingThemeFormGroup>
|
||||||
|
|
||||||
|
{/* ----------- Setting Select ----------- */}
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.TOP_RIGHT}
|
||||||
|
modifiers={{
|
||||||
|
offset: { offset: '0, 4' },
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Customize Templates'}
|
||||||
|
onClick={handleCustomizeBtnClick}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button minimal icon={<MoreIcon height={'14px'} width={'14px'} />} />
|
||||||
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
|
</PageForm.FooterActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { defaultTo, isEmpty } from 'lodash';
|
import { defaultTo, isEmpty } from 'lodash';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateCreditNoteFormSchema,
|
CreateCreditNoteFormSchema,
|
||||||
EditCreditNoteFormSchema,
|
EditCreditNoteFormSchema,
|
||||||
@@ -42,6 +42,7 @@ import {
|
|||||||
CreditNoteExchangeRateSync,
|
CreditNoteExchangeRateSync,
|
||||||
CreditNoteSyncIncrementSettingsToForm,
|
CreditNoteSyncIncrementSettingsToForm,
|
||||||
} from './components';
|
} from './components';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note form.
|
* Credit note form.
|
||||||
@@ -148,13 +149,6 @@ function CreditNoteForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
CLASSES.PAGE_FORM,
|
|
||||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
|
||||||
CLASSES.PAGE_FORM_CREDIT_NOTE,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={
|
validationSchema={
|
||||||
isNewMode ? CreateCreditNoteFormSchema : EditCreditNoteFormSchema
|
isNewMode ? CreateCreditNoteFormSchema : EditCreditNoteFormSchema
|
||||||
@@ -162,12 +156,26 @@ function CreditNoteForm({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form
|
||||||
|
className={css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<PageForm flex="1">
|
||||||
|
<PageForm.Body>
|
||||||
<CreditNoteFormTopBar />
|
<CreditNoteFormTopBar />
|
||||||
<CreditNoteFormHeader />
|
<CreditNoteFormHeader />
|
||||||
<CreditNoteItemsEntriesEditorField />
|
<CreditNoteItemsEntriesEditorField />
|
||||||
<CreditNoteFormFooter />
|
<CreditNoteFormFooter />
|
||||||
|
</PageForm.Body>
|
||||||
|
|
||||||
|
<PageForm.Footer>
|
||||||
<CreditNoteFloatingActions />
|
<CreditNoteFloatingActions />
|
||||||
|
</PageForm.Footer>
|
||||||
|
</PageForm>
|
||||||
|
|
||||||
{/*-------- Dialogs --------*/}
|
{/*-------- Dialogs --------*/}
|
||||||
<CreditNoteFormDialogs />
|
<CreditNoteFormDialogs />
|
||||||
@@ -177,7 +185,6 @@ function CreditNoteForm({
|
|||||||
<CreditNoteExchangeRateSync />
|
<CreditNoteExchangeRateSync />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
export default compose(
|
export default compose(
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { Row, Col, Paper, Stack } from '@/components';
|
||||||
import { Row, Col, Paper } from '@/components';
|
|
||||||
import { CreditNoteFormFooterLeft } from './CreditNoteFormFooterLeft';
|
import { CreditNoteFormFooterLeft } from './CreditNoteFormFooterLeft';
|
||||||
import { CreditNoteFormFooterRight } from './CreditNoteFormFooterRight';
|
import { CreditNoteFormFooterRight } from './CreditNoteFormFooterRight';
|
||||||
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
||||||
@@ -14,8 +11,8 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
*/
|
*/
|
||||||
export default function CreditNoteFormFooter() {
|
export default function CreditNoteFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<Stack mt={'20px'} px={'32px'} pb={'20px'} flex={1}>
|
||||||
<CreditNoteFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<CreditNoteFormFooterLeft />
|
<CreditNoteFormFooterLeft />
|
||||||
@@ -26,10 +23,7 @@ export default function CreditNoteFormFooter() {
|
|||||||
<CreditNoteFormFooterRight />
|
<CreditNoteFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</CreditNoteFooterPaper>
|
</Paper>
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const CreditNoteFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import CreditNoteFormHeaderFields from './CreditNoteFormHeaderFields';
|
import CreditNoteFormHeaderFields from './CreditNoteFormHeaderFields';
|
||||||
|
|
||||||
import { getEntriesTotal } from '@/containers/Entries/utils';
|
import { getEntriesTotal } from '@/containers/Entries/utils';
|
||||||
import { PageFormBigNumber } from '@/components';
|
import { Group, PageFormBigNumber } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note header.
|
* Credit note header.
|
||||||
*/
|
*/
|
||||||
function CreditNoteFormHeader() {
|
function CreditNoteFormHeader() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<Group
|
||||||
|
position="apart"
|
||||||
|
align={'flex-start'}
|
||||||
|
display="flex"
|
||||||
|
bg="white"
|
||||||
|
p="25px 32px"
|
||||||
|
borderBottom="1px solid #d2dce2"
|
||||||
|
>
|
||||||
<CreditNoteFormHeaderFields />
|
<CreditNoteFormHeaderFields />
|
||||||
<CreditNoteFormBigNumber />
|
<CreditNoteFormBigNumber />
|
||||||
</div>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import styled from 'styled-components';
|
|||||||
import { FormGroup, InputGroup, Position } from '@blueprintjs/core';
|
import { FormGroup, InputGroup, Position } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { DateInput } from '@blueprintjs/datetime';
|
||||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
import {
|
import {
|
||||||
@@ -14,26 +16,41 @@ import {
|
|||||||
CustomerDrawerLink,
|
CustomerDrawerLink,
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
CustomersSelect,
|
CustomersSelect,
|
||||||
|
Stack,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { customerNameFieldShouldUpdate } from './utils';
|
import { customerNameFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
||||||
import { CreditNoteExchangeRateInputField } from './components';
|
import { CreditNoteExchangeRateInputField } from './components';
|
||||||
import { CreditNoteTransactionNoField } from './CreditNoteTransactionNoField';
|
import { CreditNoteTransactionNoField } from './CreditNoteTransactionNoField';
|
||||||
import {
|
|
||||||
momentFormatter,
|
|
||||||
tansformDateValue,
|
|
||||||
inputIntent,
|
|
||||||
handleDateChange,
|
|
||||||
} from '@/utils';
|
|
||||||
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
||||||
|
|
||||||
|
const getCreditNoteFieldsStyle = (theme: Theme) => css`
|
||||||
|
.${theme.bpPrefix}-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.${theme.bpPrefix}-inline {
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-label {
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-form-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note form header fields.
|
* Credit note form header fields.
|
||||||
*/
|
*/
|
||||||
export default function CreditNoteFormHeaderFields({}) {
|
export default function CreditNoteFormHeaderFields() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const styleClassName = getCreditNoteFieldsStyle(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<Stack spacing={18} flex={1} className={styleClassName}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<CreditNoteCustomersSelect />
|
<CreditNoteCustomersSelect />
|
||||||
|
|
||||||
@@ -41,48 +58,35 @@ export default function CreditNoteFormHeaderFields({}) {
|
|||||||
<CreditNoteExchangeRateInputField />
|
<CreditNoteExchangeRateInputField />
|
||||||
|
|
||||||
{/* ----------- Credit note date ----------- */}
|
{/* ----------- Credit note date ----------- */}
|
||||||
<FastField name={'credit_note_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'credit_note_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'credit_note.label_credit_note_date'} />}
|
label={<T id={'credit_note.label_credit_note_date'} />}
|
||||||
inline={true}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
className={classNames('form-group--credit_note_date', CLASSES.FILL)}
|
inline
|
||||||
intent={inputIntent({ error, touched })}
|
fastField
|
||||||
helperText={<ErrorMessage name="credit_note_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'credit_note_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('credit_note_date', formattedDate);
|
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
||||||
inputProps={{
|
inputProps={{
|
||||||
leftIcon: <Icon icon={'date-range'} />,
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true
|
||||||
}}
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
{/* ----------- Credit note # ----------- */}
|
{/* ----------- Credit note # ----------- */}
|
||||||
<CreditNoteTransactionNoField />
|
<CreditNoteTransactionNoField />
|
||||||
|
|
||||||
{/* ----------- Reference ----------- */}
|
{/* ----------- Reference ----------- */}
|
||||||
<FastField name={'reference_no'}>
|
<FormGroup label={<T id={'reference_no'} />} name={'reference_no'} inline>
|
||||||
{({ field, meta: { error, touched } }) => (
|
<InputGroup name={'reference_no'} minimal />
|
||||||
<FormGroup
|
|
||||||
label={<T id={'reference_no'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="reference_no" />}
|
|
||||||
>
|
|
||||||
<InputGroup minimal={true} {...field} />
|
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
</Stack>
|
||||||
</FastField>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import '@/style/pages/CreditNote/PageForm.scss';
|
|
||||||
|
|
||||||
import CreditNoteForm from './CreditNoteForm';
|
import CreditNoteForm from './CreditNoteForm';
|
||||||
import { CreditNoteFormProvider } from './CreditNoteFormProvider';
|
import {
|
||||||
|
CreditNoteFormProvider,
|
||||||
|
useCreditNoteFormContext,
|
||||||
|
} from './CreditNoteFormProvider';
|
||||||
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
||||||
|
import { DashboardInsider } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note form page.
|
* Credit note form page.
|
||||||
@@ -18,8 +20,24 @@ export default function CreditNoteFormPage() {
|
|||||||
return (
|
return (
|
||||||
<CreditNoteFormProvider creditNoteId={idAsInteger}>
|
<CreditNoteFormProvider creditNoteId={idAsInteger}>
|
||||||
<AutoExchangeRateProvider>
|
<AutoExchangeRateProvider>
|
||||||
<CreditNoteForm />
|
<CreditNoteFormPageContent />
|
||||||
</AutoExchangeRateProvider>
|
</AutoExchangeRateProvider>
|
||||||
</CreditNoteFormProvider>
|
</CreditNoteFormProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CreditNoteFormPageContent() {
|
||||||
|
const { isBootLoading } = useCreditNoteFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={isBootLoading}
|
||||||
|
className={css`
|
||||||
|
min-height: calc(100vh - var(--top-offset));
|
||||||
|
max-height: calc(100vh - var(--top-offset));
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<CreditNoteForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -113,6 +113,13 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
|
|||||||
})
|
})
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
const isBootLoading =
|
||||||
|
isItemsLoading ||
|
||||||
|
isCustomersLoading ||
|
||||||
|
isCreditNoteLoading ||
|
||||||
|
isInvoiceLoading ||
|
||||||
|
isBrandingTemplatesLoading;
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
items,
|
items,
|
||||||
@@ -141,20 +148,11 @@ function CreditNoteFormProvider({ creditNoteId, ...props }) {
|
|||||||
// Credit note state
|
// Credit note state
|
||||||
creditNoteState,
|
creditNoteState,
|
||||||
isCreditNoteStateLoading,
|
isCreditNoteStateLoading,
|
||||||
|
|
||||||
|
isBootLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading =
|
return <CreditNoteFormContext.Provider value={provider} {...props} />;
|
||||||
isItemsLoading ||
|
|
||||||
isCustomersLoading ||
|
|
||||||
isCreditNoteLoading ||
|
|
||||||
isInvoiceLoading ||
|
|
||||||
isBrandingTemplatesLoading;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={isLoading} name={'credit-note-form'}>
|
|
||||||
<CreditNoteFormContext.Provider value={provider} {...props} />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCreditNoteFormContext = () =>
|
const useCreditNoteFormContext = () =>
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
||||||
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
import { useCreditNoteFormContext } from './CreditNoteFormProvider';
|
||||||
import { entriesFieldShouldUpdate } from './utils';
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
|
import { Box } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Credit note items entries editor field.
|
* Credit note items entries editor field.
|
||||||
@@ -14,7 +13,7 @@ export default function CreditNoteItemsEntriesEditorField() {
|
|||||||
const { items } = useCreditNoteFormContext();
|
const { items } = useCreditNoteFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<Box p="18px 32px 0">
|
||||||
<FastField
|
<FastField
|
||||||
name={'entries'}
|
name={'entries'}
|
||||||
items={items}
|
items={items}
|
||||||
@@ -38,6 +37,6 @@ export default function CreditNoteItemsEntriesEditorField() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
import {
|
||||||
|
ElementCustomize,
|
||||||
|
ElementCustomizeContent,
|
||||||
|
} from '../../../ElementCustomize/ElementCustomize';
|
||||||
import { EstimateCustomizeGeneralField } from './EstimateCustomizeFieldsGeneral';
|
import { EstimateCustomizeGeneralField } from './EstimateCustomizeFieldsGeneral';
|
||||||
import { EstimateCustomizeContentFields } from './EstimateCustomizeFieldsContent';
|
import { EstimateCustomizeContentFields } from './EstimateCustomizeFieldsContent';
|
||||||
import { EstimatePaperTemplate } from './EstimatePaperTemplate';
|
import {
|
||||||
import { EstimateCustomizeValues } from './types';
|
EstimatePaperTemplate,
|
||||||
|
EstimatePaperTemplateProps,
|
||||||
|
} from './EstimatePaperTemplate';
|
||||||
|
import { EstimateBrandingState, EstimateCustomizeValues } from './types';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
|
||||||
export function EstimateCustomizeContent() {
|
export function EstimateCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -19,12 +27,22 @@ export function EstimateCustomizeContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrandingTemplateForm<EstimateCustomizeValues>
|
<BrandingTemplateForm<EstimateCustomizeValues, EstimateBrandingState>
|
||||||
templateId={templateId}
|
templateId={templateId}
|
||||||
defaultValues={initialValues}
|
defaultValues={initialValues}
|
||||||
onSuccess={handleSuccess}
|
onSuccess={handleSuccess}
|
||||||
resource={'SaleEstimate'}
|
resource={'SaleEstimate'}
|
||||||
>
|
>
|
||||||
|
<EstimateCustomizeFormContent />
|
||||||
|
</BrandingTemplateForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function EstimateCustomizeFormContent() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<EstimatePaperTemplateFormConnected />
|
<EstimatePaperTemplateFormConnected />
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
@@ -33,15 +51,29 @@ export function EstimateCustomizeContent() {
|
|||||||
<EstimateCustomizeGeneralField />
|
<EstimateCustomizeGeneralField />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
<ElementCustomize.FieldsTab
|
||||||
|
id={'content'}
|
||||||
|
label={'Content'}
|
||||||
|
tabProps={{ disabled: !isTemplateNameFilled }}
|
||||||
|
>
|
||||||
<EstimateCustomizeContentFields />
|
<EstimateCustomizeContentFields />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
</BrandingTemplateForm>
|
</ElementCustomizeContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Injects the `EstimatePaperTemplate` component props from the form and branding states.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
function EstimatePaperTemplateFormConnected() {
|
function EstimatePaperTemplateFormConnected() {
|
||||||
const { values } = useFormikContext<EstimateCustomizeValues>();
|
const { values } = useFormikContext<EstimateCustomizeValues>();
|
||||||
|
const { brandingState } = useElementCustomizeContext();
|
||||||
|
|
||||||
return <EstimatePaperTemplate {...values} />;
|
const mergedProps: EstimatePaperTemplateProps = {
|
||||||
|
...brandingState,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <EstimatePaperTemplate {...mergedProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,15 +20,11 @@ export const initialValues = {
|
|||||||
showExpirationDate: true,
|
showExpirationDate: true,
|
||||||
expirationDateLabel: 'Expiration Date',
|
expirationDateLabel: 'Expiration Date',
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName: 'Bigcapital Technology, Inc.',
|
|
||||||
|
|
||||||
// Customer address
|
// Customer address
|
||||||
showCustomerAddress: true,
|
showCustomerAddress: true,
|
||||||
|
|
||||||
// Company address
|
// Company address
|
||||||
showCompanyAddress: true,
|
showCompanyAddress: true,
|
||||||
companyAddress: '',
|
|
||||||
billedToLabel: 'Billed To',
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
import { BrandingState, BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
||||||
|
|
||||||
|
export interface EstimateBrandingState extends BrandingState {}
|
||||||
|
|
||||||
export interface EstimateCustomizeValues extends BrandingTemplateValues {
|
export interface EstimateCustomizeValues extends BrandingTemplateValues {
|
||||||
// Colors
|
// Colors
|
||||||
@@ -20,9 +22,6 @@ export interface EstimateCustomizeValues extends BrandingTemplateValues {
|
|||||||
estimateDateLabel?: string;
|
estimateDateLabel?: string;
|
||||||
showEstimateDate?: boolean;
|
showEstimateDate?: boolean;
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName?: string;
|
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
showBilledFromAddress?: boolean;
|
showBilledFromAddress?: boolean;
|
||||||
showBillingToAddress?: boolean;
|
showBillingToAddress?: boolean;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
@@ -12,21 +11,25 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
import { useEstimateFormBrandingTemplatesOptions } from './utils';
|
import { useEstimateFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
BrandingThemeFormGroup,
|
BrandingThemeFormGroup,
|
||||||
BrandingThemeSelectButton,
|
BrandingThemeSelectButton,
|
||||||
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
import { MoreIcon } from '@/icons/More';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate floating actions bar.
|
* Estimate floating actions bar.
|
||||||
*/
|
*/
|
||||||
export default function EstimateFloatingActions() {
|
export default function EstimateFloatingActions() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { openDrawer } = useDrawerActions();
|
||||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
// Estimate form context.
|
// Estimate form context.
|
||||||
@@ -78,13 +81,16 @@ export default function EstimateFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles the invoice customize button click.
|
||||||
|
const handleCustomizeBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleEstimate' });
|
||||||
|
};
|
||||||
|
|
||||||
const brandingTemplatesOptions = useEstimateFormBrandingTemplatesOptions();
|
const brandingTemplatesOptions = useEstimateFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<PageForm.FooterActions position={'apart'} spacing={10}>
|
||||||
spacing={10}
|
<Group spacing={10}>
|
||||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
|
||||||
>
|
|
||||||
{/* ----------- Save And Deliver ----------- */}
|
{/* ----------- Save And Deliver ----------- */}
|
||||||
<If condition={!estimate || !estimate?.is_delivered}>
|
<If condition={!estimate || !estimate?.is_delivered}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -200,7 +206,9 @@ export default function EstimateFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group spacing={0}>
|
||||||
{/* ----------- Branding Template Select ----------- */}
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
<BrandingThemeFormGroup
|
<BrandingThemeFormGroup
|
||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
@@ -219,6 +227,27 @@ export default function EstimateFloatingActions() {
|
|||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</BrandingThemeFormGroup>
|
</BrandingThemeFormGroup>
|
||||||
|
|
||||||
|
{/* ----------- More Select ----------- */}
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.TOP_RIGHT}
|
||||||
|
modifiers={{
|
||||||
|
offset: { offset: '0, 4' },
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Customize Templates'}
|
||||||
|
onClick={handleCustomizeBtnClick}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button minimal icon={<MoreIcon height={'14px'} width={'14px'} />} />
|
||||||
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
|
</PageForm.FooterActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
import { css } from '@emotion/css';
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CreateEstimateFormSchema,
|
CreateEstimateFormSchema,
|
||||||
@@ -36,8 +35,9 @@ import {
|
|||||||
handleErrors,
|
handleErrors,
|
||||||
resetFormState,
|
resetFormState,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
|
||||||
/**
|
/**6
|
||||||
* Estimate form.
|
* Estimate form.
|
||||||
*/
|
*/
|
||||||
function EstimateForm({
|
function EstimateForm({
|
||||||
@@ -148,13 +148,6 @@ function EstimateForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
CLASSES.PAGE_FORM,
|
|
||||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
|
||||||
CLASSES.PAGE_FORM_ESTIMATE,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={
|
validationSchema={
|
||||||
isNewMode ? CreateEstimateFormSchema : EditEstimateFormSchema
|
isNewMode ? CreateEstimateFormSchema : EditEstimateFormSchema
|
||||||
@@ -162,12 +155,26 @@ function EstimateForm({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form
|
||||||
|
className={css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<PageForm flex={1}>
|
||||||
|
<PageForm.Body>
|
||||||
<EstimtaeFormTopBar />
|
<EstimtaeFormTopBar />
|
||||||
<EstimateFormHeader />
|
<EstimateFormHeader />
|
||||||
<EstimateItemsEntriesField />
|
<EstimateItemsEntriesField />
|
||||||
<EstimateFormFooter />
|
<EstimateFormFooter />
|
||||||
|
</PageForm.Body>
|
||||||
|
|
||||||
|
<PageForm.Footer>
|
||||||
<EstimateFloatingActions />
|
<EstimateFloatingActions />
|
||||||
|
</PageForm.Footer>
|
||||||
|
</PageForm>
|
||||||
|
|
||||||
{/*------- Dialogs -------*/}
|
{/*------- Dialogs -------*/}
|
||||||
<EstimateFormDialogs />
|
<EstimateFormDialogs />
|
||||||
@@ -177,7 +184,6 @@ function EstimateForm({
|
|||||||
<EstimateSyncAutoExRateToForm />
|
<EstimateSyncAutoExRateToForm />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import { x } from '@xstyled/emotion';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { Row, Col, Paper } from '@/components';
|
import { Row, Col, Paper } from '@/components';
|
||||||
import { EstimateFormFooterLeft } from './EstimateFormFooterLeft';
|
import { EstimateFormFooterLeft } from './EstimateFormFooterLeft';
|
||||||
import { EstimateFormFooterRight } from './EstimateFormFooterRight';
|
import { EstimateFormFooterRight } from './EstimateFormFooterRight';
|
||||||
@@ -14,8 +12,8 @@ import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmen
|
|||||||
*/
|
*/
|
||||||
export default function EstiamteFormFooter() {
|
export default function EstiamteFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<x.div mt={'20px'} px={'32px'} pb={'20px'} flex={1}>
|
||||||
<EstimateFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<EstimateFormFooterLeft />
|
<EstimateFormFooterLeft />
|
||||||
@@ -26,11 +24,7 @@ export default function EstiamteFormFooter() {
|
|||||||
<EstimateFormFooterRight />
|
<EstimateFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</EstimateFooterPaper>
|
</Paper>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const EstimateFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,22 +1,26 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { x } from '@xstyled/emotion';
|
||||||
|
|
||||||
import EstimateFormHeaderFields from './EstimateFormHeaderFields';
|
import EstimateFormHeaderFields from './EstimateFormHeaderFields';
|
||||||
|
|
||||||
import { getEntriesTotal } from '@/containers/Entries/utils';
|
import { getEntriesTotal } from '@/containers/Entries/utils';
|
||||||
import { PageFormBigNumber } from '@/components';
|
import { Group, PageFormBigNumber } from '@/components';
|
||||||
|
|
||||||
// Estimate form top header.
|
// Estimate form top header.
|
||||||
function EstimateFormHeader() {
|
function EstimateFormHeader() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<Group
|
||||||
|
position="apart"
|
||||||
|
align={'flex-start'}
|
||||||
|
bg="white"
|
||||||
|
p="25px 32px"
|
||||||
|
borderBottom="1px solid #d2dce2"
|
||||||
|
>
|
||||||
<EstimateFormHeaderFields />
|
<EstimateFormHeaderFields />
|
||||||
<EstimateFormBigTotal />
|
<EstimateFormBigTotal />
|
||||||
</div>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
|
import { Position, Classes } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { useFormikContext } from 'formik';
|
||||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FeatureCan,
|
FeatureCan,
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
@@ -13,15 +12,11 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
CustomerDrawerLink,
|
CustomerDrawerLink,
|
||||||
CustomersSelect,
|
CustomersSelect,
|
||||||
|
FInputGroup,
|
||||||
|
Stack,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import {
|
|
||||||
momentFormatter,
|
|
||||||
tansformDateValue,
|
|
||||||
inputIntent,
|
|
||||||
handleDateChange,
|
|
||||||
} from '@/utils';
|
|
||||||
import { customersFieldShouldUpdate } from './utils';
|
import { customersFieldShouldUpdate } from './utils';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { ProjectsSelect } from '@/containers/Projects/components';
|
import { ProjectsSelect } from '@/containers/Projects/components';
|
||||||
import {
|
import {
|
||||||
@@ -31,15 +26,36 @@ import {
|
|||||||
import { EstimateFormEstimateNumberField } from './EstimateFormEstimateNumberField';
|
import { EstimateFormEstimateNumberField } from './EstimateFormEstimateNumberField';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
||||||
|
import { useTheme } from '@emotion/react';
|
||||||
|
import { Theme } from '@xstyled/emotion';
|
||||||
|
|
||||||
|
const getEstimateFieldsStyle = (theme: Theme) => css`
|
||||||
|
.${theme.bpPrefix}-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.${theme.bpPrefix}-inline {
|
||||||
|
max-width: 470px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-label {
|
||||||
|
min-width: 160px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-form-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate form header.
|
* Estimate form header.
|
||||||
*/
|
*/
|
||||||
export default function EstimateFormHeader() {
|
export default function EstimateFormHeader() {
|
||||||
|
const theme = useTheme();
|
||||||
const { projects } = useEstimateFormContext();
|
const { projects } = useEstimateFormContext();
|
||||||
|
const styleClassName = getEstimateFieldsStyle(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<Stack spacing={18} flex={1} className={styleClassName}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<EstimateFormCustomerSelect />
|
<EstimateFormCustomerSelect />
|
||||||
|
|
||||||
@@ -47,78 +63,55 @@ export default function EstimateFormHeader() {
|
|||||||
<EstimateExchangeRateInputField />
|
<EstimateExchangeRateInputField />
|
||||||
|
|
||||||
{/* ----------- Estimate Date ----------- */}
|
{/* ----------- Estimate Date ----------- */}
|
||||||
<FastField name={'estimate_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'estimate_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'estimate_date'} />}
|
label={<T id={'estimate_date'} />}
|
||||||
inline={true}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
className={classNames(CLASSES.FILL, 'form-group--estimate-date')}
|
inline
|
||||||
intent={inputIntent({ error, touched })}
|
fastField
|
||||||
helperText={<ErrorMessage name="estimate_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'estimate_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('estimate_date', formattedDate);
|
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
inputProps={{
|
inputProps={{
|
||||||
leftIcon: <Icon icon={'date-range'} />,
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/* ----------- Expiration date ----------- */}
|
{/* ----------- Expiration date ----------- */}
|
||||||
<FastField name={'expiration_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'expiration_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'expiration_date'} />}
|
label={<T id={'expiration_date'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
inline
|
||||||
inline={true}
|
fastField
|
||||||
className={classNames(
|
|
||||||
CLASSES.FORM_GROUP_LIST_SELECT,
|
|
||||||
CLASSES.FILL,
|
|
||||||
'form-group--expiration-date',
|
|
||||||
)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="expiration_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'expiration_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('expiration_date', formattedDate);
|
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
inputProps={{
|
inputProps={{
|
||||||
leftIcon: <Icon icon={'date-range'} />,
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/* ----------- Estimate number ----------- */}
|
{/* ----------- Estimate number ----------- */}
|
||||||
<EstimateFormEstimateNumberField />
|
<EstimateFormEstimateNumberField />
|
||||||
|
|
||||||
{/* ----------- Reference ----------- */}
|
{/* ----------- Reference ----------- */}
|
||||||
<FastField name={'reference'}>
|
<FFormGroup name={'reference'} label={<T id={'reference'} />} inline fill>
|
||||||
{({ form, field, meta: { error, touched } }) => (
|
<FInputGroup name={'reference'} minimal={true} />
|
||||||
<FormGroup
|
</FFormGroup>
|
||||||
label={<T id={'reference'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="reference" />}
|
|
||||||
>
|
|
||||||
<InputGroup minimal={true} {...field} />
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Project name -----------*/}
|
{/*------------ Project name -----------*/}
|
||||||
<FeatureCan feature={Features.Projects}>
|
<FeatureCan feature={Features.Projects}>
|
||||||
@@ -136,7 +129,7 @@ export default function EstimateFormHeader() {
|
|||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,15 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import '@/style/pages/SaleEstimate/PageForm.scss';
|
|
||||||
|
|
||||||
import EstimateForm from './EstimateForm';
|
import EstimateForm from './EstimateForm';
|
||||||
import { EstimateFormProvider } from './EstimateFormProvider';
|
import {
|
||||||
|
EstimateFormProvider,
|
||||||
|
useEstimateFormContext,
|
||||||
|
} from './EstimateFormProvider';
|
||||||
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
||||||
|
import { DashboardInsider } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Estimate form page.
|
* Estimate form page.
|
||||||
@@ -18,8 +21,24 @@ export default function EstimateFormPage() {
|
|||||||
return (
|
return (
|
||||||
<EstimateFormProvider estimateId={idInteger}>
|
<EstimateFormProvider estimateId={idInteger}>
|
||||||
<AutoExchangeRateProvider>
|
<AutoExchangeRateProvider>
|
||||||
<EstimateForm />
|
<EstimateFormPageContent />
|
||||||
</AutoExchangeRateProvider>
|
</AutoExchangeRateProvider>
|
||||||
</EstimateFormProvider>
|
</EstimateFormProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function EstimateFormPageContent() {
|
||||||
|
const { isBootLoading } = useEstimateFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={isBootLoading}
|
||||||
|
className={css`
|
||||||
|
min-height: calc(100vh - var(--top-offset));
|
||||||
|
max-height: calc(100vh - var(--top-offset));
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<EstimateForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -103,6 +103,13 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
|
|||||||
const isFeatureLoading =
|
const isFeatureLoading =
|
||||||
isWarehouesLoading || isBranchesLoading || isProjectsLoading;
|
isWarehouesLoading || isBranchesLoading || isProjectsLoading;
|
||||||
|
|
||||||
|
const isBootLoading =
|
||||||
|
isCustomersLoading ||
|
||||||
|
isItemsLoading ||
|
||||||
|
isEstimateLoading ||
|
||||||
|
isBrandingTemplatesLoading ||
|
||||||
|
isSaleEstimateStateLoading;
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
estimateId,
|
estimateId,
|
||||||
@@ -136,20 +143,11 @@ function EstimateFormProvider({ query, estimateId, ...props }) {
|
|||||||
// Estimate state
|
// Estimate state
|
||||||
saleEstimateState,
|
saleEstimateState,
|
||||||
isSaleEstimateStateLoading,
|
isSaleEstimateStateLoading,
|
||||||
|
|
||||||
|
isBootLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading =
|
return <EstimateFormContext.Provider value={provider} {...props} />;
|
||||||
isCustomersLoading ||
|
|
||||||
isItemsLoading ||
|
|
||||||
isEstimateLoading ||
|
|
||||||
isBrandingTemplatesLoading ||
|
|
||||||
isSaleEstimateStateLoading;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={isLoading} name={'estimate-form'}>
|
|
||||||
<EstimateFormContext.Provider value={provider} {...props} />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useEstimateFormContext = () =>
|
const useEstimateFormContext = () =>
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import { x } from '@xstyled/emotion';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
||||||
import { useEstimateFormContext } from './EstimateFormProvider';
|
import { useEstimateFormContext } from './EstimateFormProvider';
|
||||||
import { entriesFieldShouldUpdate } from './utils';
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
@@ -14,7 +13,7 @@ export default function EstimateFormItemsEntriesField() {
|
|||||||
const { items } = useEstimateFormContext();
|
const { items } = useEstimateFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<x.div p="18px 32px 0">
|
||||||
<FastField
|
<FastField
|
||||||
name={'entries'}
|
name={'entries'}
|
||||||
items={items}
|
items={items}
|
||||||
@@ -38,6 +37,6 @@ export default function EstimateFormItemsEntriesField() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,25 @@ import {
|
|||||||
InvoicePaperTemplate,
|
InvoicePaperTemplate,
|
||||||
InvoicePaperTemplateProps,
|
InvoicePaperTemplateProps,
|
||||||
} from './InvoicePaperTemplate';
|
} from './InvoicePaperTemplate';
|
||||||
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
import {
|
||||||
|
ElementCustomize,
|
||||||
|
ElementCustomizeContent,
|
||||||
|
} from '../../../ElementCustomize/ElementCustomize';
|
||||||
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
|
import { InvoiceCustomizeGeneralField } from './InvoiceCustomizeGeneralFields';
|
||||||
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
|
import { InvoiceCustomizeContentFields } from './InvoiceCutomizeContentFields';
|
||||||
import { InvoiceCustomizeValues } from './types';
|
import { InvoiceCustomizeFormValues, InvoiceCustomizeState } from './types';
|
||||||
import { InvoiceCustomizeSchema } from './InvoiceCustomizeForm.schema';
|
import { InvoiceCustomizeSchema } from './InvoiceCustomizeForm.schema';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice branding template customize.
|
||||||
|
* @return {JSX.Element}
|
||||||
|
*/
|
||||||
export function InvoiceCustomizeContent() {
|
export function InvoiceCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
const { closeDrawer } = useDrawerActions();
|
const { closeDrawer } = useDrawerActions();
|
||||||
@@ -25,13 +34,27 @@ export function InvoiceCustomizeContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrandingTemplateForm<InvoiceCustomizeValues>
|
<BrandingTemplateForm<InvoiceCustomizeFormValues, InvoiceCustomizeState>
|
||||||
templateId={templateId}
|
templateId={templateId}
|
||||||
defaultValues={initialValues}
|
defaultValues={initialValues}
|
||||||
validationSchema={InvoiceCustomizeSchema}
|
validationSchema={InvoiceCustomizeSchema}
|
||||||
onSuccess={handleSuccess}
|
onSuccess={handleSuccess}
|
||||||
resource={'SaleInvoice'}
|
resource={'SaleInvoice'}
|
||||||
>
|
>
|
||||||
|
<InvoiceCustomizeFormContent />
|
||||||
|
</BrandingTemplateForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoice branding template customize preview and fields.
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
function InvoiceCustomizeFormContent() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<InvoicePaperTemplateFormConnected />
|
<InvoicePaperTemplateFormConnected />
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
@@ -40,22 +63,38 @@ export function InvoiceCustomizeContent() {
|
|||||||
<InvoiceCustomizeGeneralField />
|
<InvoiceCustomizeGeneralField />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
<ElementCustomize.FieldsTab
|
||||||
|
id={'content'}
|
||||||
|
label={'Content'}
|
||||||
|
tabProps={{ disabled: !isTemplateNameFilled }}
|
||||||
|
>
|
||||||
<InvoiceCustomizeContentFields />
|
<InvoiceCustomizeContentFields />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
</BrandingTemplateForm>
|
</ElementCustomizeContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const withFormikProps = <P extends object>(
|
/**
|
||||||
|
* Injects the `InvoicePaperTemplate` component props from the form and branding states.
|
||||||
|
* @param Component
|
||||||
|
* @returns {JSX.Element}
|
||||||
|
*/
|
||||||
|
const withInvoicePreviewTemplateProps = <P extends object>(
|
||||||
Component: React.ComponentType<P>,
|
Component: React.ComponentType<P>,
|
||||||
) => {
|
) => {
|
||||||
return (props: Omit<P, keyof InvoicePaperTemplateProps>) => {
|
return (props: Omit<P, keyof InvoicePaperTemplateProps>) => {
|
||||||
const { values } = useFormikContext<InvoicePaperTemplateProps>();
|
const { values } = useFormikContext<InvoiceCustomizeFormValues>();
|
||||||
|
const { brandingState } = useElementCustomizeContext();
|
||||||
|
|
||||||
return <Component {...(props as P)} {...values} />;
|
const mergedProps: InvoicePaperTemplateProps = {
|
||||||
|
...brandingState,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <Component {...(props as P)} {...mergedProps} />;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InvoicePaperTemplateFormConnected =
|
export const InvoicePaperTemplateFormConnected = R.compose(
|
||||||
R.compose(withFormikProps)(InvoicePaperTemplate);
|
withInvoicePreviewTemplateProps,
|
||||||
|
)(InvoicePaperTemplate);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
color: #111;
|
color: #111;
|
||||||
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color), 0 10px 15px rgba(0, 0, 0, 0.05);
|
box-shadow: inset 0 4px 0px 0 var(--invoice-primary-color), 0 10px 15px rgba(0, 0, 0, 0.05);
|
||||||
padding: 24px 30px;
|
padding: 30px 30px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
position: relative;
|
position: relative;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
height: 1123px;
|
height: 1123px;
|
||||||
}
|
}
|
||||||
.bigTitle{
|
.bigTitle{
|
||||||
font-size: 60px;
|
font-size: 30px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -105,7 +105,10 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
img{
|
img{
|
||||||
max-width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
max-width: 260px;
|
||||||
|
max-height: 100px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.logoImg {
|
.logoImg {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import clsx from 'classnames';
|
import clsx from 'classnames';
|
||||||
import { get } from 'lodash';
|
import { get } from 'lodash';
|
||||||
import { Box, Group, GroupProps, Stack } from '@/components';
|
import { Box, Group, GroupProps } from '@/components';
|
||||||
import styles from './InvoicePaperTemplate.module.scss';
|
import styles from './InvoicePaperTemplate.module.scss';
|
||||||
|
|
||||||
export interface PaperTemplateProps {
|
export interface PaperTemplateProps {
|
||||||
|
|||||||
@@ -16,19 +16,17 @@ export const initialValues = {
|
|||||||
showInvoiceNumber: true,
|
showInvoiceNumber: true,
|
||||||
invoiceNumberLabel: 'Invoice number',
|
invoiceNumberLabel: 'Invoice number',
|
||||||
|
|
||||||
|
// Issue date
|
||||||
showDateIssue: true,
|
showDateIssue: true,
|
||||||
dateIssueLabel: 'Date of Issue',
|
dateIssueLabel: 'Date of Issue',
|
||||||
|
|
||||||
|
// Due date.
|
||||||
showDueDate: true,
|
showDueDate: true,
|
||||||
dueDateLabel: 'Due Date',
|
dueDateLabel: 'Due Date',
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName: 'Bigcapital Technology, Inc.',
|
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
showCustomerAddress: true,
|
showCustomerAddress: true,
|
||||||
showCompanyAddress: true,
|
showCompanyAddress: true,
|
||||||
companyAddress: '',
|
|
||||||
billedToLabel: 'Billed To',
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
@@ -41,6 +39,7 @@ export const initialValues = {
|
|||||||
showSubtotal: true,
|
showSubtotal: true,
|
||||||
subtotalLabel: 'Subtotal',
|
subtotalLabel: 'Subtotal',
|
||||||
|
|
||||||
|
// Discount
|
||||||
showDiscount: true,
|
showDiscount: true,
|
||||||
discountLabel: 'Discount',
|
discountLabel: 'Discount',
|
||||||
|
|
||||||
@@ -52,6 +51,7 @@ export const initialValues = {
|
|||||||
paymentMadeLabel: 'Payment Made',
|
paymentMadeLabel: 'Payment Made',
|
||||||
showPaymentMade: true,
|
showPaymentMade: true,
|
||||||
|
|
||||||
|
// Due amount
|
||||||
dueAmountLabel: 'Due Amount',
|
dueAmountLabel: 'Due Amount',
|
||||||
showDueAmount: true,
|
showDueAmount: true,
|
||||||
|
|
||||||
@@ -59,6 +59,7 @@ export const initialValues = {
|
|||||||
termsConditionsLabel: 'Terms & Conditions',
|
termsConditionsLabel: 'Terms & Conditions',
|
||||||
showTermsConditions: true,
|
showTermsConditions: true,
|
||||||
|
|
||||||
|
// Statement
|
||||||
statementLabel: 'Statement',
|
statementLabel: 'Statement',
|
||||||
showStatement: true,
|
showStatement: true,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
|
import { BrandingState, BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
||||||
|
|
||||||
export interface InvoiceCustomizeValues extends BrandingTemplateValues {
|
export interface InvoiceCustomizeState extends BrandingState {}
|
||||||
|
|
||||||
|
export interface InvoiceCustomizeFormValues extends BrandingTemplateValues {
|
||||||
// Colors
|
// Colors
|
||||||
primaryColor?: string;
|
primaryColor?: string;
|
||||||
secondaryColor?: string;
|
secondaryColor?: string;
|
||||||
@@ -14,15 +16,14 @@ export interface InvoiceCustomizeValues extends BrandingTemplateValues {
|
|||||||
showInvoiceNumber?: boolean;
|
showInvoiceNumber?: boolean;
|
||||||
invoiceNumberLabel?: string;
|
invoiceNumberLabel?: string;
|
||||||
|
|
||||||
|
// Date issue
|
||||||
showDateIssue?: boolean;
|
showDateIssue?: boolean;
|
||||||
dateIssueLabel?: string;
|
dateIssueLabel?: string;
|
||||||
|
|
||||||
|
// Due date
|
||||||
showDueDate?: boolean;
|
showDueDate?: boolean;
|
||||||
dueDateLabel?: string;
|
dueDateLabel?: string;
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName?: string;
|
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
showBilledFromAddress?: boolean;
|
showBilledFromAddress?: boolean;
|
||||||
showBillingToAddress?: boolean;
|
showBillingToAddress?: boolean;
|
||||||
@@ -38,6 +39,7 @@ export interface InvoiceCustomizeValues extends BrandingTemplateValues {
|
|||||||
showSubtotal?: boolean;
|
showSubtotal?: boolean;
|
||||||
subtotalLabel?: string;
|
subtotalLabel?: string;
|
||||||
|
|
||||||
|
// Discount
|
||||||
showDiscount?: boolean;
|
showDiscount?: boolean;
|
||||||
discountLabel?: string;
|
discountLabel?: string;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { omit } from 'lodash';
|
import { omit } from 'lodash';
|
||||||
import { useFormikContext } from 'formik';
|
import { InvoiceCustomizeFormValues } from './types';
|
||||||
import { InvoiceCustomizeValues } from './types';
|
|
||||||
import {
|
import {
|
||||||
CreatePdfTemplateValues,
|
CreatePdfTemplateValues,
|
||||||
EditPdfTemplateValues,
|
EditPdfTemplateValues,
|
||||||
@@ -10,7 +9,7 @@ import { initialValues } from './constants';
|
|||||||
import { useBrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
|
import { useBrandingTemplateBoot } from '@/containers/BrandingTemplates/BrandingTemplateBoot';
|
||||||
|
|
||||||
export const transformToEditRequest = (
|
export const transformToEditRequest = (
|
||||||
values: InvoiceCustomizeValues,
|
values: InvoiceCustomizeFormValues,
|
||||||
): EditPdfTemplateValues => {
|
): EditPdfTemplateValues => {
|
||||||
return {
|
return {
|
||||||
templateName: values.templateName,
|
templateName: values.templateName,
|
||||||
@@ -19,7 +18,7 @@ export const transformToEditRequest = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const transformToNewRequest = (
|
export const transformToNewRequest = (
|
||||||
values: InvoiceCustomizeValues,
|
values: InvoiceCustomizeFormValues,
|
||||||
): CreatePdfTemplateValues => {
|
): CreatePdfTemplateValues => {
|
||||||
return {
|
return {
|
||||||
resource: 'SaleInvoice',
|
resource: 'SaleInvoice',
|
||||||
@@ -28,7 +27,7 @@ export const transformToNewRequest = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeValues => {
|
export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeFormValues => {
|
||||||
const { pdfTemplate } = useBrandingTemplateBoot();
|
const { pdfTemplate } = useBrandingTemplateBoot();
|
||||||
|
|
||||||
const defaultPdfTemplate = {
|
const defaultPdfTemplate = {
|
||||||
@@ -40,6 +39,6 @@ export const useInvoiceCustomizeInitialValues = (): InvoiceCustomizeValues => {
|
|||||||
...(transformToForm(
|
...(transformToForm(
|
||||||
defaultPdfTemplate,
|
defaultPdfTemplate,
|
||||||
initialValues,
|
initialValues,
|
||||||
) as InvoiceCustomizeValues),
|
) as InvoiceCustomizeFormValues),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
@@ -11,22 +11,25 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
import { If, Icon, FormattedMessage as T, Group, FSelect } from '@/components';
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
import { useInvoiceFormBrandingTemplatesOptions } from './utils';
|
import { useInvoiceFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
BrandingThemeFormGroup,
|
BrandingThemeFormGroup,
|
||||||
BrandingThemeSelectButton,
|
BrandingThemeSelectButton,
|
||||||
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
import { MoreIcon } from '@/icons/More';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice floating actions bar.
|
* Invoice floating actions bar.
|
||||||
*/
|
*/
|
||||||
export default function InvoiceFloatingActions() {
|
export default function InvoiceFloatingActions() {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
const { openDrawer } = useDrawerActions();
|
||||||
|
|
||||||
// Formik context.
|
// Formik context.
|
||||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
@@ -80,13 +83,16 @@ export default function InvoiceFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles the invoice customize button click.
|
||||||
|
const handleCustomizeBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleInvoice' });
|
||||||
|
};
|
||||||
|
|
||||||
const brandingTemplatesOptions = useInvoiceFormBrandingTemplatesOptions();
|
const brandingTemplatesOptions = useInvoiceFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<PageForm.FooterActions spacing={10} position={'apart'}>
|
||||||
spacing={10}
|
<Group spacing={10}>
|
||||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
|
||||||
>
|
|
||||||
{/* ----------- Save And Deliver ----------- */}
|
{/* ----------- Save And Deliver ----------- */}
|
||||||
<If condition={!invoice || !invoice?.is_delivered}>
|
<If condition={!invoice || !invoice?.is_delivered}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -121,6 +127,7 @@ export default function InvoiceFloatingActions() {
|
|||||||
/>
|
/>
|
||||||
</Popover>
|
</Popover>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
{/* ----------- Save As Draft ----------- */}
|
{/* ----------- Save As Draft ----------- */}
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
@@ -153,6 +160,7 @@ export default function InvoiceFloatingActions() {
|
|||||||
</Popover>
|
</Popover>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
{/* ----------- Save and New ----------- */}
|
{/* ----------- Save and New ----------- */}
|
||||||
<If condition={invoice && invoice?.is_delivered}>
|
<If condition={invoice && invoice?.is_delivered}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -199,7 +207,9 @@ export default function InvoiceFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group spacing={0}>
|
||||||
{/* ----------- Branding Template Select ----------- */}
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
<BrandingThemeFormGroup
|
<BrandingThemeFormGroup
|
||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
@@ -212,13 +222,32 @@ export default function InvoiceFloatingActions() {
|
|||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
items={brandingTemplatesOptions}
|
items={brandingTemplatesOptions}
|
||||||
input={({ activeItem, text, label, value }) => (
|
input={({ activeItem, text, label, value }) => (
|
||||||
<BrandingThemeSelectButton text={text || 'Brand Theme'} minimal />
|
<BrandingThemeSelectButton text={text || 'Brand Theme'} />
|
||||||
)}
|
)}
|
||||||
filterable={false}
|
filterable={false}
|
||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</BrandingThemeFormGroup>
|
</BrandingThemeFormGroup>
|
||||||
|
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.TOP_RIGHT}
|
||||||
|
modifiers={{
|
||||||
|
offset: { offset: '0, 4' },
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Customize Templates'}
|
||||||
|
onClick={handleCustomizeBtnClick}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button minimal icon={<MoreIcon height={'14px'} width={'14px'} />} />
|
||||||
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
|
</PageForm.FooterActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
import { sumBy, isEmpty, defaultTo } from 'lodash';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { css } from '@emotion/css';
|
||||||
import {
|
import {
|
||||||
getCreateInvoiceFormSchema,
|
getCreateInvoiceFormSchema,
|
||||||
getEditInvoiceFormSchema,
|
getEditInvoiceFormSchema,
|
||||||
@@ -23,7 +22,7 @@ import withDashboardActions from '@/containers/Dashboard/withDashboardActions';
|
|||||||
import withSettings from '@/containers/Settings/withSettings';
|
import withSettings from '@/containers/Settings/withSettings';
|
||||||
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
import withCurrentOrganization from '@/containers/Organization/withCurrentOrganization';
|
||||||
|
|
||||||
import { AppToaster } from '@/components';
|
import { AppToaster, Box } from '@/components';
|
||||||
import { compose, orderingLinesIndexes, transactionNumber } from '@/utils';
|
import { compose, orderingLinesIndexes, transactionNumber } from '@/utils';
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
import { InvoiceFormActions } from './InvoiceFormActions';
|
import { InvoiceFormActions } from './InvoiceFormActions';
|
||||||
@@ -34,12 +33,16 @@ import {
|
|||||||
transformValueToRequest,
|
transformValueToRequest,
|
||||||
resetFormState,
|
resetFormState,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { InvoiceExchangeRateSync, InvoiceNoSyncSettingsToForm } from './components';
|
import {
|
||||||
|
InvoiceExchangeRateSync,
|
||||||
|
InvoiceNoSyncSettingsToForm,
|
||||||
|
} from './components';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice form.
|
* Invoice form.
|
||||||
*/
|
*/
|
||||||
function InvoiceForm({
|
function InvoiceFormRoot({
|
||||||
// #withSettings
|
// #withSettings
|
||||||
invoiceNextNumber,
|
invoiceNextNumber,
|
||||||
invoiceNumberPrefix,
|
invoiceNumberPrefix,
|
||||||
@@ -61,7 +64,7 @@ function InvoiceForm({
|
|||||||
createInvoiceMutate,
|
createInvoiceMutate,
|
||||||
editInvoiceMutate,
|
editInvoiceMutate,
|
||||||
submitPayload,
|
submitPayload,
|
||||||
saleInvoiceState
|
saleInvoiceState,
|
||||||
} = useInvoiceFormContext();
|
} = useInvoiceFormContext();
|
||||||
|
|
||||||
// Invoice number.
|
// Invoice number.
|
||||||
@@ -156,13 +159,6 @@ function InvoiceForm({
|
|||||||
const EditInvoiceFormSchema = getEditInvoiceFormSchema();
|
const EditInvoiceFormSchema = getEditInvoiceFormSchema();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
CLASSES.PAGE_FORM,
|
|
||||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
|
||||||
CLASSES.PAGE_FORM_INVOICE,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={
|
validationSchema={
|
||||||
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
|
isNewMode ? CreateInvoiceFormSchema : EditInvoiceFormSchema
|
||||||
@@ -170,16 +166,29 @@ function InvoiceForm({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form
|
||||||
|
className={css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<PageForm flex={1}>
|
||||||
|
<PageForm.Body>
|
||||||
<InvoiceFormTopBar />
|
<InvoiceFormTopBar />
|
||||||
<InvoiceFormHeader />
|
<InvoiceFormHeader />
|
||||||
|
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<Box p="18px 32px 0">
|
||||||
<InvoiceFormActions />
|
<InvoiceFormActions />
|
||||||
<InvoiceItemsEntriesEditorField />
|
<InvoiceItemsEntriesEditorField />
|
||||||
</div>
|
</Box>
|
||||||
<InvoiceFormFooter />
|
<InvoiceFormFooter />
|
||||||
|
</PageForm.Body>
|
||||||
|
|
||||||
|
<PageForm.Footer>
|
||||||
<InvoiceFloatingActions />
|
<InvoiceFloatingActions />
|
||||||
|
</PageForm.Footer>
|
||||||
|
|
||||||
{/*---------- Dialogs ----------*/}
|
{/*---------- Dialogs ----------*/}
|
||||||
<InvoiceFormDialogs />
|
<InvoiceFormDialogs />
|
||||||
@@ -187,13 +196,13 @@ function InvoiceForm({
|
|||||||
{/*---------- Effects ----------*/}
|
{/*---------- Effects ----------*/}
|
||||||
<InvoiceNoSyncSettingsToForm />
|
<InvoiceNoSyncSettingsToForm />
|
||||||
<InvoiceExchangeRateSync />
|
<InvoiceExchangeRateSync />
|
||||||
|
</PageForm>
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export const InvoiceForm = compose(
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withSettings(({ invoiceSettings }) => ({
|
withSettings(({ invoiceSettings }) => ({
|
||||||
invoiceNextNumber: invoiceSettings?.nextNumber,
|
invoiceNextNumber: invoiceSettings?.nextNumber,
|
||||||
@@ -203,4 +212,4 @@ export default compose(
|
|||||||
invoiceTermsConditions: invoiceSettings?.termsConditions,
|
invoiceTermsConditions: invoiceSettings?.termsConditions,
|
||||||
})),
|
})),
|
||||||
withCurrentOrganization(),
|
withCurrentOrganization(),
|
||||||
)(InvoiceForm);
|
)(InvoiceFormRoot);
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import { x } from '@xstyled/emotion';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { Row, Col, Paper } from '@/components';
|
||||||
import { Paper, Row, Col } from '@/components';
|
|
||||||
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
|
import { InvoiceFormFooterLeft } from './InvoiceFormFooterLeft';
|
||||||
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
|
import { InvoiceFormFooterRight } from './InvoiceFormFooterRight';
|
||||||
import { UploadAttachmentButton } from '../../../Attachments/UploadAttachmentButton';
|
import { UploadAttachmentButton } from '../../../Attachments/UploadAttachmentButton';
|
||||||
|
|
||||||
export default function InvoiceFormFooter() {
|
export default function InvoiceFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<x.div mt={'20px'} px={'32px'} pb={'20px'} flex={1}>
|
||||||
<InvoiceFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<InvoiceFormFooterLeft />
|
<InvoiceFormFooterLeft />
|
||||||
@@ -23,11 +21,7 @@ export default function InvoiceFormFooter() {
|
|||||||
<InvoiceFormFooterRight />
|
<InvoiceFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</InvoiceFooterPaper>
|
</Paper>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const InvoiceFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { Group, PageFormBigNumber } from '@/components';
|
||||||
import InvoiceFormHeaderFields from './InvoiceFormHeaderFields';
|
import InvoiceFormHeaderFields from './InvoiceFormHeaderFields';
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { PageFormBigNumber } from '@/components';
|
|
||||||
import { useInvoiceDueAmount } from './utils';
|
import { useInvoiceDueAmount } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -15,10 +11,16 @@ import { useInvoiceDueAmount } from './utils';
|
|||||||
*/
|
*/
|
||||||
function InvoiceFormHeader() {
|
function InvoiceFormHeader() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<Group
|
||||||
|
position="apart"
|
||||||
|
align={'flex-start'}
|
||||||
|
bg="white"
|
||||||
|
p="25px 32px"
|
||||||
|
borderBottom="1px solid #d2dce2"
|
||||||
|
>
|
||||||
<InvoiceFormHeaderFields />
|
<InvoiceFormHeaderFields />
|
||||||
<InvoiceFormBigTotal />
|
<InvoiceFormBigTotal />
|
||||||
</div>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,30 +2,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
|
import { Position, Classes } from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { useFormikContext } from 'formik';
|
||||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
import { css } from '@emotion/css';
|
||||||
|
import { Theme, useTheme } from '@emotion/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
Col,
|
|
||||||
Row,
|
|
||||||
CustomerDrawerLink,
|
CustomerDrawerLink,
|
||||||
FieldRequiredHint,
|
FieldRequiredHint,
|
||||||
FeatureCan,
|
FeatureCan,
|
||||||
CustomersSelect,
|
CustomersSelect,
|
||||||
|
Stack,
|
||||||
|
FInputGroup,
|
||||||
|
Icon,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import {
|
|
||||||
momentFormatter,
|
|
||||||
tansformDateValue,
|
|
||||||
inputIntent,
|
|
||||||
handleDateChange,
|
|
||||||
} from '@/utils';
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { customerNameFieldShouldUpdate } from './utils';
|
import { customerNameFieldShouldUpdate } from './utils';
|
||||||
|
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
|
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
||||||
import {
|
import {
|
||||||
InvoiceExchangeRateInputField,
|
InvoiceExchangeRateInputField,
|
||||||
InvoiceProjectSelectButton,
|
InvoiceProjectSelectButton,
|
||||||
@@ -36,99 +33,99 @@ import {
|
|||||||
ProjectBillableEntriesLink,
|
ProjectBillableEntriesLink,
|
||||||
} from '@/containers/Projects/components';
|
} from '@/containers/Projects/components';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
|
||||||
|
const getInvoiceFieldsStyle = (theme: Theme) => css`
|
||||||
|
.${theme.bpPrefix}-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.${theme.bpPrefix}-inline {
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-label {
|
||||||
|
min-width: 150px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-form-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice form header fields.
|
* Invoice form header fields.
|
||||||
*/
|
*/
|
||||||
export default function InvoiceFormHeaderFields() {
|
export default function InvoiceFormHeaderFields() {
|
||||||
// Invoice form context.
|
const theme = useTheme();
|
||||||
const { projects } = useInvoiceFormContext();
|
const { projects } = useInvoiceFormContext();
|
||||||
const { values } = useFormikContext();
|
const { values } = useFormikContext();
|
||||||
|
const invoiceFieldsClassName = getInvoiceFieldsStyle(theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<Stack spacing={18} flex={1} className={invoiceFieldsClassName}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<InvoiceFormCustomerSelect />
|
<InvoiceFormCustomerSelect />
|
||||||
|
|
||||||
{/* ----------- Exchange rate ----------- */}
|
{/* ----------- Exchange rate ----------- */}
|
||||||
<InvoiceExchangeRateInputField />
|
<InvoiceExchangeRateInputField />
|
||||||
|
|
||||||
<Row>
|
|
||||||
<Col xs={6}>
|
|
||||||
{/* ----------- Invoice date ----------- */}
|
{/* ----------- Invoice date ----------- */}
|
||||||
<FastField name={'invoice_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'invoice_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'invoice_date'} />}
|
label={<T id={'invoice_date'} />}
|
||||||
inline={true}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
className={classNames('form-group--invoice-date', CLASSES.FILL)}
|
inline
|
||||||
intent={inputIntent({ error, touched })}
|
fastField
|
||||||
helperText={<ErrorMessage name="invoice_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'invoice_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('invoice_date', formattedDate);
|
|
||||||
})}
|
|
||||||
popoverProps={{
|
popoverProps={{
|
||||||
position: Position.BOTTOM_LEFT,
|
position: Position.BOTTOM_LEFT,
|
||||||
minimal: true,
|
minimal: true,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
inputProps={{
|
||||||
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col xs={6}>
|
|
||||||
{/* ----------- Due date ----------- */}
|
{/* ----------- Due date ----------- */}
|
||||||
<FastField name={'due_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'due_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'due_date'} />}
|
label={<T id={'due_date'} />}
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
inline={true}
|
inline
|
||||||
className={classNames('form-group--due-date', CLASSES.FILL)}
|
fastField
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="due_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'due_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('due_date', formattedDate);
|
|
||||||
})}
|
|
||||||
popoverProps={{
|
popoverProps={{
|
||||||
position: Position.BOTTOM_LEFT,
|
position: Position.BOTTOM_LEFT,
|
||||||
minimal: true,
|
minimal: true,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
inputProps={{
|
||||||
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true,
|
||||||
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
|
|
||||||
{/* ----------- Invoice number ----------- */}
|
{/* ----------- Invoice number ----------- */}
|
||||||
<InvoiceFormInvoiceNumberField />
|
<InvoiceFormInvoiceNumberField />
|
||||||
|
|
||||||
{/* ----------- Reference ----------- */}
|
{/* ----------- Reference ----------- */}
|
||||||
<FastField name={'reference_no'}>
|
<FFormGroup name={'reference_no'} label={<T id={'reference'} />} inline>
|
||||||
{({ field, meta: { error, touched } }) => (
|
<FInputGroup name={'reference_no'} minimal={true} />
|
||||||
<FormGroup
|
</FFormGroup>
|
||||||
label={<T id={'reference'} />}
|
|
||||||
inline={true}
|
|
||||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="reference_no" />}
|
|
||||||
>
|
|
||||||
<InputGroup minimal={true} {...field} />
|
|
||||||
</FormGroup>
|
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Project name -----------*/}
|
{/*------------ Project name -----------*/}
|
||||||
<FeatureCan feature={Features.Projects}>
|
<FeatureCan feature={Features.Projects}>
|
||||||
@@ -151,7 +148,7 @@ export default function InvoiceFormHeaderFields() {
|
|||||||
)}
|
)}
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,43 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import '@/style/pages/SaleInvoice/PageForm.scss';
|
import { InvoiceForm } from './InvoiceForm';
|
||||||
|
import {
|
||||||
import InvoiceForm from './InvoiceForm';
|
InvoiceFormProvider,
|
||||||
import { InvoiceFormProvider } from './InvoiceFormProvider';
|
useInvoiceFormContext,
|
||||||
|
} from './InvoiceFormProvider';
|
||||||
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
||||||
|
import { DashboardInsider } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoice form page.
|
* Invoice form page.
|
||||||
*/
|
*/
|
||||||
export default function InvoiceFormPage() {
|
export default function InvoiceFormPage() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const idAsInteger = parseInt(id, 10);
|
const invoiceId = parseInt(id, 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<InvoiceFormProvider invoiceId={idAsInteger}>
|
<InvoiceFormProvider invoiceId={invoiceId}>
|
||||||
<AutoExchangeRateProvider>
|
<AutoExchangeRateProvider>
|
||||||
<InvoiceForm />
|
<InvoiceFormPageContent />
|
||||||
</AutoExchangeRateProvider>
|
</AutoExchangeRateProvider>
|
||||||
</InvoiceFormProvider>
|
</InvoiceFormProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function InvoiceFormPageContent() {
|
||||||
|
const { isBootLoading } = useInvoiceFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={isBootLoading}
|
||||||
|
className={css`
|
||||||
|
min-height: calc(100vh - var(--top-offset));
|
||||||
|
max-height: calc(100vh - var(--top-offset));
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<InvoiceForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) {
|
|||||||
isProjectsLoading ||
|
isProjectsLoading ||
|
||||||
isBrandingTemplatesLoading;
|
isBrandingTemplatesLoading;
|
||||||
|
|
||||||
|
const isBootLoading =
|
||||||
|
isInvoiceLoading ||
|
||||||
|
isItemsLoading ||
|
||||||
|
isCustomersLoading ||
|
||||||
|
isEstimateLoading ||
|
||||||
|
isSettingsLoading ||
|
||||||
|
isInvoiceStateLoading;
|
||||||
|
|
||||||
const provider = {
|
const provider = {
|
||||||
invoice,
|
invoice,
|
||||||
items,
|
items,
|
||||||
@@ -170,21 +178,11 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) {
|
|||||||
// Invoice state
|
// Invoice state
|
||||||
saleInvoiceState,
|
saleInvoiceState,
|
||||||
isInvoiceStateLoading,
|
isInvoiceStateLoading,
|
||||||
|
|
||||||
|
isBootLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading =
|
return <InvoiceFormContext.Provider value={provider} {...props} />;
|
||||||
isInvoiceLoading ||
|
|
||||||
isItemsLoading ||
|
|
||||||
isCustomersLoading ||
|
|
||||||
isEstimateLoading ||
|
|
||||||
isSettingsLoading ||
|
|
||||||
isInvoiceStateLoading;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={isLoading} name={'invoice-form'}>
|
|
||||||
<InvoiceFormContext.Provider value={provider} {...props} />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useInvoiceFormContext = () =>
|
const useInvoiceFormContext = () =>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
|
import { x } from '@xstyled/emotion';
|
||||||
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
import ItemsEntriesTable from '@/containers/Entries/ItemsEntriesTable';
|
||||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||||
import { entriesFieldShouldUpdate } from './utils';
|
import { entriesFieldShouldUpdate } from './utils';
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
@@ -12,15 +11,18 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { FSelect, Group, Icon, FormattedMessage as T } from '@/components';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { FSelect, Group, Icon, FormattedMessage as T } from '@/components';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
import { usePaymentReceiveFormContext } from './PaymentReceiveFormProvider';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import {
|
import {
|
||||||
BrandingThemeFormGroup,
|
BrandingThemeFormGroup,
|
||||||
BrandingThemeSelectButton,
|
BrandingThemeSelectButton,
|
||||||
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
import { usePaymentReceivedFormBrandingTemplatesOptions } from './utils';
|
import { usePaymentReceivedFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
import { MoreIcon } from '@/icons/More';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive floating actions bar.
|
* Payment receive floating actions bar.
|
||||||
@@ -35,6 +37,8 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
// History context.
|
// History context.
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { openDrawer } = useDrawerActions();
|
||||||
|
|
||||||
// Handle submit button click.
|
// Handle submit button click.
|
||||||
const handleSubmitBtnClick = (event) => {
|
const handleSubmitBtnClick = (event) => {
|
||||||
setSubmitPayload({ redirect: true });
|
setSubmitPayload({ redirect: true });
|
||||||
@@ -58,14 +62,17 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
submitForm();
|
submitForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles the invoice customize button click.
|
||||||
|
const handleCustomizeBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'PaymentReceive' });
|
||||||
|
};
|
||||||
|
|
||||||
const brandingTemplatesOpts =
|
const brandingTemplatesOpts =
|
||||||
usePaymentReceivedFormBrandingTemplatesOptions();
|
usePaymentReceivedFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<PageForm.FooterActions position={'apart'} spacing={20}>
|
||||||
spacing={10}
|
<Group spacing={10}>
|
||||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
|
||||||
>
|
|
||||||
{/* ----------- Save and New ----------- */}
|
{/* ----------- Save and New ----------- */}
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
<Button
|
<Button
|
||||||
@@ -109,7 +116,6 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
onClick={handleClearBtnClick}
|
onClick={handleClearBtnClick}
|
||||||
text={!isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
text={!isNewMode ? <T id={'reset'} /> : <T id={'clear'} />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ----------- Cancel ----------- */}
|
{/* ----------- Cancel ----------- */}
|
||||||
<Button
|
<Button
|
||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
@@ -117,7 +123,9 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group spacing={0}>
|
||||||
{/* ----------- Branding Template Select ----------- */}
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
<BrandingThemeFormGroup
|
<BrandingThemeFormGroup
|
||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
@@ -136,6 +144,27 @@ export default function PaymentReceiveFormFloatingActions() {
|
|||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</BrandingThemeFormGroup>
|
</BrandingThemeFormGroup>
|
||||||
|
|
||||||
|
{/* ----------- Setting Select ----------- */}
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.TOP_RIGHT}
|
||||||
|
modifiers={{
|
||||||
|
offset: { offset: '0, 4' },
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Customize Templates'}
|
||||||
|
onClick={handleCustomizeBtnClick}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button minimal icon={<MoreIcon height={'14px'} width={'14px'} />} />
|
||||||
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
|
</PageForm.FooterActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import { isEmpty, defaultTo } from 'lodash';
|
import { isEmpty, defaultTo } from 'lodash';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import '@/style/pages/PaymentReceive/PageForm.scss';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
import PaymentReceiveHeader from './PaymentReceiveFormHeader';
|
||||||
import PaymentReceiveFormBody from './PaymentReceiveFormBody';
|
import PaymentReceiveFormBody from './PaymentReceiveFormBody';
|
||||||
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
|
import PaymentReceiveFloatingActions from './PaymentReceiveFloatingActions';
|
||||||
@@ -40,11 +37,12 @@ import {
|
|||||||
getExceededAmountFromValues,
|
getExceededAmountFromValues,
|
||||||
} from './utils';
|
} from './utils';
|
||||||
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
|
import { PaymentReceiveSyncIncrementSettingsToForm } from './components';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment Receive form.
|
* Payment Receive form.
|
||||||
*/
|
*/
|
||||||
function PaymentReceiveForm({
|
function PaymentReceiveFormRoot({
|
||||||
// #withSettings
|
// #withSettings
|
||||||
preferredDepositAccount,
|
preferredDepositAccount,
|
||||||
paymentReceiveNextNumber,
|
paymentReceiveNextNumber,
|
||||||
@@ -159,14 +157,8 @@ function PaymentReceiveForm({
|
|||||||
return createPaymentReceiveMutate(form).then(onSaved).catch(onError);
|
return createPaymentReceiveMutate(form).then(onSaved).catch(onError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
CLASSES.PAGE_FORM,
|
|
||||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
|
||||||
CLASSES.PAGE_FORM_PAYMENT_RECEIVE,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleSubmitForm}
|
onSubmit={handleSubmitForm}
|
||||||
@@ -176,13 +168,26 @@ function PaymentReceiveForm({
|
|||||||
: EditPaymentReceiveFormSchema
|
: EditPaymentReceiveFormSchema
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form
|
||||||
|
className={css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<PageForm flex={1}>
|
||||||
<PaymentReceiveInnerProvider>
|
<PaymentReceiveInnerProvider>
|
||||||
|
<PageForm.Body>
|
||||||
<PaymentReceiveFormTopBar />
|
<PaymentReceiveFormTopBar />
|
||||||
<PaymentReceiveHeader />
|
<PaymentReceiveHeader />
|
||||||
<PaymentReceiveFormBody />
|
<PaymentReceiveFormBody />
|
||||||
<PaymentReceiveFormFooter />
|
<PaymentReceiveFormFooter />
|
||||||
|
</PageForm.Body>
|
||||||
|
|
||||||
|
<PageForm.Footer>
|
||||||
<PaymentReceiveFloatingActions />
|
<PaymentReceiveFloatingActions />
|
||||||
|
</PageForm.Footer>
|
||||||
|
|
||||||
{/* ------- Effects ------- */}
|
{/* ------- Effects ------- */}
|
||||||
<PaymentReceiveSyncIncrementSettingsToForm />
|
<PaymentReceiveSyncIncrementSettingsToForm />
|
||||||
@@ -191,13 +196,13 @@ function PaymentReceiveForm({
|
|||||||
<PaymentReceiveFormAlerts />
|
<PaymentReceiveFormAlerts />
|
||||||
<PaymentReceiveFormDialogs />
|
<PaymentReceiveFormDialogs />
|
||||||
</PaymentReceiveInnerProvider>
|
</PaymentReceiveInnerProvider>
|
||||||
|
</PageForm>
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export const PaymentReceivedForm = compose(
|
||||||
withSettings(({ paymentReceiveSettings }) => ({
|
withSettings(({ paymentReceiveSettings }) => ({
|
||||||
paymentReceiveSettings,
|
paymentReceiveSettings,
|
||||||
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
|
paymentReceiveNextNumber: paymentReceiveSettings?.nextNumber,
|
||||||
@@ -207,4 +212,4 @@ export default compose(
|
|||||||
})),
|
})),
|
||||||
withCurrentOrganization(),
|
withCurrentOrganization(),
|
||||||
withDialogActions,
|
withDialogActions,
|
||||||
)(PaymentReceiveForm);
|
)(PaymentReceiveFormRoot);
|
||||||
|
|||||||
@@ -2,15 +2,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FastField } from 'formik';
|
import { FastField } from 'formik';
|
||||||
import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
|
import PaymentReceiveItemsTable from './PaymentReceiveItemsTable';
|
||||||
import classNames from 'classnames';
|
import { Box } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment Receive form body.
|
* Payment Receive form body.
|
||||||
*/
|
*/
|
||||||
export default function PaymentReceiveFormBody() {
|
export default function PaymentReceiveFormBody() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<Box p="18px 32px 0">
|
||||||
<FastField name={'entries'}>
|
<FastField name={'entries'}>
|
||||||
{({ form: { values, setFieldValue }, field: { value } }) => (
|
{({ form: { values, setFieldValue }, field: { value } }) => (
|
||||||
<PaymentReceiveItemsTable
|
<PaymentReceiveItemsTable
|
||||||
@@ -22,6 +21,6 @@ export default function PaymentReceiveFormBody() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import { x } from '@xstyled/emotion';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { Row, Col, Paper, Box } from '@/components';
|
||||||
import { Row, Col, Paper } from '@/components';
|
|
||||||
import { PaymentReceiveFormFootetLeft } from './PaymentReceiveFormFootetLeft';
|
import { PaymentReceiveFormFootetLeft } from './PaymentReceiveFormFootetLeft';
|
||||||
import { PaymentReceiveFormFootetRight } from './PaymentReceiveFormFootetRight';
|
import { PaymentReceiveFormFootetRight } from './PaymentReceiveFormFootetRight';
|
||||||
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive form footer.
|
* Payment received form footer.
|
||||||
*/
|
*/
|
||||||
export default function PaymentReceiveFormFooter() {
|
export default function PaymentReceiveFormFooter() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<Box mt={'20px'} px={'32px'} pb={'20px'} flex={1}>
|
||||||
<PaymentReceiveFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<PaymentReceiveFormFootetLeft />
|
<PaymentReceiveFormFootetLeft />
|
||||||
@@ -26,11 +24,7 @@ export default function PaymentReceiveFormFooter() {
|
|||||||
<PaymentReceiveFormFootetRight />
|
<PaymentReceiveFormFootetRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</PaymentReceiveFooterPaper>
|
</Paper>
|
||||||
</div>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PaymentReceiveFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { sumBy } from 'lodash';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { Money } from '@/components';
|
import { Group, Money } from '@/components';
|
||||||
import { FormattedMessage as T } from '@/components';
|
import { FormattedMessage as T } from '@/components';
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { CLASSES } from '@/constants/classes';
|
||||||
@@ -14,12 +13,16 @@ import PaymentReceiveHeaderFields from './PaymentReceiveHeaderFields';
|
|||||||
*/
|
*/
|
||||||
function PaymentReceiveFormHeader() {
|
function PaymentReceiveFormHeader() {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<Group
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
position="apart"
|
||||||
|
align={'flex-start'}
|
||||||
|
bg="white"
|
||||||
|
p="25px 32px"
|
||||||
|
borderBottom="1px solid #d2dce2"
|
||||||
|
>
|
||||||
<PaymentReceiveHeaderFields />
|
<PaymentReceiveHeaderFields />
|
||||||
<PaymentReceiveFormBigTotal />
|
<PaymentReceiveFormBigTotal />
|
||||||
</div>
|
</Group>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,40 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
import { PaymentReceiveFormProvider } from './PaymentReceiveFormProvider';
|
import {
|
||||||
import PaymentReceiveForm from './PaymentReceiveForm';
|
PaymentReceiveFormProvider,
|
||||||
|
usePaymentReceiveFormContext,
|
||||||
|
} from './PaymentReceiveFormProvider';
|
||||||
|
import { PaymentReceivedForm } from './PaymentReceiveForm';
|
||||||
|
import { DashboardInsider } from '@/components';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive form page.
|
* Payment received form page.
|
||||||
*/
|
*/
|
||||||
export default function PaymentReceiveFormPage() {
|
export default function PaymentReceiveFormPage() {
|
||||||
const { id: paymentReceiveId } = useParams();
|
const { id } = useParams();
|
||||||
const paymentReceiveIdInt = parseInt(paymentReceiveId, 10);
|
const paymentReceivedId = parseInt(id, 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PaymentReceiveFormProvider paymentReceiveId={paymentReceiveIdInt}>
|
<PaymentReceiveFormProvider paymentReceiveId={paymentReceivedId}>
|
||||||
<PaymentReceiveForm paymentReceiveId={paymentReceiveIdInt} />
|
<PaymentReceivedFormPageContent />
|
||||||
</PaymentReceiveFormProvider>
|
</PaymentReceiveFormProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function PaymentReceivedFormPageContent() {
|
||||||
|
const { isBootLoading } = usePaymentReceiveFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={isBootLoading}
|
||||||
|
className={css`
|
||||||
|
min-height: calc(100vh - var(--top-offset));
|
||||||
|
max-height: calc(100vh - var(--top-offset));
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<PaymentReceivedForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,6 +97,13 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
|
|
||||||
const [isExcessConfirmed, setIsExcessConfirmed] = useState<boolean>(false);
|
const [isExcessConfirmed, setIsExcessConfirmed] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const isBootLoading =
|
||||||
|
isPaymentLoading ||
|
||||||
|
isAccountsLoading ||
|
||||||
|
isCustomersLoading ||
|
||||||
|
isBrandingTemplatesLoading ||
|
||||||
|
isPaymentReceivedStateLoading;
|
||||||
|
|
||||||
// Provider payload.
|
// Provider payload.
|
||||||
const provider = {
|
const provider = {
|
||||||
paymentReceiveId,
|
paymentReceiveId,
|
||||||
@@ -131,20 +138,11 @@ function PaymentReceiveFormProvider({ query, paymentReceiveId, ...props }) {
|
|||||||
// Payment received state
|
// Payment received state
|
||||||
isPaymentReceivedStateLoading,
|
isPaymentReceivedStateLoading,
|
||||||
paymentReceivedState,
|
paymentReceivedState,
|
||||||
|
|
||||||
|
isBootLoading,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isLoading =
|
return <PaymentReceiveFormContext.Provider value={provider} {...props} />;
|
||||||
isPaymentLoading ||
|
|
||||||
isAccountsLoading ||
|
|
||||||
isCustomersLoading ||
|
|
||||||
isBrandingTemplatesLoading ||
|
|
||||||
isPaymentReceivedStateLoading;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DashboardInsider loading={isLoading} name={'payment-receive-form'}>
|
|
||||||
<PaymentReceiveFormContext.Provider value={provider} {...props} />
|
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const usePaymentReceiveFormContext = () =>
|
const usePaymentReceiveFormContext = () =>
|
||||||
|
|||||||
@@ -3,31 +3,26 @@ import React, { useMemo } from 'react';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Position,
|
Position,
|
||||||
Classes,
|
Classes,
|
||||||
ControlGroup,
|
ControlGroup,
|
||||||
Button,
|
Button,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
|
||||||
import { isEmpty, toSafeInteger } from 'lodash';
|
import { isEmpty, toSafeInteger } from 'lodash';
|
||||||
import { FastField, useFormikContext, ErrorMessage } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
import { Theme, useTheme } from '@emotion/react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FeatureCan,
|
FeatureCan,
|
||||||
CustomersSelect,
|
CustomersSelect,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
FMoneyInputGroup,
|
FMoneyInputGroup,
|
||||||
|
Stack,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
import { safeSumBy } from '@/utils';
|
||||||
import {
|
|
||||||
safeSumBy,
|
|
||||||
momentFormatter,
|
|
||||||
tansformDateValue,
|
|
||||||
handleDateChange,
|
|
||||||
inputIntent,
|
|
||||||
} from '@/utils';
|
|
||||||
import {
|
import {
|
||||||
FFormGroup,
|
FFormGroup,
|
||||||
AccountsSelect,
|
AccountsSelect,
|
||||||
@@ -55,10 +50,29 @@ import {
|
|||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import { PaymentReceivePaymentNoField } from './PaymentReceivePaymentNoField';
|
import { PaymentReceivePaymentNoField } from './PaymentReceivePaymentNoField';
|
||||||
|
|
||||||
|
const getHeaderFieldsStyle = (theme: Theme) => css`
|
||||||
|
.${theme.bpPrefix}-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.${theme.bpPrefix}-inline {
|
||||||
|
max-width: 470px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-label {
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-form-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Payment receive header fields.
|
* Payment receive header fields.
|
||||||
*/
|
*/
|
||||||
export default function PaymentReceiveHeaderFields() {
|
export default function PaymentReceiveHeaderFields() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const styleClassName = getHeaderFieldsStyle(theme);
|
||||||
|
|
||||||
// Payment receive form context.
|
// Payment receive form context.
|
||||||
const { accounts, projects } = usePaymentReceiveFormContext();
|
const { accounts, projects } = usePaymentReceiveFormContext();
|
||||||
|
|
||||||
@@ -88,7 +102,7 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<Stack spacing={18} flex={1} className={styleClassName}>
|
||||||
{/* ------------- Customer name ------------- */}
|
{/* ------------- Customer name ------------- */}
|
||||||
<PaymentReceiveCustomerSelect />
|
<PaymentReceiveCustomerSelect />
|
||||||
|
|
||||||
@@ -97,31 +111,28 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
name={'exchange_rate'}
|
name={'exchange_rate'}
|
||||||
formGroupProps={{ label: ' ', inline: true }}
|
formGroupProps={{ label: ' ', inline: true }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* ------------- Payment date ------------- */}
|
{/* ------------- Payment date ------------- */}
|
||||||
<FastField name={'payment_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'payment_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'payment_date'} />}
|
label={<T id={'payment_date'} />}
|
||||||
inline={true}
|
|
||||||
labelInfo={<FieldRequiredHint />}
|
labelInfo={<FieldRequiredHint />}
|
||||||
className={classNames('form-group--select-list', CLASSES.FILL)}
|
inline
|
||||||
intent={inputIntent({ error, touched })}
|
fastField
|
||||||
helperText={<ErrorMessage name="payment_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'payment_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('payment_date', formattedDate);
|
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
inputProps={{
|
inputProps={{
|
||||||
leftIcon: <Icon icon={'date-range'} />,
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/* ------------ Full amount ------------ */}
|
{/* ------------ Full amount ------------ */}
|
||||||
<FFormGroup
|
<FFormGroup
|
||||||
@@ -143,9 +154,25 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
{!isEmpty(entries) && (
|
{!isEmpty(entries) && (
|
||||||
<Button
|
<Button
|
||||||
onClick={handleReceiveFullAmountClick}
|
onClick={handleReceiveFullAmountClick}
|
||||||
className={'receive-full-amount'}
|
className={css`
|
||||||
small={true}
|
&:not([class*='${theme.bpPrefix}-intent-']) {
|
||||||
minimal={true}
|
&.${theme.bpPrefix}-minimal {
|
||||||
|
width: auto;
|
||||||
|
padding: 0;
|
||||||
|
min-height: auto;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
background-color: transparent;
|
||||||
|
color: #0052cc;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
small
|
||||||
|
minimal
|
||||||
>
|
>
|
||||||
<T id={'receive_full_amount'} /> (
|
<T id={'receive_full_amount'} /> (
|
||||||
<Money amount={totalDueAmount} currency={currency_code} />)
|
<Money amount={totalDueAmount} currency={currency_code} />)
|
||||||
@@ -186,10 +213,10 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
<FFormGroup
|
<FFormGroup
|
||||||
name={'reference_no'}
|
name={'reference_no'}
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
inline={true}
|
inline
|
||||||
fastField
|
fastField
|
||||||
>
|
>
|
||||||
<InputGroup name={'reference_no'} minimal={true} fastField />
|
<InputGroup name={'reference_no'} minimal fastField />
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
{/*------------ Project name -----------*/}
|
{/*------------ Project name -----------*/}
|
||||||
@@ -208,7 +235,7 @@ export default function PaymentReceiveHeaderFields() {
|
|||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,24 @@
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { ElementCustomize } from '../../../ElementCustomize/ElementCustomize';
|
import {
|
||||||
|
ElementCustomize,
|
||||||
|
ElementCustomizeContent,
|
||||||
|
} from '../../../ElementCustomize/ElementCustomize';
|
||||||
import { PaymentReceivedCustomizeGeneralField } from './PaymentReceivedCustomizeFieldsGeneral';
|
import { PaymentReceivedCustomizeGeneralField } from './PaymentReceivedCustomizeFieldsGeneral';
|
||||||
import { PaymentReceivedCustomizeContentFields } from './PaymentReceivedCustomizeFieldsContent';
|
import { PaymentReceivedCustomizeContentFields } from './PaymentReceivedCustomizeFieldsContent';
|
||||||
import { PaymentReceivedCustomizeValues } from './types';
|
import {
|
||||||
import { PaymentReceivedPaperTemplate } from './PaymentReceivedPaperTemplate';
|
PaymentReceivedCustomizeValues,
|
||||||
|
PaymentReceivedPreviewState,
|
||||||
|
} from './types';
|
||||||
|
import {
|
||||||
|
PaymentReceivedPaperTemplate,
|
||||||
|
PaymentReceivedPaperTemplateProps,
|
||||||
|
} from './PaymentReceivedPaperTemplate';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
|
||||||
export function PaymentReceivedCustomizeContent() {
|
export function PaymentReceivedCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -20,12 +31,25 @@ export function PaymentReceivedCustomizeContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrandingTemplateForm<PaymentReceivedCustomizeValues>
|
<BrandingTemplateForm<
|
||||||
|
PaymentReceivedCustomizeValues,
|
||||||
|
PaymentReceivedPreviewState
|
||||||
|
>
|
||||||
templateId={templateId}
|
templateId={templateId}
|
||||||
defaultValues={initialValues}
|
defaultValues={initialValues}
|
||||||
onSuccess={handleSuccess}
|
onSuccess={handleSuccess}
|
||||||
resource={'PaymentReceive'}
|
resource={'PaymentReceive'}
|
||||||
>
|
>
|
||||||
|
<PaymentReceivedCustomizeFormContent />
|
||||||
|
</BrandingTemplateForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function PaymentReceivedCustomizeFormContent() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<PaymentReceivedPaperTemplateFormConnected />
|
<PaymentReceivedPaperTemplateFormConnected />
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
@@ -34,15 +58,25 @@ export function PaymentReceivedCustomizeContent() {
|
|||||||
<PaymentReceivedCustomizeGeneralField />
|
<PaymentReceivedCustomizeGeneralField />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
<ElementCustomize.FieldsTab
|
||||||
|
id={'content'}
|
||||||
|
label={'Content'}
|
||||||
|
tabProps={{ disabled: !isTemplateNameFilled }}
|
||||||
|
>
|
||||||
<PaymentReceivedCustomizeContentFields />
|
<PaymentReceivedCustomizeContentFields />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
</BrandingTemplateForm>
|
</ElementCustomizeContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function PaymentReceivedPaperTemplateFormConnected() {
|
function PaymentReceivedPaperTemplateFormConnected() {
|
||||||
const { values } = useFormikContext<PaymentReceivedCustomizeValues>();
|
const { values } = useFormikContext<PaymentReceivedCustomizeValues>();
|
||||||
|
const { brandingState } = useElementCustomizeContext();
|
||||||
|
|
||||||
return <PaymentReceivedPaperTemplate {...values} />;
|
const paperTemplateProps: PaymentReceivedPaperTemplateProps = {
|
||||||
|
...brandingState,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <PaymentReceivedPaperTemplate {...paperTemplateProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,8 @@ export const initialValues = {
|
|||||||
showPaymentReceivedDate: true,
|
showPaymentReceivedDate: true,
|
||||||
paymentReceivedDateLabel: 'Date of Issue',
|
paymentReceivedDateLabel: 'Date of Issue',
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName: 'Bigcapital Technology, Inc.',
|
|
||||||
|
|
||||||
// Customer address
|
// Customer address
|
||||||
showCompanyAddress: true,
|
showCompanyAddress: true,
|
||||||
companyAddress: '',
|
|
||||||
|
|
||||||
// Company address
|
// Company address
|
||||||
showCustomerAddress: true,
|
showCustomerAddress: true,
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
import { BrandingState, BrandingTemplateValues } from '@/containers/BrandingTemplates/types';
|
||||||
|
|
||||||
|
export interface PaymentReceivedPreviewState extends BrandingState {}
|
||||||
|
|
||||||
export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
|
export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
|
||||||
// Colors
|
// Colors
|
||||||
@@ -20,15 +22,11 @@ export interface PaymentReceivedCustomizeValues extends BrandingTemplateValues {
|
|||||||
showDueDate?: boolean;
|
showDueDate?: boolean;
|
||||||
dueDateLabel?: string;
|
dueDateLabel?: string;
|
||||||
|
|
||||||
// # Company name
|
|
||||||
companyName?: string;
|
|
||||||
|
|
||||||
// # Customer address
|
// # Customer address
|
||||||
showCustomerAddress?: boolean;
|
showCustomerAddress?: boolean;
|
||||||
|
|
||||||
// # Company address
|
// # Company address
|
||||||
showCompanyAddress?: boolean;
|
showCompanyAddress?: boolean;
|
||||||
companyAddress?: string;
|
|
||||||
billedToLabel?: string;
|
billedToLabel?: string;
|
||||||
|
|
||||||
// Entries
|
// Entries
|
||||||
|
|||||||
@@ -1,13 +1,21 @@
|
|||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { ElementCustomize } from '@/containers/ElementCustomize/ElementCustomize';
|
import {
|
||||||
|
ElementCustomize,
|
||||||
|
ElementCustomizeContent,
|
||||||
|
} from '@/containers/ElementCustomize/ElementCustomize';
|
||||||
import { ReceiptCustomizeGeneralField } from './ReceiptCustomizeFieldsGeneral';
|
import { ReceiptCustomizeGeneralField } from './ReceiptCustomizeFieldsGeneral';
|
||||||
import { ReceiptCustomizeFieldsContent } from './ReceiptCustomizeFieldsContent';
|
import { ReceiptCustomizeFieldsContent } from './ReceiptCustomizeFieldsContent';
|
||||||
import { ReceiptPaperTemplate } from './ReceiptPaperTemplate';
|
import {
|
||||||
import { ReceiptCustomizeValues } from './types';
|
ReceiptPaperTemplate,
|
||||||
|
ReceiptPaperTemplateProps,
|
||||||
|
} from './ReceiptPaperTemplate';
|
||||||
|
import { EstimateBrandingState, ReceiptCustomizeValues } from './types';
|
||||||
import { initialValues } from './constants';
|
import { initialValues } from './constants';
|
||||||
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
import { BrandingTemplateForm } from '@/containers/BrandingTemplates/BrandingTemplateForm';
|
||||||
import { useDrawerActions } from '@/hooks/state';
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
import { useDrawerContext } from '@/components/Drawer/DrawerProvider';
|
||||||
|
import { useElementCustomizeContext } from '@/containers/ElementCustomize/ElementCustomizeProvider';
|
||||||
|
import { useIsTemplateNamedFilled } from '@/containers/BrandingTemplates/utils';
|
||||||
|
|
||||||
export function ReceiptCustomizeContent() {
|
export function ReceiptCustomizeContent() {
|
||||||
const { payload, name } = useDrawerContext();
|
const { payload, name } = useDrawerContext();
|
||||||
@@ -19,12 +27,22 @@ export function ReceiptCustomizeContent() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrandingTemplateForm<ReceiptCustomizeValues>
|
<BrandingTemplateForm<ReceiptCustomizeValues, EstimateBrandingState>
|
||||||
resource={'SaleReceipt'}
|
resource={'SaleReceipt'}
|
||||||
templateId={templateId}
|
templateId={templateId}
|
||||||
defaultValues={initialValues}
|
defaultValues={initialValues}
|
||||||
onSuccess={handleFormSuccess}
|
onSuccess={handleFormSuccess}
|
||||||
>
|
>
|
||||||
|
<ReceiptCustomizeFormContent />
|
||||||
|
</BrandingTemplateForm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ReceiptCustomizeFormContent() {
|
||||||
|
const isTemplateNameFilled = useIsTemplateNamedFilled();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ElementCustomizeContent>
|
||||||
<ElementCustomize.PaperTemplate>
|
<ElementCustomize.PaperTemplate>
|
||||||
<ReceiptPaperTemplateFormConnected />
|
<ReceiptPaperTemplateFormConnected />
|
||||||
</ElementCustomize.PaperTemplate>
|
</ElementCustomize.PaperTemplate>
|
||||||
@@ -33,15 +51,25 @@ export function ReceiptCustomizeContent() {
|
|||||||
<ReceiptCustomizeGeneralField />
|
<ReceiptCustomizeGeneralField />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
|
|
||||||
<ElementCustomize.FieldsTab id={'content'} label={'Content'}>
|
<ElementCustomize.FieldsTab
|
||||||
|
id={'content'}
|
||||||
|
label={'Content'}
|
||||||
|
tabProps={{ disabled: !isTemplateNameFilled }}
|
||||||
|
>
|
||||||
<ReceiptCustomizeFieldsContent />
|
<ReceiptCustomizeFieldsContent />
|
||||||
</ElementCustomize.FieldsTab>
|
</ElementCustomize.FieldsTab>
|
||||||
</BrandingTemplateForm>
|
</ElementCustomizeContent>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ReceiptPaperTemplateFormConnected() {
|
function ReceiptPaperTemplateFormConnected() {
|
||||||
const { values } = useFormikContext<ReceiptCustomizeValues>();
|
const { values } = useFormikContext<ReceiptCustomizeValues>();
|
||||||
|
const { brandingState } = useElementCustomizeContext();
|
||||||
|
|
||||||
return <ReceiptPaperTemplate {...values} />;
|
const mergedProps: ReceiptPaperTemplateProps = {
|
||||||
|
...brandingState,
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
return <ReceiptPaperTemplate {...mergedProps} />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,10 @@ export const initialValues = {
|
|||||||
showReceiptDate: true,
|
showReceiptDate: true,
|
||||||
receiptDateLabel: 'Date of Issue',
|
receiptDateLabel: 'Date of Issue',
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName: 'Bigcapital Technology, Inc.',
|
|
||||||
|
|
||||||
// Customer address
|
// Customer address
|
||||||
showCustomerAddress: true,
|
showCustomerAddress: true,
|
||||||
|
|
||||||
// Company address
|
// Company address
|
||||||
companyAddress: '',
|
|
||||||
showCompanyAddress: true,
|
showCompanyAddress: true,
|
||||||
billedToLabel: 'Billed To',
|
billedToLabel: 'Billed To',
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
|
import { BrandingState, BrandingTemplateValues } from "@/containers/BrandingTemplates/types";
|
||||||
|
|
||||||
|
export interface EstimateBrandingState extends BrandingState {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export interface ReceiptCustomizeValues extends BrandingTemplateValues {
|
export interface ReceiptCustomizeValues extends BrandingTemplateValues {
|
||||||
// Colors
|
// Colors
|
||||||
@@ -16,9 +20,6 @@ export interface ReceiptCustomizeValues extends BrandingTemplateValues {
|
|||||||
showReceiptDate?: boolean;
|
showReceiptDate?: boolean;
|
||||||
receiptDateLabel?: string;
|
receiptDateLabel?: string;
|
||||||
|
|
||||||
// Company name
|
|
||||||
companyName?: string;
|
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
showBilledFromAddress?: boolean;
|
showBilledFromAddress?: boolean;
|
||||||
showBilledToAddress?: boolean;
|
showBilledToAddress?: boolean;
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { sumBy, isEmpty } from 'lodash';
|
import { sumBy, isEmpty } from 'lodash';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import {
|
import {
|
||||||
EditReceiptFormSchema,
|
EditReceiptFormSchema,
|
||||||
CreateReceiptFormSchema,
|
CreateReceiptFormSchema,
|
||||||
@@ -17,7 +16,7 @@ import { useReceiptFormContext } from './ReceiptFormProvider';
|
|||||||
import ReceiptFromHeader from './ReceiptFormHeader';
|
import ReceiptFromHeader from './ReceiptFormHeader';
|
||||||
import ReceiptItemsEntriesEditor from './ReceiptItemsEntriesEditor';
|
import ReceiptItemsEntriesEditor from './ReceiptItemsEntriesEditor';
|
||||||
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
|
import ReceiptFormFloatingActions from './ReceiptFormFloatingActions';
|
||||||
import ReceiptFormFooter from './ReceiptFormFooter';
|
import { ReceiptFormFooter } from './ReceiptFormFooter';
|
||||||
import ReceiptFormDialogs from './ReceiptFormDialogs';
|
import ReceiptFormDialogs from './ReceiptFormDialogs';
|
||||||
import ReceiptFormTopBar from './ReceiptFormTopbar';
|
import ReceiptFormTopBar from './ReceiptFormTopbar';
|
||||||
|
|
||||||
@@ -38,11 +37,12 @@ import {
|
|||||||
ReceiptSyncAutoExRateToForm,
|
ReceiptSyncAutoExRateToForm,
|
||||||
ReceiptSyncIncrementSettingsToForm,
|
ReceiptSyncIncrementSettingsToForm,
|
||||||
} from './components';
|
} from './components';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt form.
|
* Receipt form.
|
||||||
*/
|
*/
|
||||||
function ReceiptForm({
|
function ReceiptFormRoot({
|
||||||
// #withSettings
|
// #withSettings
|
||||||
receiptNextNumber,
|
receiptNextNumber,
|
||||||
receiptNumberPrefix,
|
receiptNumberPrefix,
|
||||||
@@ -150,13 +150,6 @@ function ReceiptForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
|
||||||
className={classNames(
|
|
||||||
CLASSES.PAGE_FORM,
|
|
||||||
CLASSES.PAGE_FORM_STRIP_STYLE,
|
|
||||||
CLASSES.PAGE_FORM_RECEIPT,
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Formik
|
<Formik
|
||||||
validationSchema={
|
validationSchema={
|
||||||
isNewMode ? CreateReceiptFormSchema : EditReceiptFormSchema
|
isNewMode ? CreateReceiptFormSchema : EditReceiptFormSchema
|
||||||
@@ -164,26 +157,39 @@ function ReceiptForm({
|
|||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
>
|
>
|
||||||
<Form>
|
<Form
|
||||||
|
className={css({
|
||||||
|
overflow: 'hidden',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
flex: 1,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<PageForm flex={1}>
|
||||||
|
<PageForm.Body>
|
||||||
<ReceiptFormTopBar />
|
<ReceiptFormTopBar />
|
||||||
<ReceiptFromHeader />
|
<ReceiptFromHeader />
|
||||||
<ReceiptItemsEntriesEditor />
|
<ReceiptItemsEntriesEditor />
|
||||||
<ReceiptFormFooter />
|
<ReceiptFormFooter />
|
||||||
|
</PageForm.Body>
|
||||||
|
|
||||||
|
<PageForm.Footer>
|
||||||
<ReceiptFormFloatingActions />
|
<ReceiptFormFloatingActions />
|
||||||
|
</PageForm.Footer>
|
||||||
|
</PageForm>
|
||||||
|
|
||||||
{/*---------- Dialogs ---------*/}
|
{/*---------- Dialogs ---------*/}
|
||||||
<ReceiptFormDialogs />
|
<ReceiptFormDialogs />
|
||||||
css
|
|
||||||
{/*---------- Effects ---------*/}
|
{/*---------- Effects ---------*/}
|
||||||
<ReceiptSyncIncrementSettingsToForm />
|
<ReceiptSyncIncrementSettingsToForm />
|
||||||
<ReceiptSyncAutoExRateToForm />
|
<ReceiptSyncAutoExRateToForm />
|
||||||
</Form>
|
</Form>
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export const ReceiptForm = compose(
|
||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withSettings(({ receiptSettings }) => ({
|
withSettings(({ receiptSettings }) => ({
|
||||||
receiptNextNumber: receiptSettings?.nextNumber,
|
receiptNextNumber: receiptSettings?.nextNumber,
|
||||||
@@ -194,4 +200,4 @@ export default compose(
|
|||||||
preferredDepositAccount: receiptSettings?.preferredDepositAccount,
|
preferredDepositAccount: receiptSettings?.preferredDepositAccount,
|
||||||
})),
|
})),
|
||||||
withCurrentOrganization(),
|
withCurrentOrganization(),
|
||||||
)(ReceiptForm);
|
)(ReceiptFormRoot);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
@@ -14,14 +13,17 @@ import {
|
|||||||
import { FSelect, Group, FormattedMessage as T } from '@/components';
|
import { FSelect, Group, FormattedMessage as T } from '@/components';
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { If, Icon } from '@/components';
|
import { If, Icon } from '@/components';
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
import { useReceiptFormBrandingTemplatesOptions } from './utils';
|
import { useReceiptFormBrandingTemplatesOptions } from './utils';
|
||||||
|
import { useDrawerActions } from '@/hooks/state';
|
||||||
import {
|
import {
|
||||||
BrandingThemeFormGroup,
|
BrandingThemeFormGroup,
|
||||||
BrandingThemeSelectButton,
|
BrandingThemeSelectButton,
|
||||||
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
} from '@/containers/BrandingTemplates/BrandingTemplatesSelectFields';
|
||||||
|
import { PageForm } from '@/components/PageForm';
|
||||||
|
import { MoreIcon } from '@/icons/More';
|
||||||
|
import { DRAWERS } from '@/constants/drawers';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt floating actions bar.
|
* Receipt floating actions bar.
|
||||||
@@ -30,6 +32,8 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
// History context.
|
// History context.
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
|
const { openDrawer } = useDrawerActions();
|
||||||
|
|
||||||
// Formik context.
|
// Formik context.
|
||||||
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
const { resetForm, submitForm, isSubmitting } = useFormikContext();
|
||||||
|
|
||||||
@@ -81,13 +85,16 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
resetForm();
|
resetForm();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handles the invoice customize button click.
|
||||||
|
const handleCustomizeBtnClick = () => {
|
||||||
|
openDrawer(DRAWERS.BRANDING_TEMPLATES, { resource: 'SaleReceipt' });
|
||||||
|
};
|
||||||
|
|
||||||
const brandingTemplatesOptions = useReceiptFormBrandingTemplatesOptions();
|
const brandingTemplatesOptions = useReceiptFormBrandingTemplatesOptions();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Group
|
<PageForm.FooterActions spacing={10} position="apart">
|
||||||
spacing={10}
|
<Group spacing={10}>
|
||||||
className={classNames(CLASSES.PAGE_FORM_FLOATING_ACTIONS)}
|
|
||||||
>
|
|
||||||
{/* ----------- Save And Close ----------- */}
|
{/* ----------- Save And Close ----------- */}
|
||||||
<If condition={!receipt || !receipt?.is_closed}>
|
<If condition={!receipt || !receipt?.is_closed}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -154,6 +161,7 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
</Popover>
|
</Popover>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
{/* ----------- Save and New ----------- */}
|
{/* ----------- Save and New ----------- */}
|
||||||
<If condition={receipt && receipt?.is_closed}>
|
<If condition={receipt && receipt?.is_closed}>
|
||||||
<ButtonGroup>
|
<ButtonGroup>
|
||||||
@@ -185,6 +193,7 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
</Popover>
|
</Popover>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
{/* ----------- Clear & Reset----------- */}
|
{/* ----------- Clear & Reset----------- */}
|
||||||
<Button
|
<Button
|
||||||
className={'ml1'}
|
className={'ml1'}
|
||||||
@@ -198,7 +207,9 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
onClick={handleCancelBtnClick}
|
onClick={handleCancelBtnClick}
|
||||||
text={<T id={'cancel'} />}
|
text={<T id={'cancel'} />}
|
||||||
/>
|
/>
|
||||||
|
</Group>
|
||||||
|
|
||||||
|
<Group spacing={0}>
|
||||||
{/* ----------- Branding Template Select ----------- */}
|
{/* ----------- Branding Template Select ----------- */}
|
||||||
<BrandingThemeFormGroup
|
<BrandingThemeFormGroup
|
||||||
name={'pdf_template_id'}
|
name={'pdf_template_id'}
|
||||||
@@ -217,6 +228,27 @@ export default function ReceiptFormFloatingActions() {
|
|||||||
popoverProps={{ minimal: true }}
|
popoverProps={{ minimal: true }}
|
||||||
/>
|
/>
|
||||||
</BrandingThemeFormGroup>
|
</BrandingThemeFormGroup>
|
||||||
|
|
||||||
|
{/* ----------- Setting Select ----------- */}
|
||||||
|
<Popover
|
||||||
|
minimal={true}
|
||||||
|
interactionKind={PopoverInteractionKind.CLICK}
|
||||||
|
position={Position.TOP_RIGHT}
|
||||||
|
modifiers={{
|
||||||
|
offset: { offset: '0, 4' },
|
||||||
|
}}
|
||||||
|
content={
|
||||||
|
<Menu>
|
||||||
|
<MenuItem
|
||||||
|
text={'Customize Templates'}
|
||||||
|
onClick={handleCustomizeBtnClick}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Button minimal icon={<MoreIcon height={'14px'} width={'14px'} />} />
|
||||||
|
</Popover>
|
||||||
</Group>
|
</Group>
|
||||||
|
</PageForm.FooterActions>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,15 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import { x } from '@xstyled/emotion';
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { Paper, Row, Col } from '@/components';
|
import { Paper, Row, Col } from '@/components';
|
||||||
import { ReceiptFormFooterLeft } from './ReceiptFormFooterLeft';
|
import { ReceiptFormFooterLeft } from './ReceiptFormFooterLeft';
|
||||||
import { ReceiptFormFooterRight } from './ReceiptFormFooterRight';
|
import { ReceiptFormFooterRight } from './ReceiptFormFooterRight';
|
||||||
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
import { UploadAttachmentButton } from '@/containers/Attachments/UploadAttachmentButton';
|
||||||
|
|
||||||
export default function ReceiptFormFooter({}) {
|
export function ReceiptFormFooter({}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_FOOTER)}>
|
<x.div mt={'20px'} px={'32px'} pb={'20px'} flex={1}>
|
||||||
<ReceiptFooterPaper>
|
<Paper p={'20px'}>
|
||||||
<Row>
|
<Row>
|
||||||
<Col md={8}>
|
<Col md={8}>
|
||||||
<ReceiptFormFooterLeft />
|
<ReceiptFormFooterLeft />
|
||||||
@@ -23,11 +20,7 @@ export default function ReceiptFormFooter({}) {
|
|||||||
<ReceiptFormFooterRight />
|
<ReceiptFormFooterRight />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</ReceiptFooterPaper>
|
</Paper>
|
||||||
</div>
|
</x.div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReceiptFooterPaper = styled(Paper)`
|
|
||||||
padding: 20px;
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import classNames from 'classnames';
|
|
||||||
import { useFormikContext } from 'formik';
|
import { useFormikContext } from 'formik';
|
||||||
|
import { Group, PageFormBigNumber } from '@/components';
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { PageFormBigNumber } from '@/components';
|
|
||||||
import ReceiptFormHeaderFields from './ReceiptFormHeaderFields';
|
import ReceiptFormHeaderFields from './ReceiptFormHeaderFields';
|
||||||
|
|
||||||
import { getEntriesTotal } from '@/containers/Entries/utils';
|
import { getEntriesTotal } from '@/containers/Entries/utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,12 +14,19 @@ function ReceiptFormHeader({
|
|||||||
onReceiptNumberChanged,
|
onReceiptNumberChanged,
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
<Group
|
||||||
|
position="apart"
|
||||||
|
align={'flex-start'}
|
||||||
|
display="flex"
|
||||||
|
bg="white"
|
||||||
|
p="25px 32px"
|
||||||
|
borderBottom="1px solid #d2dce2"
|
||||||
|
>
|
||||||
<ReceiptFormHeaderFields
|
<ReceiptFormHeaderFields
|
||||||
onReceiptNumberChanged={onReceiptNumberChanged}
|
onReceiptNumberChanged={onReceiptNumberChanged}
|
||||||
/>
|
/>
|
||||||
<ReceiptFormHeaderBigTotal />
|
<ReceiptFormHeaderBigTotal />
|
||||||
</div>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FormGroup, InputGroup, Position, Classes } from '@blueprintjs/core';
|
import { useFormikContext } from 'formik';
|
||||||
import { DateInput } from '@blueprintjs/datetime';
|
import { Position, Classes } from '@blueprintjs/core';
|
||||||
import { FastField, ErrorMessage, useFormikContext } from 'formik';
|
import { css } from '@emotion/css';
|
||||||
|
import { Theme, useTheme } from '@emotion/react';
|
||||||
|
|
||||||
import { CLASSES } from '@/constants/classes';
|
|
||||||
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
|
import { ACCOUNT_TYPE } from '@/constants/accountTypes';
|
||||||
import { Features } from '@/constants';
|
import { Features } from '@/constants';
|
||||||
import {
|
import {
|
||||||
@@ -18,14 +18,11 @@ import {
|
|||||||
CustomerDrawerLink,
|
CustomerDrawerLink,
|
||||||
FormattedMessage as T,
|
FormattedMessage as T,
|
||||||
FeatureCan,
|
FeatureCan,
|
||||||
|
FInputGroup,
|
||||||
|
Stack,
|
||||||
|
FDateInput,
|
||||||
} from '@/components';
|
} from '@/components';
|
||||||
import { ProjectsSelect } from '@/containers/Projects/components';
|
import { ProjectsSelect } from '@/containers/Projects/components';
|
||||||
import {
|
|
||||||
momentFormatter,
|
|
||||||
tansformDateValue,
|
|
||||||
handleDateChange,
|
|
||||||
inputIntent,
|
|
||||||
} from '@/utils';
|
|
||||||
import { useReceiptFormContext } from './ReceiptFormProvider';
|
import { useReceiptFormContext } from './ReceiptFormProvider';
|
||||||
import { accountsFieldShouldUpdate, customersFieldShouldUpdate } from './utils';
|
import { accountsFieldShouldUpdate, customersFieldShouldUpdate } from './utils';
|
||||||
import {
|
import {
|
||||||
@@ -35,14 +32,33 @@ import {
|
|||||||
import { ReceiptFormReceiptNumberField } from './ReceiptFormReceiptNumberField';
|
import { ReceiptFormReceiptNumberField } from './ReceiptFormReceiptNumberField';
|
||||||
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
import { useCustomerUpdateExRate } from '@/containers/Entries/withExRateItemEntriesPriceRecalc';
|
||||||
|
|
||||||
|
const getEstimateFieldsStyle = (theme: Theme) => css`
|
||||||
|
.${theme.bpPrefix}-form-group {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
&.${theme.bpPrefix}-inline {
|
||||||
|
max-width: 450px;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-label {
|
||||||
|
min-width: 150px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
.${theme.bpPrefix}-form-content {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt form header fields.
|
* Receipt form header fields.
|
||||||
*/
|
*/
|
||||||
export default function ReceiptFormHeader() {
|
export default function ReceiptFormHeader() {
|
||||||
|
const theme = useTheme();
|
||||||
|
const receiptFieldsClassName = getEstimateFieldsStyle(theme);
|
||||||
const { accounts, projects } = useReceiptFormContext();
|
const { accounts, projects } = useReceiptFormContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_FIELDS)}>
|
<Stack spacing={18} flex={1} className={receiptFieldsClassName}>
|
||||||
{/* ----------- Customer name ----------- */}
|
{/* ----------- Customer name ----------- */}
|
||||||
<ReceiptFormCustomerSelect />
|
<ReceiptFormCustomerSelect />
|
||||||
|
|
||||||
@@ -76,47 +92,37 @@ export default function ReceiptFormHeader() {
|
|||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
|
|
||||||
{/* ----------- Receipt date ----------- */}
|
{/* ----------- Receipt date ----------- */}
|
||||||
<FastField name={'receipt_date'}>
|
<FFormGroup
|
||||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
name={'receipt_date'}
|
||||||
<FormGroup
|
|
||||||
label={<T id={'receipt_date'} />}
|
label={<T id={'receipt_date'} />}
|
||||||
inline={true}
|
inline
|
||||||
className={classNames(CLASSES.FILL)}
|
fastField
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="receipt_date" />}
|
|
||||||
>
|
>
|
||||||
<DateInput
|
<FDateInput
|
||||||
{...momentFormatter('YYYY/MM/DD')}
|
name={'receipt_date'}
|
||||||
value={tansformDateValue(value)}
|
formatDate={(date) => date.toLocaleDateString()}
|
||||||
onChange={handleDateChange((formattedDate) => {
|
parseDate={(str) => new Date(str)}
|
||||||
form.setFieldValue('receipt_date', formattedDate);
|
popoverProps={{ position: Position.BOTTOM_LEFT, minimal: true }}
|
||||||
})}
|
|
||||||
popoverProps={{ position: Position.BOTTOM, minimal: true }}
|
|
||||||
inputProps={{
|
inputProps={{
|
||||||
leftIcon: <Icon icon={'date-range'} />,
|
leftIcon: <Icon icon={'date-range'} />,
|
||||||
|
fill: true,
|
||||||
}}
|
}}
|
||||||
|
fill
|
||||||
|
fastField
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/* ----------- Receipt number ----------- */}
|
{/* ----------- Receipt number ----------- */}
|
||||||
<ReceiptFormReceiptNumberField />
|
<ReceiptFormReceiptNumberField />
|
||||||
|
|
||||||
{/* ----------- Reference ----------- */}
|
{/* ----------- Reference ----------- */}
|
||||||
<FastField name={'reference_no'}>
|
<FFormGroup
|
||||||
{({ field, meta: { error, touched } }) => (
|
|
||||||
<FormGroup
|
|
||||||
label={<T id={'reference'} />}
|
label={<T id={'reference'} />}
|
||||||
inline={true}
|
inline={true}
|
||||||
className={classNames('form-group--reference', CLASSES.FILL)}
|
name={'reference_no'}
|
||||||
intent={inputIntent({ error, touched })}
|
|
||||||
helperText={<ErrorMessage name="reference_no" />}
|
|
||||||
>
|
>
|
||||||
<InputGroup minimal={true} {...field} />
|
<FInputGroup minimal={true} name={'reference_no'} />
|
||||||
</FormGroup>
|
</FFormGroup>
|
||||||
)}
|
|
||||||
</FastField>
|
|
||||||
|
|
||||||
{/*------------ Project name -----------*/}
|
{/*------------ Project name -----------*/}
|
||||||
<FeatureCan feature={Features.Projects}>
|
<FeatureCan feature={Features.Projects}>
|
||||||
@@ -134,7 +140,7 @@ export default function ReceiptFormHeader() {
|
|||||||
/>
|
/>
|
||||||
</FFormGroup>
|
</FFormGroup>
|
||||||
</FeatureCan>
|
</FeatureCan>
|
||||||
</div>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,25 +1,44 @@
|
|||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
import '@/style/pages/SaleReceipt/PageForm.scss';
|
import {
|
||||||
|
ReceiptFormProvider,
|
||||||
import ReceiptFrom from './ReceiptForm';
|
useReceiptFormContext,
|
||||||
import { ReceiptFormProvider } from './ReceiptFormProvider';
|
} from './ReceiptFormProvider';
|
||||||
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
import { AutoExchangeRateProvider } from '@/containers/Entries/AutoExchangeProvider';
|
||||||
|
import { DashboardInsider } from '@/components';
|
||||||
|
import { ReceiptForm } from './ReceiptForm';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receipt form page.
|
* Receipt form page.
|
||||||
*/
|
*/
|
||||||
export default function ReceiptFormPage() {
|
export default function ReceiptFormPage() {
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const idInt = parseInt(id, 10);
|
const receiptId = parseInt(id, 10);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReceiptFormProvider receiptId={idInt}>
|
<ReceiptFormProvider receiptId={receiptId}>
|
||||||
<AutoExchangeRateProvider>
|
<AutoExchangeRateProvider>
|
||||||
<ReceiptFrom />
|
<ReceiptFormPageContent />
|
||||||
</AutoExchangeRateProvider>
|
</AutoExchangeRateProvider>
|
||||||
</ReceiptFormProvider>
|
</ReceiptFormProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ReceiptFormPageContent() {
|
||||||
|
const { isBootLoading } = useReceiptFormContext();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DashboardInsider
|
||||||
|
loading={isBootLoading}
|
||||||
|
className={css`
|
||||||
|
min-height: calc(100vh - var(--top-offset));
|
||||||
|
max-height: calc(100vh - var(--top-offset));
|
||||||
|
`}
|
||||||
|
>
|
||||||
|
<ReceiptForm />
|
||||||
|
</DashboardInsider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -120,6 +120,15 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
const isNewMode = !receiptId;
|
const isNewMode = !receiptId;
|
||||||
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
|
const isFeatureLoading = isWarehouesLoading || isBranchesLoading;
|
||||||
|
|
||||||
|
const isBootLoading =
|
||||||
|
isReceiptLoading ||
|
||||||
|
isAccountsLoading ||
|
||||||
|
isCustomersLoading ||
|
||||||
|
isItemsLoading ||
|
||||||
|
isSettingLoading ||
|
||||||
|
isBrandingTemplatesLoading ||
|
||||||
|
isSaleReceiptStateLoading;
|
||||||
|
|
||||||
const provider = {
|
const provider = {
|
||||||
receiptId,
|
receiptId,
|
||||||
receipt,
|
receipt,
|
||||||
@@ -154,21 +163,10 @@ function ReceiptFormProvider({ receiptId, ...props }) {
|
|||||||
// State
|
// State
|
||||||
isSaleReceiptStateLoading,
|
isSaleReceiptStateLoading,
|
||||||
saleReceiptState,
|
saleReceiptState,
|
||||||
};
|
|
||||||
const isLoading =
|
|
||||||
isReceiptLoading ||
|
|
||||||
isAccountsLoading ||
|
|
||||||
isCustomersLoading ||
|
|
||||||
isItemsLoading ||
|
|
||||||
isSettingLoading ||
|
|
||||||
isBrandingTemplatesLoading ||
|
|
||||||
isSaleReceiptStateLoading;
|
|
||||||
|
|
||||||
return (
|
isBootLoading,
|
||||||
<DashboardInsider loading={isLoading} name={'receipt-form'}>
|
};
|
||||||
<ReceiptFormContext.Provider value={provider} {...props} />
|
return <ReceiptFormContext.Provider value={provider} {...props} />;
|
||||||
</DashboardInsider>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useReceiptFormContext = () =>
|
const useReceiptFormContext = () =>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user