From b99512066bd78a617e9694d273c9a6674edab364 Mon Sep 17 00:00:00 2001 From: rdbsolutions Date: Thu, 5 May 2022 11:04:05 +0100 Subject: [PATCH] edit a partial completed appeal --- .env.local | 34 +-- actions/index.js | 70 +++++- components/case/summaryTypes/pinswg_dnsid.js | 2 +- components/elements/index.js | 51 +++- components/myportal/topthree.js | 7 +- components/newappeal/buildsection.js | 236 ++++++++++++------ components/timeout/index.js | 3 +- components/utils/index.js | 32 +++ .../endpoint/getbasicpartsaveddetails_api.js | 50 ++++ pages/api/endpoint/getbasicsearch_api.js | 2 +- pages/api/endpoint/getpartsavedappeal_api.js | 63 +++++ .../endpoint/getportalmoduledetails_api.js | 6 +- pages/myportal/[appealtypes].js | 17 ++ styles/sass/welshgov/_application.scss | 30 +++ 14 files changed, 497 insertions(+), 106 deletions(-) create mode 100644 pages/api/endpoint/getbasicpartsaveddetails_api.js create mode 100644 pages/api/endpoint/getpartsavedappeal_api.js diff --git a/.env.local b/.env.local index 627bb41d..7c6dfc02 100644 --- a/.env.local +++ b/.env.local @@ -4,7 +4,7 @@ ACCESS_TOKEN_ENDPOINT = https://login.microsoftonline.com/ //ESNR Tenant TENANT = a8d860de-d5e1-45af-8376-d93f02e6b502 //WGO Tenant -TENANT = 9ac1a5ab-cd58-409e-a1b6-6b10422a68f5 +//TENANT = 9ac1a5ab-cd58-409e-a1b6-6b10422a68f5 GRANT_TYPE = "client_credentials" HASHKEY = 028ffbae928d7191c0017f298260995f901d78eabde1436c0af0423459cc3715832136d84f68818d3a96399467b52143e273d4f3bf4ae190848891535421cd6c @@ -12,27 +12,27 @@ NEXT_PUBLIC_HASHKEY = 028ffbae928d7191c0017f298260995f901d78eabde1436c0af0423459 //Dev oauth ESNR -//CLIENT_ID = e082def0-3453-461f-9a5e-f4ab127dc015 -//CLIENT_SECRET = qW.7Q~TSr3sZmLEnJQJPXEdOckb_yo2t1yJmY -//RELAY_ROOT = https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/ -//RELAYURI = "dev-pedw-ns.servicebus.windows.net" -//RELAYPATH = "dev-pedw-hc" +CLIENT_ID = e082def0-3453-461f-9a5e-f4ab127dc015 +CLIENT_SECRET = qW.7Q~TSr3sZmLEnJQJPXEdOckb_yo2t1yJmY +RELAY_ROOT = https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/ +RELAYURI = "dev-pedw-ns.servicebus.windows.net" +RELAYPATH = "dev-pedw-hc" //Test Oauth ESNR -CLIENT_ID = 6619ad85-c908-490d-811b-de8c93c0f2aa -CLIENT_SECRET = qKo7Q~5QvLEplbwnBbint_I5lXqx_-kDm8Vj- -RELAY_ROOT = https://test-pedw-ns.servicebus.windows.net/test-pedw-hc/ -RELAYURI = "test-pedw-ns.servicebus.windows.net" -RELAYPATH = "test-pedw-hc" +//CLIENT_ID = 6619ad85-c908-490d-811b-de8c93c0f2aa +//CLIENT_SECRET = qKo7Q~5QvLEplbwnBbint_I5lXqx_-kDm8Vj- +//RELAY_ROOT = https://test-pedw-ns.servicebus.windows.net/test-pedw-hc/ +//RELAYURI = "test-pedw-ns.servicebus.windows.net" +//RELAYPATH = "test-pedw-hc" //Preprod Oauth WGO -CLIENT_ID = ab6c4678-b31a-4eb3-b429-e039120e488a -CLIENT_SECRET = Ioo7Q~RJfhWsE45wPsuHm1CzRk1SppnNN3lZF -RELAY_ROOT = https://pp-pedw-ns.servicebus.windows.net/pp-pedw-hc/ -RELAYURI = "pp-pedw-ns.servicebus.windows.net" -RELAYPATH = "pp-pedw-hc" +//CLIENT_ID = ab6c4678-b31a-4eb3-b429-e039120e488a +//CLIENT_SECRET = Ioo7Q~RJfhWsE45wPsuHm1CzRk1SppnNN3lZF +//RELAY_ROOT = https://pp-pedw-ns.servicebus.windows.net/pp-pedw-hc/ +//RELAYURI = "pp-pedw-ns.servicebus.windows.net" +//RELAYPATH = "pp-pedw-hc" @@ -48,7 +48,7 @@ GOOGLE_TAG_MANAGER = GTM-T78CBC3 SHOWLOGIN = "true" SHOWREPRESENTATIONS = false SHOWFILEUPLOAD = "false" -SHOWMAPS = "false" +SHOWMAPS = "true" //BASIC_AUTH_CREDENTIALS = pinswg:password|pinswg2:password2 diff --git a/actions/index.js b/actions/index.js index d8274686..f02dc4b5 100644 --- a/actions/index.js +++ b/actions/index.js @@ -77,6 +77,19 @@ export const azureHeaders = (access_token) => { }; }; +export const azureHeadersNoOdata = (access_token) => { + return { + headers: { + //"OData-MaxVersion": "4.0", + //"OData-Version": "4.0", + //"Accept": "application/json;odata.metadata=none", + //"Prefer": 'odata.include-annotations="*",return=representation', + "Content-Type": "application/json", + "Authorization": "Bearer " + access_token, + }, + }; +}; + export const azureHeadersPaged = (access_token) => { return { headers: { @@ -145,6 +158,22 @@ export const getBasicSearch = (searchString) => { }); }; +export const getPartSavedAppeal = (searchString) => { + return axios + .get( + BASE_URL + + "/api/endpoint/getpartsavedappeal_api?searchString=" + + searchString + ) + .then((res) => { + return res.data; + }) + .catch((error) => { + console.log(error); + return error.response; + }); +}; + export const getBasicDNSURLSearch = (searchString) => { return axios .get( @@ -322,6 +351,38 @@ export const getBasicSearchDetails = ( }); }; +export const getBasicPartSavedDetails = ( + appealTypeName, + caseReference, + primaryIdAttribute, + incidentID +) => { + console.log( + "///////api call: ", + BASE_URL + + "/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" + + appealTypeName + + "&primaryIdAttribute=" + + primaryIdAttribute + + "&incidentID=" + + incidentID + ); + return axios + .get( + BASE_URL + + "/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" + + appealTypeName + + "&primaryIdAttribute=" + + primaryIdAttribute + + "&incidentID=" + + incidentID + ) + .then((res) => res.data) + .catch((error) => { + console.log("this error", error); + }); +}; + export const getBasicSearchDetailsPaged = ( appealTypeName, caseReference, @@ -670,13 +731,20 @@ export const getAwaitingSubmission = (loggedInUserId) => { }; export const getPortalModuleDetails = (appealType, caseReference) => { + console.log( + BASE_URL + + "/api/endpoint/getportalmoduledetails_api?appealType=" + + appealType + + "&caseReference=" + + encodeURI(caseReference) + ); return axios .get( BASE_URL + "/api/endpoint/getportalmoduledetails_api?appealType=" + appealType + "&caseReference=" + - caseReference + encodeURI(caseReference) ) .then((res) => res.data) .catch((error) => { diff --git a/components/case/summaryTypes/pinswg_dnsid.js b/components/case/summaryTypes/pinswg_dnsid.js index d5a31851..bba52130 100644 --- a/components/case/summaryTypes/pinswg_dnsid.js +++ b/components/case/summaryTypes/pinswg_dnsid.js @@ -49,7 +49,7 @@ const Pinswg_dnsid = (props) => { className={ showMap == "true" ? "govuk-grid-column-one-half" - : "govuk-grid-column-two-thirds" + : "govuk-grid-column-full" } >

diff --git a/components/elements/index.js b/components/elements/index.js index 3c7aa921..4552a5c5 100644 --- a/components/elements/index.js +++ b/components/elements/index.js @@ -22,6 +22,7 @@ import { subYears, subMonths, subDays, + parseISO, } from "date-fns"; const RenderTextfield = ({ @@ -92,7 +93,9 @@ export function Textfield(props) { disabled /> -
{t(hint)}
+
+ {t("newappeal:new-appeal-introduction-paragraph")} +
); } else { @@ -342,18 +345,29 @@ const RenderDatePicker = ({ {errorMsg} )} + + {/* {value} +
+ {moment(value).format("DD/MM/YY")} +
*/} + @@ -554,6 +568,7 @@ const RenderYesNo = ({ type="radio" className="govuk-radios__input" value={options[key]} + //checked={value == options[key]} onChange={(e) => { let docListObj = custom.documentList; if ( @@ -690,6 +705,10 @@ const RenderRadio = ({ type="radio" className="govuk-radios__input" value={options[key].split("|")[0]} + // checked={ + // value == + // parseInt(options[key].split("|")[0]) + // } onChange={(e) => { let docListObj = custom.documentList; if ( @@ -748,15 +767,23 @@ export function Radiofield(props) { .split(","); //parentFieldShowOnValue - var showIfHasParentShowValue = - parentField != false && - !_.isEmpty(formProps[form]) && - _.has(formProps[form].values, parentField) && - parentFieldShowOnValue.indexOf( - formProps[form].values[parentField].toString() - ) > -1; + var showIfHasParentShowValue = parentField != false; + //debugger; + // parentField != false && + // !_.isEmpty(formProps[form]) && + // _.has(formProps[form].values, parentField) && + // parentFieldShowOnValue.indexOf( + // formProps[form].values[parentField].toString() + // ) > -1; - // console.log(showIfHasParentShowValue); + (showIfHasParentShowValue == parentField) != false && + showIfHasParentShowValue; + + console.log( + datafieldname, + showIfHasParentShowValue, + formProps[form].values[parentField] + ); return ( <> diff --git a/components/myportal/topthree.js b/components/myportal/topthree.js index 6787d384..b0094833 100644 --- a/components/myportal/topthree.js +++ b/components/myportal/topthree.js @@ -175,6 +175,11 @@ const TopThree = (props) => { className="govuk-link--no-underline" data-id={index} onClick={() => { + topThreeType == "awaitingSubmission" && + console.log( + "appealDetails" + // props.props.awaitingSubmission + ); setCurrentReference({ "currentReference": topThreeType != "watchedCases" @@ -209,7 +214,7 @@ const TopThree = (props) => { ] : showTopThreeArr[key][ "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" - ]} + ]}{" "} diff --git a/components/newappeal/buildsection.js b/components/newappeal/buildsection.js index 28edc2f6..0ee9b9e7 100644 --- a/components/newappeal/buildsection.js +++ b/components/newappeal/buildsection.js @@ -12,6 +12,8 @@ import { setCurrentSection, setDocumentsList, } from "../../store/appealType/action"; +import { getFormCollectionByID } from "../utils"; +import { updateCase, patchCase } from "../../actions"; import { FieldsTranslations } from "../elements"; @@ -32,6 +34,9 @@ let BuildSection = (props) => { sectionCount, onSubmit, handleSubmit, + load, + pristine, + reset, formTitle, setCurrentSection, refno, @@ -62,7 +67,64 @@ let BuildSection = (props) => { const handleParam = (setValue) => (e) => setValue(e.target.value); + const updateCaseProgress = (values) => { + let incidentId = props.appealType.caseReference.incidentid; + let updateBody = values || {}; + + let appTypeCollection = getFormCollectionByID( + props.appealType.appealTypeID + ); + let updateBindAppealTypeToIncident = + appTypeCollection.NavigationProperty; + + Object.assign(updateBody, { + "pinswg_Appellant@odata.bind": + "/contacts(" + props.props.accountDetails.loggedinUserId + ")", + "pinswg_name": props.appealType.caseReference.ticketnumber, + [updateBindAppealTypeToIncident + "@odata.bind"]: + "/incidents(" + incidentId + ")", + }); + + updateBody = JSON.stringify(updateBody); + updateBody = updateBody.replace(/:"Yes"/gm, `:true`); + updateBody = updateBody.replace(/:"No"/gm, `:false`); + updateBody = JSON.parse(updateBody); + + Object.keys(updateBody).forEach((key) => { + if (updateBody[key] === null) { + delete updateBody[key]; + } + if (key.indexOf("_") == 0) { + delete updateBody[key]; + } + }); + + console.log(updateBody); + + let updateFormCollection = appTypeCollection.LogicalCollectionName; + let primaryAttribute = appTypeCollection.PrimaryIdAttribute; + + console.log( + incidentId, + updateBody, + updateFormCollection, + primaryAttribute, + props.appealType.caseReference.ticketnumber + ); + updateCase( + incidentId, + updateBody, + updateFormCollection, + primaryAttribute, + props.appealType.caseReference.ticketnumber + ); + //console.log("patching////////"); + //patchCase(incidentId); + }; + const onHandleSubmit = (values) => { + //alert(1); + //updateCaseProgress(values); setCurrentSection(currentSection + 1); }; @@ -198,86 +260,116 @@ let BuildSection = (props) => {
- - - {Object.keys(documentListObj).length > 0 && ( -
-

- {t("newappeal:new-appeal-documentlist-nav")}: +
+
- )} + + + {Object.keys(documentListObj).length > 0 && ( +
+

+ {t("newappeal:new-appeal-documentlist-nav")}: +

+
+
    +
      +
    • +
        + {Object.entries( + documentListObj + ).map(([key, value]) => { + return ( +
      1. {value}
      2. + ); + })} +
      +
    • +
    +
+
+ )} +
+ +

); @@ -289,6 +381,7 @@ const mapStateToProps = (state) => { searchResultsObj: state.searchResultsObj, formData: state.formData, appealType: state.appealType, + initialValues: state.appealType.caseReference.caseDetails, //form: state.form, }; }; @@ -316,5 +409,6 @@ export default connect( reduxForm({ form: "appealForm", destroyOnUnmount: false, + enableReinitialize: true, // this is needed!! })(BuildSection) ); diff --git a/components/timeout/index.js b/components/timeout/index.js index 093badd4..5fb0d445 100644 --- a/components/timeout/index.js +++ b/components/timeout/index.js @@ -41,7 +41,8 @@ const TimeOut = (props) => { console.log("////////////", "\n", "logout", "\n", "//////////"); destroyCookie({}, "pinsUser"), window.localStorage.clear(), - router.replace(props.ggLogout); + //router.replace(props.ggLogout); + router.replace("/logout"); }; return ( diff --git a/components/utils/index.js b/components/utils/index.js index 14530bd4..2c8d8a23 100644 --- a/components/utils/index.js +++ b/components/utils/index.js @@ -3,6 +3,7 @@ import data from "../../data/collections.json"; import { getBasicSearchDetails, getBasicSearchDetailsPaged, + getBasicPartSavedDetails, } from "../../actions"; /* @@ -69,6 +70,37 @@ export const getSearchDetails = (searchResultsObj) => { return Promise.all(detailsArr); }; +/* + * Get the partially saved details of a case from the appeal type + * @param object searchResultsObj + */ +export const getPartSavedDetails = (searchResultsObj) => { + //console.log(searchResultsObj); + + let detailsArr = []; + searchResultsObj = searchResultsObj.value; + const detailsObj = searchResultsObj.map((searchDetail, index) => { + if (searchDetail.pinswg_appealcasetype == null) { + console.log(searchDetail.title); + } else { + let formMeta = getFormCollectionByID( + searchDetail.pinswg_appealcasetype + ); + console.log(formMeta); + detailsArr.push( + getBasicPartSavedDetails( + formMeta.LogicalCollectionName, + searchDetail.title, + formMeta.PrimaryIdAttribute, + searchDetail.incidentid + ) + ); + } + }); + //console.log(detailsArr); + return Promise.all(detailsArr); +}; + /* * Get the details of a case from the appeal type * @param object searchResultsObj diff --git a/pages/api/endpoint/getbasicpartsaveddetails_api.js b/pages/api/endpoint/getbasicpartsaveddetails_api.js new file mode 100644 index 00000000..31b1d9a2 --- /dev/null +++ b/pages/api/endpoint/getbasicpartsaveddetails_api.js @@ -0,0 +1,50 @@ +import axios from "axios"; +import CryptoJS from "crypto-js"; +import { getToken, azureHeadersNoOdata } from "../../../actions"; +import { getSelectQuery } from "../../../actions/selectQueryTypes"; + +const WORDKEY = process.env.HASHKEY; + +const WEBAPI_URL = + process.env.RELAY_ROOT || + "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; + +const hashAPIPath = (queryPath) => { + var hashlink = CryptoJS.HmacSHA256( + queryPath, + CryptoJS.enc.Hex.parse(WORDKEY) + ); + hashlink = hashlink.toString(CryptoJS.enc.Hex); + return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; +}; + +export default async function ApiProxy(req, res) { + var appealTypeName = req.query.appealTypeName; + var primaryIdAttribute = req.query.primaryIdAttribute; + var incidentID = req.query.incidentID; + var token = await getToken(); + + var queryUrl = + appealTypeName + + "?$filter=_" + + primaryIdAttribute + + "s_value eq " + + incidentID + + "&$count=true"; + + queryUrl = queryUrl; //+ getSelectQuery(appealTypeName); + + //console.log("query: ", queryUrl, "<<< { + res.status(200).json(data); + }) + .catch(({ err }) => { + res.status(400).json(err); + }); +} diff --git a/pages/api/endpoint/getbasicsearch_api.js b/pages/api/endpoint/getbasicsearch_api.js index ac577c1b..90ccf283 100644 --- a/pages/api/endpoint/getbasicsearch_api.js +++ b/pages/api/endpoint/getbasicsearch_api.js @@ -32,7 +32,7 @@ export default async function ApiProxy(req, res) { searchString + "')) and pinswg_appealcasetype ne null and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - // console.log("basic search ", queryUrl); + console.log("basic search ", queryUrl); var apiResponse = _.isEmpty(req.query) ? res.status(400).json() diff --git a/pages/api/endpoint/getpartsavedappeal_api.js b/pages/api/endpoint/getpartsavedappeal_api.js new file mode 100644 index 00000000..4479de78 --- /dev/null +++ b/pages/api/endpoint/getpartsavedappeal_api.js @@ -0,0 +1,63 @@ +import axios from "axios"; +import CryptoJS from "crypto-js"; +import _ from "lodash"; +import { getToken, azureHeadersPaged } from "../../../actions"; + +const WORDKEY = process.env.HASHKEY; + +const WEBAPI_URL = + process.env.RELAY_ROOT || + "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; + +const hashAPIPath = (queryPath) => { + var hashlink = CryptoJS.HmacSHA256( + queryPath, + CryptoJS.enc.Hex.parse(WORDKEY) + ); + hashlink = hashlink.toString(CryptoJS.enc.Hex); + + //return "&hash=" + hashlink; + + return (queryPath.indexOf("?") > -1 ? "&hash=" : "?hash=") + hashlink; +}; + +export default async function ApiProxy(req, res) { + var searchString = req.query.searchString; + var token = await getToken(); + + var queryUrl = + "incidents?$select=description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + searchString + + "') or contains(ticketnumber, '" + + searchString + + "')) and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + + console.log("basic search ", queryUrl); + + var apiResponse = _.isEmpty(req.query) + ? res.status(400).json() + : searchString.length > 0 + ? axios + .get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeadersPaged(token.access_token) + ) + .then(({ data }) => { + var dataStr; + // dataStr = dataStr.replace(/: true/gm, `: "Yes"`); + // dataStr = dataStr.replace(/: false/gm, `: "No"`); + + // console.log("replaced strinig:", dataStr); + + _.has(data, "@odata.nextLink") == true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); + res.status(200).json(data); + }) + .catch(({ err }) => { + res.status(400).json(err); + }) + : res.status(400).json(); + + return apiResponse; +} diff --git a/pages/api/endpoint/getportalmoduledetails_api.js b/pages/api/endpoint/getportalmoduledetails_api.js index 4b6401d7..c0fda940 100644 --- a/pages/api/endpoint/getportalmoduledetails_api.js +++ b/pages/api/endpoint/getportalmoduledetails_api.js @@ -20,9 +20,11 @@ const hashAPIPath = (queryPath) => { export default async function ApiProxy(req, res) { var appealType = req.query.appealType; - var caseReference = req.query.caseReference; + var caseReference = req.query.caseReference.split("'").join("''"); var token = await getToken(); + //console.log("the case:", caseReference); + var queryUrl = appealType + "?$filter=pinswg_name eq '" + @@ -31,6 +33,8 @@ export default async function ApiProxy(req, res) { queryUrl = queryUrl + getSelectQuery(appealType); + //console.log("test:", queryUrl); + return axios .get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), diff --git a/pages/myportal/[appealtypes].js b/pages/myportal/[appealtypes].js index 21e713df..6cd6cf1b 100644 --- a/pages/myportal/[appealtypes].js +++ b/pages/myportal/[appealtypes].js @@ -10,6 +10,7 @@ import { getAppealsTypes, getMandatoryFields, getPickLists, + getPartSavedAppeal, } from "../../actions"; import { getProviderConfig } from "../../actions/govgateway"; import Breadcrumbs from "../../components/breadcrumbs"; @@ -32,6 +33,7 @@ import { import { setForm } from "../../store/formData/action"; import { getGovGatewayConfig } from "../../store/govgateway/action"; import { wrapper } from "../../store/store"; +import { getPartSavedDetails } from "../../components/utils"; let Home = (props) => { const { footerLinks, pages, formData } = props; @@ -218,10 +220,23 @@ export const getServerSideProps = wrapper.getServerSideProps( await getPickLists(query.appealtypes), ]); + let searchResultsObj = await getPartSavedAppeal(query.casereference); + //console.log("eeee:", searchResultsObj); + let searchDetailsObj = await getPartSavedDetails(searchResultsObj); + console.log("appeal:", searchDetailsObj[0].value[0]); + + searchDetailsObj = searchDetailsObj[0].value[0]; + + searchDetailsObj = JSON.stringify(searchDetailsObj); + searchDetailsObj = searchDetailsObj.replace(/:true/gm, `:"Yes"`); + searchDetailsObj = searchDetailsObj.replace(/:false/gm, `:"No"`); + searchDetailsObj = JSON.parse(searchDetailsObj); + let caseReference = {}; caseReference.ticketnumber = query.casereference; caseReference.incidentid = query.inid; + caseReference.caseDetails = searchDetailsObj; var path = require("path"); const configDirectory = path.resolve(process.cwd(), "data/formsxml"); @@ -255,6 +270,8 @@ const mapStateToProps = (state) => { form: state.form, accountDetails: state.accountDetails, govGateway: state.govGateway, + awaitingSubmission: state.awaitingSubmission, + currentView: state.currentView, }; }; diff --git a/styles/sass/welshgov/_application.scss b/styles/sass/welshgov/_application.scss index 3a3432f6..dde24ffa 100644 --- a/styles/sass/welshgov/_application.scss +++ b/styles/sass/welshgov/_application.scss @@ -683,11 +683,28 @@ textarea, word-wrap: normal; } +// .at-nav-container{ +// @media screen and (min-width: 480px) { +// position: fixed; + +// } +// } + .at-nav { border-top: 5px solid #1d70b8; padding-top: 25px; + .progress-heading{ + display:flex; + } + .progress-case-reference{ + margin-left:10px; + } + + } + + .at-nav ul.govuk-list.js-hidden { display: none; } @@ -841,6 +858,19 @@ textarea, } } +.progress-save{ + padding: 10px 15px !important; + font-size: 16px; + float: right; + border-color: $midgrey !important; + color: $midgrey !important; + + &:hover { + background-color: $midgrey !important; + color: $white !important; + } +} + .results .govuk-summary-list__row { @media screen and (max-width: 900px) { padding: 20px 10px;