diff --git a/.dockerignore b/.dockerignore index 9b15d0730..72424b12a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -17,3 +17,4 @@ pacts .project *.log *.txt +.env diff --git a/app/assets/sass/application.scss b/app/assets/sass/application.scss index e6b0f87f9..13ebfcb2d 100644 --- a/app/assets/sass/application.scss +++ b/app/assets/sass/application.scss @@ -57,5 +57,4 @@ $govuk-suppressed-warnings: ( @import "components/system-messages"; @import "components/spinner"; @import "components/service-settings"; -@import "components/hint-and-body-width"; @import "components/fieldset-legend-width"; diff --git a/app/assets/sass/components/hint-and-body-width.scss b/app/assets/sass/components/hint-and-body-width.scss deleted file mode 100644 index 1908573d5..000000000 --- a/app/assets/sass/components/hint-and-body-width.scss +++ /dev/null @@ -1,4 +0,0 @@ -.hint-and-body-width { - max-width: 630px; - word-wrap: break-word; -} diff --git a/app/assets/sass/components/service-settings.scss b/app/assets/sass/components/service-settings.scss index c9e510140..63333dac0 100644 --- a/app/assets/sass/components/service-settings.scss +++ b/app/assets/sass/components/service-settings.scss @@ -34,6 +34,10 @@ @include govuk-typography-weight-bold(); } +.service-settings-pane { + max-width: 630px; +} + .service-settings-inset-text--grey { background-color: govuk-colour("light-grey"); } @@ -45,6 +49,9 @@ } .task-list { + .govuk-task-list__name-and-hint { + width: 70% !important; + } a:visited, a:link { color: govuk-colour("blue"); } diff --git a/app/assets/sass/components/system-messages.scss b/app/assets/sass/components/system-messages.scss index afa257959..407e6a6d2 100644 --- a/app/assets/sass/components/system-messages.scss +++ b/app/assets/sass/components/system-messages.scss @@ -20,6 +20,8 @@ .system-message__text { flex: 1; + overflow-wrap: break-word; + overflow: hidden; } .system-message__icon--success { diff --git a/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.js b/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.js index daf3e785b..b9c7daa92 100644 --- a/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.js +++ b/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.js @@ -2,7 +2,8 @@ const { response } = require('@utils/response') const formatSimplifiedAccountPathsFor = require('@utils/simplified-account/format/format-simplified-account-paths-for') const paths = require('@root/paths') const { updateCustomParagraphByServiceIdAndAccountType } = require('@services/email.service') -const { validateOptionalField } = require('@utils/validation/server-side-form-validations') +const { body, validationResult } = require('express-validator') +const formatValidationErrors = require('@utils/simplified-account/format/format-validation-errors') const CUSTOM_PARAGRAPH_MAX_LENGTH = 5000 function get (req, res) { @@ -10,7 +11,7 @@ function get (req, res) { const service = req.service response(req, res, 'simplified-account/settings/email-notifications/custom-paragraph', { - customParagraphText: account.rawResponse.email_notifications.PAYMENT_CONFIRMED.template_body, + customParagraph: account.rawResponse.email_notifications.PAYMENT_CONFIRMED.template_body, serviceName: service.name, backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.templates, service.externalId, account.type), @@ -22,22 +23,31 @@ function get (req, res) { async function postEditCustomParagraph (req, res) { const serviceExternalId = req.service.externalId const accountType = req.account.type - const customParagraph = req.body['custom-paragraph'] + const validations = [ + body('customParagraph').trim().isLength({ max: CUSTOM_PARAGRAPH_MAX_LENGTH }).withMessage(`Custom paragraph name must be ${CUSTOM_PARAGRAPH_MAX_LENGTH} characters or fewer`) + ] - const validationResult = validateOptionalField(customParagraph, CUSTOM_PARAGRAPH_MAX_LENGTH, 'custom paragraph') - if (!validationResult.valid) { + await Promise.all(validations.map(validation => validation.run(req))) + const errors = validationResult(req) + + if (!errors.isEmpty()) { + const formattedErrors = formatValidationErrors(errors) return response(req, res, 'simplified-account/settings/email-notifications/custom-paragraph', { errors: { - customParagraph: validationResult.message + summary: formattedErrors.errorSummary, + formErrors: formattedErrors.formErrors }, - customParagraphText: customParagraph, + customParagraph: req.body.customParagraph, serviceName: req.service.name, backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.templates, + req.service.externalId, accountType), + removeCustomParagraphLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.removeCustomParagraph, req.service.externalId, accountType) }) } - await updateCustomParagraphByServiceIdAndAccountType(serviceExternalId, accountType, customParagraph) + const newCustomParagraph = req.body.customParagraph.trim() + await updateCustomParagraphByServiceIdAndAccountType(serviceExternalId, accountType, newCustomParagraph) req.flash('messages', { state: 'success', icon: '✓', heading: 'Custom paragraph updated' }) res.redirect(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.templates, serviceExternalId, accountType)) @@ -48,6 +58,7 @@ async function postRemoveCustomParagraph (req, res) { const accountType = req.account.type await updateCustomParagraphByServiceIdAndAccountType(serviceExternalId, accountType, '') + req.flash('messages', { state: 'success', icon: '✓', heading: 'Custom paragraph removed' }) res.redirect(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.emailNotifications.templates, serviceExternalId, accountType)) } diff --git a/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.test.js b/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.test.js index c79738037..8f6a65965 100644 --- a/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.test.js +++ b/app/controllers/simplified-account/settings/email-notifications/templates/custom-paragraph.controller.test.js @@ -79,7 +79,7 @@ describe('Controller: settings/email-notifications/templates/custom-paragraph', }) it('should pass context data to the response method', () => { - expect(responseStub.args[0][3]).to.have.property('customParagraphText').to.equal('Do this next') + expect(responseStub.args[0][3]).to.have.property('customParagraph').to.equal('Do this next') expect(responseStub.args[0][3]).to.have.property('serviceName').to.equal(SERVICE_NAME) expect(responseStub.args[0][3]).to.have.property('backLink').to.contain(paths.simplifiedAccount.settings.emailNotifications.index) }) @@ -87,7 +87,7 @@ describe('Controller: settings/email-notifications/templates/custom-paragraph', describe('postRemoveCustomParagraph', () => { before(() => { - const body = { 'custom-paragraph': 'a test custom paragraph' } + const body = { customParagraph: 'a test custom paragraph' } setupTest(body) customParagraphController.postRemoveCustomParagraph(req, res) }) @@ -105,7 +105,7 @@ describe('Controller: settings/email-notifications/templates/custom-paragraph', describe('postEditCustomParagraph', () => { before(() => { - const body = { 'custom-paragraph': 'a test custom paragraph' } + const body = { customParagraph: 'a test custom paragraph' } setupTest(body) customParagraphController.postEditCustomParagraph(req, res) }) @@ -125,7 +125,7 @@ describe('Controller: settings/email-notifications/templates/custom-paragraph', describe('with validation error', () => { const invalidText = 'hi'.repeat(5000) before(() => { - const body = { 'custom-paragraph': invalidText } + const body = { customParagraph: invalidText } setupTest(body) customParagraphController.postEditCustomParagraph(req, res) }) @@ -141,12 +141,23 @@ describe('Controller: settings/email-notifications/templates/custom-paragraph', }) it('should pass context data to the response method', () => { - expect(responseStub.args[0][3]).to.have.property('errors').to.deep.equal({ - customParagraph: 'Custom paragraph must be 5000 characters or fewer' - }) - expect(responseStub.args[0][3]).to.have.property('customParagraphText').to.equal(invalidText) + const errors = responseStub.args[0][3].errors + expect(errors.summary).to.deep.equal( + [ + { + text: 'Custom paragraph name must be 5000 characters or fewer', + href: '#custom-paragraph' + } + ]) + expect(errors.formErrors).to.deep.equal( + { + customParagraph: 'Custom paragraph name must be 5000 characters or fewer' + } + ) + expect(responseStub.args[0][3]).to.have.property('customParagraph').to.equal(invalidText) expect(responseStub.args[0][3]).to.have.property('serviceName').to.equal(SERVICE_NAME) expect(responseStub.args[0][3]).to.have.property('backLink').to.contain(paths.simplifiedAccount.settings.emailNotifications.index) + expect(responseStub.args[0][3]).to.have.property('removeCustomParagraphLink').to.contain(paths.simplifiedAccount.settings.emailNotifications.removeCustomParagraph) }) }) }) diff --git a/app/controllers/simplified-account/settings/service-name/service-name.controller.js b/app/controllers/simplified-account/settings/service-name/service-name.controller.js index 0774bc9e7..c31d782e4 100644 --- a/app/controllers/simplified-account/settings/service-name/service-name.controller.js +++ b/app/controllers/simplified-account/settings/service-name/service-name.controller.js @@ -36,11 +36,11 @@ function getEditServiceName (req, res) { async function postEditServiceName (req, res) { const editCy = req.body.cy === 'true' const validations = [ - body('serviceNameInput').trim().isLength({ max: SERVICE_NAME_MAX_LENGTH }).withMessage(`Service name must be ${SERVICE_NAME_MAX_LENGTH} characters or fewer`) + body('serviceName').trim().isLength({ max: SERVICE_NAME_MAX_LENGTH }).withMessage(`Service name must be ${SERVICE_NAME_MAX_LENGTH} characters or fewer`) ] // we don't check presence for welsh names if (!editCy) { - validations.push(body('serviceNameInput').trim().notEmpty().withMessage('Service name is required')) + validations.push(body('serviceName').trim().notEmpty().withMessage('Service name is required')) } await Promise.all(validations.map(validation => validation.run(req))) @@ -53,13 +53,14 @@ async function postEditServiceName (req, res) { formErrors: formattedErrors.formErrors }, editCy, - serviceName: req.body.serviceNameInput, + serviceName: req.body.serviceName, backLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.index, req.service.externalId, req.account.type), - submitLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.edit, req.service.externalId, req.account.type) + submitLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.edit, req.service.externalId, req.account.type), + removeCyLink: formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.removeCy, req.service.externalId, req.account.type) }) } - const newServiceName = req.body.serviceNameInput.trim() + const newServiceName = req.body.serviceName.trim() editCy ? await updateServiceName(req.service.externalId, req.service.serviceName.en, newServiceName) : await updateServiceName(req.service.externalId, newServiceName, req.service.serviceName.cy) res.redirect(formatSimplifiedAccountPathsFor(paths.simplifiedAccount.settings.serviceName.index, req.service.externalId, req.account.type)) } diff --git a/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js b/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js index 858b3e733..0ad1ba716 100644 --- a/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js +++ b/app/controllers/simplified-account/settings/service-name/service-name.controller.test.js @@ -123,7 +123,7 @@ describe('Controller: settings/service-name', () => { before(() => { setupTest('postEditServiceName', {}, { body: { - serviceNameInput: 'New English Name', + serviceName: 'New English Name', cy: 'false' } }) @@ -143,7 +143,7 @@ describe('Controller: settings/service-name', () => { before(() => { setupTest('postEditServiceName', {}, { body: { - serviceNameInput: 'Enw Cymraeg newydd', + serviceName: 'Enw Cymraeg newydd', cy: 'true' } }) @@ -186,6 +186,7 @@ describe('Controller: settings/service-name', () => { expect(responseStub.calledOnce).to.be.true // eslint-disable-line const [, , template, context] = responseStub.args[0] expect(template).to.equal('simplified-account/settings/service-name/edit-service-name') + expect(context).to.have.property('removeCyLink').to.contain(paths.simplifiedAccount.settings.serviceName.removeCy) expect(context.errors).to.deep.equal({ summary: ['Error summary'], formErrors: { serviceNameInput: 'Error message' } diff --git a/app/simplified-account-routes.js b/app/simplified-account-routes.js index 8b477b27f..1228fe226 100644 --- a/app/simplified-account-routes.js +++ b/app/simplified-account-routes.js @@ -75,18 +75,20 @@ simplifiedAccount.get(paths.simplifiedAccount.settings.cardTypes.index, permissi // stripe details const stripeDetailsPath = paths.simplifiedAccount.settings.stripeDetails const stripeDetailsRouter = new Router({ mergeParams: true }) - .use(enforcePaymentProviderType(STRIPE), permission('stripe-account-details:update')) + .use(enforceLiveAccountOnly, enforcePaymentProviderType(STRIPE), permission('stripe-account-details:update')) stripeDetailsRouter.get(stripeDetailsPath.index, serviceSettingsController.stripeDetails.get) + stripeDetailsRouter.get(stripeDetailsPath.bankAccount, serviceSettingsController.stripeDetails.bankAccount.get) stripeDetailsRouter.post(stripeDetailsPath.bankAccount, serviceSettingsController.stripeDetails.bankAccount.post) -// -- new stuff + stripeDetailsRouter.get(stripeDetailsPath.companyNumber, serviceSettingsController.stripeDetails.companyNumber.get) stripeDetailsRouter.post(stripeDetailsPath.companyNumber, serviceSettingsController.stripeDetails.companyNumber.post) + stripeDetailsRouter.get(stripeDetailsPath.organisationDetails.index, serviceSettingsController.stripeDetails.organisationDetails.get) stripeDetailsRouter.post(stripeDetailsPath.organisationDetails.index, serviceSettingsController.stripeDetails.organisationDetails.post) stripeDetailsRouter.get(stripeDetailsPath.organisationDetails.update, serviceSettingsController.stripeDetails.organisationDetails.update.get) stripeDetailsRouter.post(stripeDetailsPath.organisationDetails.update, serviceSettingsController.stripeDetails.organisationDetails.update.post) -// -- responsible person + stripeDetailsRouter.get(stripeDetailsPath.responsiblePerson.index, serviceSettingsController.stripeDetails.responsiblePerson.get) stripeDetailsRouter.post(stripeDetailsPath.responsiblePerson.index, serviceSettingsController.stripeDetails.responsiblePerson.post) stripeDetailsRouter.get(stripeDetailsPath.responsiblePerson.homeAddress, serviceSettingsController.stripeDetails.responsiblePerson.homeAddress.get) @@ -95,13 +97,16 @@ stripeDetailsRouter.get(stripeDetailsPath.responsiblePerson.contactDetails, serv stripeDetailsRouter.post(stripeDetailsPath.responsiblePerson.contactDetails, serviceSettingsController.stripeDetails.responsiblePerson.contactDetails.post) stripeDetailsRouter.get(stripeDetailsPath.responsiblePerson.checkYourAnswers, serviceSettingsController.stripeDetails.responsiblePerson.checkYourAnswers.get) stripeDetailsRouter.post(stripeDetailsPath.responsiblePerson.checkYourAnswers, serviceSettingsController.stripeDetails.responsiblePerson.checkYourAnswers.post) -// -- + stripeDetailsRouter.get(stripeDetailsPath.vatNumber, serviceSettingsController.stripeDetails.vatNumber.get) stripeDetailsRouter.post(stripeDetailsPath.vatNumber, serviceSettingsController.stripeDetails.vatNumber.post) + stripeDetailsRouter.get(stripeDetailsPath.director, serviceSettingsController.stripeDetails.director.get) stripeDetailsRouter.post(stripeDetailsPath.director, serviceSettingsController.stripeDetails.director.post) + stripeDetailsRouter.get(stripeDetailsPath.governmentEntityDocument, serviceSettingsController.stripeDetails.governmentEntityDocument.get) stripeDetailsRouter.post(stripeDetailsPath.governmentEntityDocument, [upload.single(GOV_ENTITY_DOC_FORM_FIELD_NAME), ...serviceSettingsController.stripeDetails.governmentEntityDocument.post]) + simplifiedAccount.use(stripeDetailsRouter) module.exports = simplifiedAccount diff --git a/app/views/simplified-account/settings/email-notifications/collect-email-page.njk b/app/views/simplified-account/settings/email-notifications/collect-email-page.njk index c7d7960b5..c8696efbd 100644 --- a/app/views/simplified-account/settings/email-notifications/collect-email-page.njk +++ b/app/views/simplified-account/settings/email-notifications/collect-email-page.njk @@ -1,74 +1,61 @@ {% extends "../settings-layout.njk" %} {% block settingsPageTitle %} - Email notifications - {{currentService.name}} {{currentGatewayAccount.full_type}} + Email notifications - {{ currentService.name }} {{ currentGatewayAccount.full_type }} {% endblock %} {% block settingsContent %} +
- - + {{ govukButton({ + text: 'Save changes' + }) }} + {% endblock %} diff --git a/app/views/simplified-account/settings/email-notifications/custom-paragraph.njk b/app/views/simplified-account/settings/email-notifications/custom-paragraph.njk index 5cbc1993e..20d277f5b 100644 --- a/app/views/simplified-account/settings/email-notifications/custom-paragraph.njk +++ b/app/views/simplified-account/settings/email-notifications/custom-paragraph.njk @@ -5,38 +5,20 @@ {% endblock %} {% block settingsContent %} - - {{ govukBackLink({ - text: "Back", - href: backLink - }) }} - - {% if errors %} - {{ govukErrorSummary({ - titleText: "There is a problem", - errorList: [ - { - text: errors.customParagraph, - href: "#" - } - ] - }) }} - {% endif %} -- You can add a custom paragraph to give users more information about your service. For example, you can tell users: + You can add a custom paragraph to give users more information about your service. For example, you can tell users:
- If you include a link in your custom paragraph you must: + If you include a link in your custom paragraph you must:
- Do not include contact details in the custom paragraph. If you want to provide contact details, you must link to a page on a gov.uk domain. + Do not include contact details in the custom paragraph. If you want to provide contact details, you must link to a + page on a gov.uk domain.
- You should not: + You should not:
View email templates and add a custom paragraph
+ {% else %} - + {% endif %} {% endblock %} diff --git a/app/views/simplified-account/settings/email-notifications/payment-confirmation-email-toggle.njk b/app/views/simplified-account/settings/email-notifications/payment-confirmation-email-toggle.njk index 5ed0a43cd..c5af732ca 100644 --- a/app/views/simplified-account/settings/email-notifications/payment-confirmation-email-toggle.njk +++ b/app/views/simplified-account/settings/email-notifications/payment-confirmation-email-toggle.njk @@ -5,15 +5,8 @@ {% endblock %} {% block settingsContent %} - - {{ govukBackLink({ - text: "Back", - href: backLink - }) }} - {% endblock %} diff --git a/app/views/simplified-account/settings/email-notifications/refund-email-toggle.njk b/app/views/simplified-account/settings/email-notifications/refund-email-toggle.njk index 8f554e1c3..b059f5156 100644 --- a/app/views/simplified-account/settings/email-notifications/refund-email-toggle.njk +++ b/app/views/simplified-account/settings/email-notifications/refund-email-toggle.njk @@ -5,12 +5,6 @@ {% endblock %} {% block settingsContent %} - - {{ govukBackLink({ - text: "Back", - href: backLink - }) }} -