1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2024-11-29 10:24:20 +01:00

(ui) add alert on leaving plan form with unsaved changes

This commit is contained in:
Sylvain 2023-03-09 15:57:46 +01:00
parent 2b8a7008bd
commit dad3babbe4
6 changed files with 27 additions and 16 deletions

View File

@ -26,6 +26,8 @@ import { PlanPricingForm } from './plan-pricing-form';
import { AdvancedAccountingForm } from '../accounting/advanced-accounting-form'; import { AdvancedAccountingForm } from '../accounting/advanced-accounting-form';
import { FabTabs } from '../base/fab-tabs'; import { FabTabs } from '../base/fab-tabs';
import { PlanLimitForm } from './plan-limit-form'; import { PlanLimitForm } from './plan-limit-form';
import { UnsavedFormAlert } from '../form/unsaved-form-alert';
import { UIRouter } from '@uirouter/angularjs';
declare const Application: IApplication; declare const Application: IApplication;
@ -35,12 +37,13 @@ interface PlanFormProps {
onError: (message: string) => void, onError: (message: string) => void,
onSuccess: (message: string) => void, onSuccess: (message: string) => void,
beforeSubmit?: (data: Plan) => void, beforeSubmit?: (data: Plan) => void,
uiRouter: UIRouter
} }
/** /**
* Form to edit or create subscription plans * Form to edit or create subscription plans
*/ */
export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuccess, beforeSubmit }) => { export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuccess, beforeSubmit, uiRouter }) => {
const { handleSubmit, register, control, formState, setValue } = useForm<Plan>({ defaultValues: { ...plan } }); const { handleSubmit, register, control, formState, setValue } = useForm<Plan>({ defaultValues: { ...plan } });
const output = useWatch<Plan>({ control }); // eslint-disable-line const output = useWatch<Plan>({ control }); // eslint-disable-line
const { t } = useTranslation('admin'); const { t } = useTranslation('admin');
@ -316,6 +319,7 @@ export const PlanForm: React.FC<PlanFormProps> = ({ action, plan, onError, onSuc
</header> </header>
<form onSubmit={handleSubmit(onSubmit)}> <form onSubmit={handleSubmit(onSubmit)}>
<UnsavedFormAlert uiRouter={uiRouter} formState={formState} />
<FabTabs tabs={[ <FabTabs tabs={[
{ {
id: 'settings', id: 'settings',
@ -350,4 +354,4 @@ const PlanFormWrapper: React.FC<PlanFormProps> = (props) => {
</Loader> </Loader>
); );
}; };
Application.Components.component('planForm', react2angular(PlanFormWrapper, ['action', 'plan', 'onError', 'onSuccess'])); Application.Components.component('planForm', react2angular(PlanFormWrapper, ['action', 'plan', 'onError', 'onSuccess', 'uiRouter']));

View File

@ -21,11 +21,14 @@
/** /**
* Controller used in the plan creation form * Controller used in the plan creation form
*/ */
Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal', 'groups', 'prices', 'partners', 'CSRF', '$state', 'growl', '_t', 'planCategories', Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal', 'groups', 'prices', 'partners', 'CSRF', '$state', 'growl', '_t', '$uiRouter',
function ($scope, $uibModal, groups, prices, partners, CSRF, $state, growl, _t, planCategories) { function ($scope, $uibModal, groups, prices, partners, CSRF, $state, growl, _t, $uiRouter) {
// protection against request forgery // protection against request forgery
CSRF.setMetaTags(); CSRF.setMetaTags();
// the following item is used by the UnsavedFormAlert component to detect a page change
$scope.uiRouter = $uiRouter;
/** /**
* Shows an error message forwarded from a child component * Shows an error message forwarded from a child component
*/ */
@ -46,13 +49,16 @@ Application.Controllers.controller('NewPlanController', ['$scope', '$uibModal',
/** /**
* Controller used in the plan edition form * Controller used in the plan edition form
*/ */
Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'spaces', 'prices', 'partners', 'CSRF', '$state', '$transition$', 'growl', '$filter', '_t', 'Plan', 'planCategories', Application.Controllers.controller('EditPlanController', ['$scope', 'groups', 'plans', 'planPromise', 'machines', 'spaces', 'prices', 'partners', 'CSRF', '$state', '$transition$', 'growl', '$filter', '_t', '$uiRouter',
function ($scope, groups, plans, planPromise, machines, spaces, prices, partners, CSRF, $state, $transition$, growl, $filter, _t, Plan, planCategories) { function ($scope, groups, plans, planPromise, machines, spaces, prices, partners, CSRF, $state, $transition$, growl, $filter, _t, $uiRouter) {
// protection against request forgery // protection against request forgery
CSRF.setMetaTags(); CSRF.setMetaTags();
$scope.suscriptionPlan = cleanPlan(planPromise); $scope.suscriptionPlan = cleanPlan(planPromise);
// the following item is used by the UnsavedFormAlert component to detect a page change
$scope.uiRouter = $uiRouter;
/** /**
* Shows an error message forwarded from a child component * Shows an error message forwarded from a child component
*/ */

View File

@ -44,7 +44,7 @@ export interface Plan {
partnership?: boolean, partnership?: boolean,
partners?: Array<Partner>, partners?: Array<Partner>,
advanced_accounting_attributes?: AdvancedAccounting, advanced_accounting_attributes?: AdvancedAccounting,
plan_limitations_attributes: Array<PlanLimitation> plan_limitations_attributes?: Array<PlanLimitation>
} }
export interface PlansDuration { export interface PlansDuration {

View File

@ -13,4 +13,4 @@
</div> </div>
</section> </section>
<plan-form action="'update'" plan="suscriptionPlan" on-error="onError" on-success="onSuccess"></plan-form> <plan-form action="'update'" plan="suscriptionPlan" on-error="onError" on-success="onSuccess" ui-router="uiRouter"></plan-form>

View File

@ -14,4 +14,4 @@
</div> </div>
</section> </section>
<plan-form action="'create'" on-error="onError" on-success="onSuccess"></plan-form> <plan-form action="'create'" on-error="onError" on-success="onSuccess" ui-router="uiRouter"></plan-form>

View File

@ -7,6 +7,7 @@ import userEvent from '@testing-library/user-event';
import plans from '../../__fixtures__/plans'; import plans from '../../__fixtures__/plans';
import machines from '../../__fixtures__/machines'; import machines from '../../__fixtures__/machines';
import { tiptapEvent } from '../../__lib__/tiptap'; import { tiptapEvent } from '../../__lib__/tiptap';
import { uiRouter } from '../../__lib__/ui-router';
describe('PlanForm', () => { describe('PlanForm', () => {
const onError = jest.fn(); const onError = jest.fn();
@ -14,7 +15,7 @@ describe('PlanForm', () => {
const beforeSubmit = jest.fn(); const beforeSubmit = jest.fn();
test('render create PlanForm', async () => { test('render create PlanForm', async () => {
render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} />); render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ }));
expect(screen.getByLabelText(/app.admin.plan_form.name/)).toBeInTheDocument(); expect(screen.getByLabelText(/app.admin.plan_form.name/)).toBeInTheDocument();
expect(screen.getByLabelText(/app.admin.plan_form.transversal/)).toBeInTheDocument(); expect(screen.getByLabelText(/app.admin.plan_form.transversal/)).toBeInTheDocument();
@ -35,7 +36,7 @@ describe('PlanForm', () => {
}); });
test('create new plan', async () => { test('create new plan', async () => {
render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} beforeSubmit={beforeSubmit} />); render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} beforeSubmit={beforeSubmit} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ }));
const user = userEvent.setup(); const user = userEvent.setup();
// base_name // base_name
@ -98,7 +99,7 @@ describe('PlanForm', () => {
test('render update PlanForm with partner', async () => { test('render update PlanForm with partner', async () => {
const plan = plans[1]; const plan = plans[1];
render(<PlanForm action="update" plan={plan} onError={onError} onSuccess={onSuccess} />); render(<PlanForm action="update" plan={plan} onError={onError} onSuccess={onSuccess} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_pricing_form.copy_prices_from/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_pricing_form.copy_prices_from/ }));
expect(screen.getByLabelText(/app.admin.plan_form.name/)).toBeInTheDocument(); expect(screen.getByLabelText(/app.admin.plan_form.name/)).toBeInTheDocument();
expect(screen.queryByLabelText(/app.admin.plan_form.transversal/)).toBeNull(); expect(screen.queryByLabelText(/app.admin.plan_form.transversal/)).toBeNull();
@ -123,14 +124,14 @@ describe('PlanForm', () => {
}); });
test('selecting transversal plan disables group select', async () => { test('selecting transversal plan disables group select', async () => {
render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} />); render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ }));
fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.transversal/ })); fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.transversal/ }));
expect(screen.queryByRole('combobox', { name: /app.admin.plan_form.group/ })).toBeNull(); expect(screen.queryByRole('combobox', { name: /app.admin.plan_form.group/ })).toBeNull();
}); });
test('selecting partner plan shows partner selection', async () => { test('selecting partner plan shows partner selection', async () => {
render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} />); render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ }));
fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.partner_plan/ })); fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.partner_plan/ }));
expect(screen.getByLabelText(/app.admin.plan_form.notified_partner/)); expect(screen.getByLabelText(/app.admin.plan_form.notified_partner/));
@ -138,7 +139,7 @@ describe('PlanForm', () => {
}); });
test('creating a new partner selects him by default', async () => { test('creating a new partner selects him by default', async () => {
render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} />); render(<PlanForm action="create" onError={onError} onSuccess={onSuccess} uiRouter={uiRouter} />);
await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ })); await waitFor(() => screen.getByRole('combobox', { name: /app.admin.plan_form.group/ }));
fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.partner_plan/ })); fireEvent.click(screen.getByRole('switch', { name: /app.admin.plan_form.partner_plan/ }));
fireEvent.click(screen.getByRole('button', { name: /app.admin.plan_form.new_user/ })); fireEvent.click(screen.getByRole('button', { name: /app.admin.plan_form.new_user/ }));
@ -157,7 +158,7 @@ describe('PlanForm', () => {
test('update plan prices', async () => { test('update plan prices', async () => {
const plan = plans[1]; const plan = plans[1];
const machine = machines[1]; const machine = machines[1];
render(<PlanForm action="update" plan={plan} onError={onError} onSuccess={onSuccess} beforeSubmit={beforeSubmit} />); render(<PlanForm action="update" plan={plan} onError={onError} onSuccess={onSuccess} beforeSubmit={beforeSubmit} uiRouter={uiRouter} />);
await waitFor(() => screen.getByLabelText(new RegExp(machine.name))); await waitFor(() => screen.getByLabelText(new RegExp(machine.name)));
// update machine price // update machine price
fireEvent.change(screen.getByLabelText(new RegExp(machine.name)), { target: { value: 42.42 } }); fireEvent.change(screen.getByLabelText(new RegExp(machine.name)), { target: { value: 42.42 } });