mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: mark specific template as default
This commit is contained in:
@@ -21,7 +21,6 @@ export class PdfTemplatesController extends BaseController {
|
||||
this.validationResult,
|
||||
this.deletePdfTemplate.bind(this)
|
||||
);
|
||||
|
||||
router.put(
|
||||
'/:template_id',
|
||||
[
|
||||
@@ -54,6 +53,12 @@ export class PdfTemplatesController extends BaseController {
|
||||
this.validationResult,
|
||||
this.createPdfInvoiceTemplate.bind(this)
|
||||
);
|
||||
router.post(
|
||||
'/:template_id/assign_default',
|
||||
[param('template_id').exists().isInt().toInt()],
|
||||
this.validationResult,
|
||||
this.assginPdfTemplateAsDefault.bind(this)
|
||||
);
|
||||
return router;
|
||||
}
|
||||
|
||||
@@ -139,4 +144,29 @@ export class PdfTemplatesController extends BaseController {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
|
||||
async assginPdfTemplateAsDefault(
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
) {
|
||||
const { tenantId } = req;
|
||||
const { template_id: templateId } = req.params;
|
||||
|
||||
try {
|
||||
await this.pdfTemplateApplication.assignPdfTemplateAsDefault(
|
||||
tenantId,
|
||||
Number(templateId)
|
||||
);
|
||||
return res
|
||||
.status(204)
|
||||
.send({
|
||||
id: templateId,
|
||||
message:
|
||||
'The given pdf template has been assigned as default template',
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import UnitOfWork from '../UnitOfWork';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class AssignPdfTemplateDefault {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private uow: UnitOfWork;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
public async assignDefaultTemplate(tenantId: number, templateId: number) {
|
||||
const { PdfTemplate } = this.tenancy.models(tenantId);
|
||||
|
||||
const oldPdfTempalte = await PdfTemplate.query()
|
||||
.findById(templateId)
|
||||
.throwIfNotFound();
|
||||
|
||||
return this.uow.withTransaction(
|
||||
tenantId,
|
||||
async (trx?: Knex.Transaction) => {
|
||||
await PdfTemplate.query(trx)
|
||||
.where('resource', oldPdfTempalte.resource)
|
||||
.patch({ default: false });
|
||||
|
||||
await PdfTemplate.query(trx)
|
||||
.findById(templateId)
|
||||
.patch({ default: true });
|
||||
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.pdfTemplate.onAssignedDefault,
|
||||
{
|
||||
tenantId,
|
||||
templateId,
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import { DeletePdfTemplate } from './DeletePdfTemplate';
|
||||
import { GetPdfTemplate } from './GetPdfTemplate';
|
||||
import { GetPdfTemplates } from './GetPdfTemplates';
|
||||
import { EditPdfTemplate } from './EditPdfTemplate';
|
||||
import { AssignPdfTemplateDefault } from './AssignPdfTemplateDefault';
|
||||
|
||||
@Service()
|
||||
export class PdfTemplateApplication {
|
||||
@@ -23,6 +24,9 @@ export class PdfTemplateApplication {
|
||||
@Inject()
|
||||
private editPdfTemplateService: EditPdfTemplate;
|
||||
|
||||
@Inject()
|
||||
private assignPdfTemplateDefaultService: AssignPdfTemplateDefault;
|
||||
|
||||
public async createPdfTemplate(
|
||||
tenantId: number,
|
||||
templateName: string,
|
||||
@@ -66,4 +70,14 @@ export class PdfTemplateApplication {
|
||||
) {
|
||||
return this.getPdfTemplatesService.getPdfTemplates(tenantId, query);
|
||||
}
|
||||
|
||||
public async assignPdfTemplateAsDefault(
|
||||
tenantId: number,
|
||||
templateId: number
|
||||
) {
|
||||
return this.assignPdfTemplateDefaultService.assignDefaultTemplate(
|
||||
tenantId,
|
||||
templateId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,6 +695,9 @@ export default {
|
||||
onDeleting: 'onPdfTemplateDeleting',
|
||||
onDeleted: 'onPdfTemplateDeleted',
|
||||
|
||||
onAssignedDefault: 'onPdfTemplateAssignedDefault',
|
||||
onAssigningDefault: 'onPdfTemplateAssigningDefault',
|
||||
|
||||
onInvoiceCreated: 'onInvoicePdfTemplateCreated',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -42,6 +42,11 @@ function BrandingTemplateTableRoot({
|
||||
openDrawer(drawerName, { templateId, resource });
|
||||
};
|
||||
|
||||
// Handle mark as default button click.
|
||||
const handleMarkDefaultTemplate = (template) => {
|
||||
openAlert('branding-template-mark-default', { templateId: template.id });
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
@@ -54,6 +59,7 @@ function BrandingTemplateTableRoot({
|
||||
payload={{
|
||||
onDeleteTemplate: handleDeleteTemplate,
|
||||
onEditTemplate: handleEditTemplate,
|
||||
onMarkDefaultTemplate: handleMarkDefaultTemplate,
|
||||
}}
|
||||
rowContextMenu={ActionsMenu}
|
||||
onCellClick={handleCellClick}
|
||||
|
||||
@@ -7,10 +7,19 @@ import { Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onDeleteTemplate, onEditTemplate },
|
||||
payload: { onDeleteTemplate, onEditTemplate, onMarkDefaultTemplate },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
{!original.default && (
|
||||
<>
|
||||
<MenuItem
|
||||
text={'Mark as Default'}
|
||||
onClick={safeCallback(onMarkDefaultTemplate, original)}
|
||||
/>
|
||||
<MenuDivider />
|
||||
</>
|
||||
)}
|
||||
<MenuItem
|
||||
text={'Edit Template'}
|
||||
onClick={safeCallback(onEditTemplate, original)}
|
||||
|
||||
@@ -5,6 +5,14 @@ const DeleteBrandingTemplateAlert = React.lazy(
|
||||
() => import('./DeleteBrandingTemplateAlert'),
|
||||
);
|
||||
|
||||
const MarkDefaultBrandingTemplateAlert = React.lazy(
|
||||
() => import('./MarkDefaultBrandingTemplateAlert'),
|
||||
);
|
||||
|
||||
export const BrandingTemplatesAlerts = [
|
||||
{ name: 'branding-template-delete', component: DeleteBrandingTemplateAlert },
|
||||
{
|
||||
name: 'branding-template-mark-default',
|
||||
component: MarkDefaultBrandingTemplateAlert,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { AppToaster } from '@/components';
|
||||
import { Alert, Intent } from '@blueprintjs/core';
|
||||
import { useAssignPdfTemplateAsDefault } from '@/hooks/query/pdf-templates';
|
||||
|
||||
import withAlertStoreConnect from '@/containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from '@/containers/Alert/withAlertActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Mark default branding template alert.
|
||||
*/
|
||||
function MarkDefaultBrandingTemplateAlert({
|
||||
// #ownProps
|
||||
name,
|
||||
|
||||
// #withAlertStoreConnect
|
||||
isOpen,
|
||||
payload: { templateId },
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const { mutateAsync: assignPdfTemplateAsDefault } =
|
||||
useAssignPdfTemplateAsDefault();
|
||||
|
||||
const handleConfirmDelete = () => {
|
||||
assignPdfTemplateAsDefault({ templateId })
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message:
|
||||
'The branding template has been marked as default successfully.',
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
})
|
||||
.catch((error) => {
|
||||
AppToaster.show({
|
||||
message: 'Something went wrong.',
|
||||
intent: Intent.DANGER,
|
||||
});
|
||||
closeAlert(name);
|
||||
});
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
return (
|
||||
<Alert
|
||||
cancelButtonText={intl.get('cancel')}
|
||||
confirmButtonText={'Mark as Default'}
|
||||
intent={Intent.WARNING}
|
||||
isOpen={isOpen}
|
||||
onCancel={handleCancel}
|
||||
onConfirm={handleConfirmDelete}
|
||||
>
|
||||
<p>
|
||||
Are you sure want to mark the given branding template as default
|
||||
template?
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
)(MarkDefaultBrandingTemplateAlert);
|
||||
@@ -166,3 +166,40 @@ export const useGetPdfTemplates = (
|
||||
options,
|
||||
);
|
||||
};
|
||||
|
||||
export interface AssignPdfTemplateAsDefaultValues {
|
||||
templateId: number;
|
||||
}
|
||||
|
||||
export interface AssignPdfTemplateAsDefaultResponse {}
|
||||
|
||||
export const useAssignPdfTemplateAsDefault = (
|
||||
options?: UseMutationOptions<
|
||||
AssignPdfTemplateAsDefaultResponse,
|
||||
Error,
|
||||
AssignPdfTemplateAsDefaultValues
|
||||
>,
|
||||
): UseMutationResult<
|
||||
AssignPdfTemplateAsDefaultResponse,
|
||||
Error,
|
||||
AssignPdfTemplateAsDefaultValues
|
||||
> => {
|
||||
const apiRequest = useApiRequest();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<
|
||||
AssignPdfTemplateAsDefaultResponse,
|
||||
Error,
|
||||
AssignPdfTemplateAsDefaultValues
|
||||
>(
|
||||
({ templateId }) =>
|
||||
apiRequest
|
||||
.post(`/pdf-templates/${templateId}/assign_default`)
|
||||
.then((res) => res.data),
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries([PdfTemplatesQueryKey]);
|
||||
},
|
||||
...options,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user