diff --git a/src/client/actions.js b/src/client/actions.js index 31ea9a1ed4e..0b7c3b0fcff 100644 --- a/src/client/actions.js +++ b/src/client/actions.js @@ -314,8 +314,7 @@ export const COPY_TASK_DETAILS_LOADED = 'COPY_TASK_DETAILS_LOADED' export const INVESTMENT__PROJECT_LOADED = 'INVESTMENT__PROJECT_LOADED' -export const EXPORT_WINS__CONFIRMED_LOADED = 'EXPORT_WINS__CONFIRMED_LOADED' -export const EXPORT_WINS__DETAILS_LOADED = 'EXPORT_WINS__DETAILS_LOADED' +export const EXPORT_WIN_RESENT__SUCCESS = 'EXPORT_WIN_RESENT__SUCCESS' export const PAGINATED_RESOURCE__GO_TO_PAGE = 'PAGINATED_RESOURCE__GO_TO_PAGE' export const PAGINATED_RESOURCE__ON_SUCCESS = 'PAGINATED_RESOURCE__ON_SUCCESS' diff --git a/src/client/modules/ExportPipeline/ExportDetails/index.jsx b/src/client/modules/ExportPipeline/ExportDetails/index.jsx index 88ee6319839..68d85889aaf 100644 --- a/src/client/modules/ExportPipeline/ExportDetails/index.jsx +++ b/src/client/modules/ExportPipeline/ExportDetails/index.jsx @@ -131,7 +131,7 @@ const ExportDetailsForm = ({ exportItem }) => { /> {isEmpty(exportItem.estimated_win_date) diff --git a/src/client/modules/ExportWins/Details.jsx b/src/client/modules/ExportWins/Details.jsx deleted file mode 100644 index db77fa8f6a1..00000000000 --- a/src/client/modules/ExportWins/Details.jsx +++ /dev/null @@ -1,160 +0,0 @@ -import React from 'react' -import { Link } from 'govuk-react' -import { Link as ReactRouterLink } from 'react-router-dom/cjs/react-router-dom' -import styled from 'styled-components' -import { SPACING } from '@govuk-react/constants' -import pluralize from 'pluralize' - -import { DefaultLayout, SummaryTable } from '../../components' -import urls from '../../../lib/urls' -import { formatMediumDate } from '../../utils/date' -import { currencyGBP } from '../../utils/number-utils' -import ExportWin from '../../components/Resource/ExportWin' - -const VerticalSpacer = styled.div` - display: flex; - flex-direction: column; - gap: ${SPACING.SCALE_1}; - margin-bottom: ${SPACING.SCALE_5}; -` - -const NormalFontWeightRow = styled(SummaryTable.Row)` - & th { - font-weight: normal; - } -` - -const ExportWinTitle = (props) => ( - - {(exportWin) => ( - <> - {exportWin.nameOfExport} to {exportWin.country?.name} - - )} - -) - -const groupBreakdowns = (breakdowns) => { - const result = - breakdowns.reduce( - ({ groups: groups, totalAmount, yearRange }, breakdown) => { - const group = groups[breakdown.type.name] - - return { - totalAmount: totalAmount + breakdown.value, - yearRange: { - min: Math.min(yearRange.min, breakdown.year), - max: Math.max(yearRange.max, breakdown.year), - }, - groups: { - ...groups, - [breakdown.type.name]: { - yearRange: { - min: Math.min(group?.yearRange.min || Infinity, breakdown.year), - max: Math.max(group?.yearRange.min || 0, breakdown.year), - }, - total: (group?.total || 0) + breakdown.value, - }, - }, - } - }, - { - groups: {}, - totalAmount: 0, - yearRange: { min: Infinity, max: 0 }, - } - ) || {} - - return { - ...result, - totalYears: result.yearRange.max - result.yearRange.min + 1, - } -} - -const Detail = ({ - match: { - params: { winId }, - }, -}) => ( - } - pageTitle={} - breadcrumbs={[ - { - link: urls.dashboard.index(), - text: 'Home', - }, - { - link: urls.companies.exportWins.index(), - text: 'Export wins', - }, - { text: }, - ]} - > - - {(exportWin) => { - const { groups, totalAmount, totalYears } = exportWin - ? groupBreakdowns(exportWin.breakdowns) - : {} - - return ( - <> - - - {exportWin?.goodsVsServices.name} - - - {exportWin?.country.name} - - {exportWin && - Object.keys(groups).length > 1 && - Object.entries(groups).map(([k, { total, yearRange }]) => { - const years = yearRange.max - yearRange.min + 1 - return ( - - {`${currencyGBP(total)} over ${years} ${pluralize('year', years)}`} - - ) - })} - - {exportWin && - `${currencyGBP(totalAmount)} over ${totalYears} ${pluralize('year', totalYears)}`} - - - {exportWin && formatMediumDate(exportWin.date)} - - - {exportWin?.leadOfficer.name} - - - {exportWin?.comments} - - - {exportWin && (exportWin.isPersonallyConfirmed ? 'Yes' : 'No')} - - - - {exportWin?.isPersonallyConfirmed && ( - - View customer feedback - - )} - - Export wins - - - - ) - }} - - -) - -export default Detail diff --git a/src/client/modules/ExportWins/Details/ResendExportWin.jsx b/src/client/modules/ExportWins/Details/ResendExportWin.jsx new file mode 100644 index 00000000000..be0243a31b8 --- /dev/null +++ b/src/client/modules/ExportWins/Details/ResendExportWin.jsx @@ -0,0 +1,46 @@ +import React from 'react' +import Button from '@govuk-react/button' + +import { EXPORT_WIN_RESENT__SUCCESS } from '../../../actions' +import multiInstance from '../../../utils/multiinstance' +import { TASK_RESEND_EXPORT_WIN } from './state' +import Task from '../../../components/Task' + +const _ResendExportWin = ({ id }) => { + return ( + + {(getTask) => { + const task = getTask(TASK_RESEND_EXPORT_WIN, id) + return ( + + ) + }} + + ) +} + +export const ResendExportWin = multiInstance({ + name: 'ResendExportWin', + actionPattern: 'EXPORT_WIN_RESENT__', + reducer: (state, action) => { + switch (action.type) { + case EXPORT_WIN_RESENT__SUCCESS: + return { + success: true, + } + default: + return state + } + }, + component: _ResendExportWin, +}) diff --git a/src/client/modules/ExportWins/Details/index.jsx b/src/client/modules/ExportWins/Details/index.jsx new file mode 100644 index 00000000000..e7b4378cfbd --- /dev/null +++ b/src/client/modules/ExportWins/Details/index.jsx @@ -0,0 +1,168 @@ +import React from 'react' +import { Link as ReactRouterLink } from 'react-router-dom' +import { SPACING } from '@govuk-react/constants' +import styled from 'styled-components' +import { connect } from 'react-redux' +import { Link } from 'govuk-react' +import pluralize from 'pluralize' + +import { DefaultLayout, SummaryTable } from '../../../components' +import ExportWin from '../../../components/Resource/ExportWin' +import { currencyGBP } from '../../../utils/number-utils' +import { formatMediumDate } from '../../../utils/date' +import { ResendExportWin } from './ResendExportWin' +import urls from '../../../../lib/urls' +import { state2props } from './state' + +const VerticalSpacer = styled.div` + display: flex; + flex-direction: column; + gap: ${SPACING.SCALE_1}; + margin-bottom: ${SPACING.SCALE_5}; +` + +const NormalFontWeightRow = styled(SummaryTable.Row)` + & th { + font-weight: normal; + } +` + +const ExportWinTitle = (props) => ( + + {(exportWin) => ( + <> + {exportWin.nameOfExport} to {exportWin.country?.name} + + )} + +) + +const groupBreakdowns = (breakdowns) => { + const result = + breakdowns.reduce( + ({ groups: groups, totalAmount, yearRange }, breakdown) => { + const group = groups[breakdown.type.name] + + return { + totalAmount: totalAmount + breakdown.value, + yearRange: { + min: Math.min(yearRange.min, breakdown.year), + max: Math.max(yearRange.max, breakdown.year), + }, + groups: { + ...groups, + [breakdown.type.name]: { + yearRange: { + min: Math.min(group?.yearRange.min || Infinity, breakdown.year), + max: Math.max(group?.yearRange.min || 0, breakdown.year), + }, + total: (group?.total || 0) + breakdown.value, + }, + }, + } + }, + { + groups: {}, + totalAmount: 0, + yearRange: { min: Infinity, max: 0 }, + } + ) || {} + + return { + ...result, + totalYears: result.yearRange.max - result.yearRange.min + 1, + } +} + +const Detail = (props) => { + const { winId } = props.match.params + const success = props[winId]?.success + const flashMessage = success && { success: ['Successfully sent'] } + + return ( + } + pageTitle={} + breadcrumbs={[ + { + link: urls.dashboard.index(), + text: 'Home', + }, + { + link: urls.companies.exportWins.index(), + text: 'Export wins', + }, + { text: }, + ]} + flashMessages={flashMessage} + > + + {(exportWin) => { + const { groups, totalAmount, totalYears } = exportWin + ? groupBreakdowns(exportWin.breakdowns) + : {} + + return ( + <> + + + {exportWin?.goodsVsServices.name} + + + {exportWin?.country.name} + + {exportWin && + Object.keys(groups).length > 1 && + Object.entries(groups).map(([k, { total, yearRange }]) => { + const years = yearRange.max - yearRange.min + 1 + return ( + + {`${currencyGBP(total)} over ${years} ${pluralize('year', years)}`} + + ) + })} + + {exportWin && + `${currencyGBP(totalAmount)} over ${totalYears} ${pluralize('year', totalYears)}`} + + + {exportWin && formatMediumDate(exportWin.date)} + + + {exportWin?.leadOfficer.name} + + + {exportWin?.comments} + + + {exportWin && + (exportWin.isPersonallyConfirmed ? 'Yes' : 'No')} + + + {exportWin && } + + {exportWin?.isPersonallyConfirmed && ( + + View customer feedback + + )} + + Export wins + + + + ) + }} + + + ) +} + +export default connect(state2props)(Detail) diff --git a/src/client/modules/ExportWins/Details/state.js b/src/client/modules/ExportWins/Details/state.js new file mode 100644 index 00000000000..f671b2378ae --- /dev/null +++ b/src/client/modules/ExportWins/Details/state.js @@ -0,0 +1,3 @@ +export const TASK_RESEND_EXPORT_WIN = 'TASK_RESEND_EXPORT_WIN' +export const ID = 'ResendExportWin' +export const state2props = (state) => state[ID] diff --git a/src/client/modules/ExportWins/Details/tasks.js b/src/client/modules/ExportWins/Details/tasks.js new file mode 100644 index 00000000000..0563fb8c0b9 --- /dev/null +++ b/src/client/modules/ExportWins/Details/tasks.js @@ -0,0 +1,4 @@ +import { apiProxyAxios } from '../../../components/Task/utils' + +export const resendExportWin = (id) => + apiProxyAxios.post(`/v4/export-win/${id}/resend-win`) diff --git a/src/client/reducers.js b/src/client/reducers.js index 33d686cfe62..24db311d2cc 100644 --- a/src/client/reducers.js +++ b/src/client/reducers.js @@ -181,6 +181,8 @@ import getInteractionReducer from './modules/Interactions/InteractionDetails/red import { PREVIEW_QUOTE_ID } from './modules/Omis/state' import orderQuoteReducer from './modules/Omis/reducer' +import { ResendExportWin } from './modules/ExportWins/Details/ResendExportWin' + export const reducers = { tasks, [FLASH_MESSAGE_ID]: flashMessageReducer, @@ -202,6 +204,7 @@ export const reducers = { ...ContactForm.reducerSpread, ...Form.reducerSpread, ...FieldAddAnother.reducerSpread, + ...ResendExportWin.reducerSpread, [DNB_CHECK_ID]: dnbCheckReducer, [INVESTMENT_OPPORTUNITIES_LIST_ID]: investmentOpportunitiesListReducer, [INVESTMENT_OPPORTUNITIES_DETAILS_ID]: investmentOpportunitiesDetailsReducer, diff --git a/src/client/tasks.js b/src/client/tasks.js index 1855d959a7a..bc65488dd3b 100644 --- a/src/client/tasks.js +++ b/src/client/tasks.js @@ -454,6 +454,9 @@ import { getExportWin, } from '../client/modules/ExportWins/Form/tasks' +import { resendExportWin } from '../client/modules/ExportWins/Details/tasks' +import { TASK_RESEND_EXPORT_WIN } from './modules/ExportWins/Details/state' + export const tasks = { 'Create list': createList, 'Edit company': editCompany, @@ -709,4 +712,5 @@ export const tasks = { [TASK_CREATE_QUOTE]: createQuote, [TASK_CANCEL_QUOTE]: cancelQuote, [TASK_ADD_PROJECT_DOCUMENT]: addProjectDocument, + [TASK_RESEND_EXPORT_WIN]: resendExportWin, } diff --git a/test/functional/cypress/specs/export-pipeline/export-details-spec.js b/test/functional/cypress/specs/export-pipeline/export-details-spec.js index f9bc91599c1..043252883f9 100644 --- a/test/functional/cypress/specs/export-pipeline/export-details-spec.js +++ b/test/functional/cypress/specs/export-pipeline/export-details-spec.js @@ -42,7 +42,7 @@ describe('Export Details summary ', () => { Owner: exportItem.owner.name, 'Team members': exportItem.team_members.map((obj) => obj.name).join(''), 'Total estimated export value': estimatedExportValue, - 'Estimated date for Win': estimatedWinDate, + 'Estimated date for win': estimatedWinDate, Status: capitalize(exportItem.status), 'Export potential': capitalize(exportItem.export_potential), Destination: exportItem.destination_country.name,