mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +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.validationResult,
|
||||||
this.deletePdfTemplate.bind(this)
|
this.deletePdfTemplate.bind(this)
|
||||||
);
|
);
|
||||||
|
|
||||||
router.put(
|
router.put(
|
||||||
'/:template_id',
|
'/:template_id',
|
||||||
[
|
[
|
||||||
@@ -54,6 +53,12 @@ export class PdfTemplatesController extends BaseController {
|
|||||||
this.validationResult,
|
this.validationResult,
|
||||||
this.createPdfInvoiceTemplate.bind(this)
|
this.createPdfInvoiceTemplate.bind(this)
|
||||||
);
|
);
|
||||||
|
router.post(
|
||||||
|
'/:template_id/assign_default',
|
||||||
|
[param('template_id').exists().isInt().toInt()],
|
||||||
|
this.validationResult,
|
||||||
|
this.assginPdfTemplateAsDefault.bind(this)
|
||||||
|
);
|
||||||
return router;
|
return router;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,4 +144,29 @@ export class PdfTemplatesController extends BaseController {
|
|||||||
next(error);
|
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 { GetPdfTemplate } from './GetPdfTemplate';
|
||||||
import { GetPdfTemplates } from './GetPdfTemplates';
|
import { GetPdfTemplates } from './GetPdfTemplates';
|
||||||
import { EditPdfTemplate } from './EditPdfTemplate';
|
import { EditPdfTemplate } from './EditPdfTemplate';
|
||||||
|
import { AssignPdfTemplateDefault } from './AssignPdfTemplateDefault';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class PdfTemplateApplication {
|
export class PdfTemplateApplication {
|
||||||
@@ -23,6 +24,9 @@ export class PdfTemplateApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private editPdfTemplateService: EditPdfTemplate;
|
private editPdfTemplateService: EditPdfTemplate;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private assignPdfTemplateDefaultService: AssignPdfTemplateDefault;
|
||||||
|
|
||||||
public async createPdfTemplate(
|
public async createPdfTemplate(
|
||||||
tenantId: number,
|
tenantId: number,
|
||||||
templateName: string,
|
templateName: string,
|
||||||
@@ -66,4 +70,14 @@ export class PdfTemplateApplication {
|
|||||||
) {
|
) {
|
||||||
return this.getPdfTemplatesService.getPdfTemplates(tenantId, query);
|
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',
|
onDeleting: 'onPdfTemplateDeleting',
|
||||||
onDeleted: 'onPdfTemplateDeleted',
|
onDeleted: 'onPdfTemplateDeleted',
|
||||||
|
|
||||||
|
onAssignedDefault: 'onPdfTemplateAssignedDefault',
|
||||||
|
onAssigningDefault: 'onPdfTemplateAssigningDefault',
|
||||||
|
|
||||||
onInvoiceCreated: 'onInvoicePdfTemplateCreated',
|
onInvoiceCreated: 'onInvoicePdfTemplateCreated',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ function BrandingTemplateTableRoot({
|
|||||||
openDrawer(drawerName, { templateId, resource });
|
openDrawer(drawerName, { templateId, resource });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle mark as default button click.
|
||||||
|
const handleMarkDefaultTemplate = (template) => {
|
||||||
|
openAlert('branding-template-mark-default', { templateId: template.id });
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -54,6 +59,7 @@ function BrandingTemplateTableRoot({
|
|||||||
payload={{
|
payload={{
|
||||||
onDeleteTemplate: handleDeleteTemplate,
|
onDeleteTemplate: handleDeleteTemplate,
|
||||||
onEditTemplate: handleEditTemplate,
|
onEditTemplate: handleEditTemplate,
|
||||||
|
onMarkDefaultTemplate: handleMarkDefaultTemplate,
|
||||||
}}
|
}}
|
||||||
rowContextMenu={ActionsMenu}
|
rowContextMenu={ActionsMenu}
|
||||||
onCellClick={handleCellClick}
|
onCellClick={handleCellClick}
|
||||||
|
|||||||
@@ -7,10 +7,19 @@ import { Intent, Menu, MenuDivider, MenuItem } from '@blueprintjs/core';
|
|||||||
*/
|
*/
|
||||||
export function ActionsMenu({
|
export function ActionsMenu({
|
||||||
row: { original },
|
row: { original },
|
||||||
payload: { onDeleteTemplate, onEditTemplate },
|
payload: { onDeleteTemplate, onEditTemplate, onMarkDefaultTemplate },
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
|
{!original.default && (
|
||||||
|
<>
|
||||||
|
<MenuItem
|
||||||
|
text={'Mark as Default'}
|
||||||
|
onClick={safeCallback(onMarkDefaultTemplate, original)}
|
||||||
|
/>
|
||||||
|
<MenuDivider />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={'Edit Template'}
|
text={'Edit Template'}
|
||||||
onClick={safeCallback(onEditTemplate, original)}
|
onClick={safeCallback(onEditTemplate, original)}
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ const DeleteBrandingTemplateAlert = React.lazy(
|
|||||||
() => import('./DeleteBrandingTemplateAlert'),
|
() => import('./DeleteBrandingTemplateAlert'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const MarkDefaultBrandingTemplateAlert = React.lazy(
|
||||||
|
() => import('./MarkDefaultBrandingTemplateAlert'),
|
||||||
|
);
|
||||||
|
|
||||||
export const BrandingTemplatesAlerts = [
|
export const BrandingTemplatesAlerts = [
|
||||||
{ name: 'branding-template-delete', component: DeleteBrandingTemplateAlert },
|
{ 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,
|
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