Skip to content

Commit

Permalink
Export project interactions Sort by functionality (#7500)
Browse files Browse the repository at this point in the history
* Layout test scenarios - Sort export project interaction list

Sort a default company export interaction list

* Added test coverage for company export interaction

* Added export interaction collection list test

* Update sandbox server `/v4/export/:exportId` endpoint error response

* Fix export interaction tab navigation failing test

* Moved a constant functions declaration

Is a best practice to declare constant functions in the beginning of the codebase before calling, in order the javascript interpreter can locate.
This action being reverted and noted as part of flakiness/tech debt.

* Use interaction `subject` rather than export `title`

Amend the current implementation of export interaction list by using `subject` rather than export interaction `title` on the list because is already on the header and repetitive each details .
  • Loading branch information
dredmonds committed Feb 11, 2025
1 parent 6eaff36 commit 8b3663a
Show file tree
Hide file tree
Showing 13 changed files with 593 additions and 2 deletions.
79 changes: 79 additions & 0 deletions src/client/modules/ExportPipeline/Export.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react'
import styled from 'styled-components'
import { useLocation } from 'react-router-dom'

import ExportInteractionsList from './ExportInteractionsList'
import ExportResource from '../../components/Resource/Export'
import { DefaultLayout } from '../../components'
import TabNav from '../../components/TabNav'
import ExportDetails from './ExportDetails'
import urls from '../../../lib/urls'

const EXPORT_ID_REGEX = /\/export\/([^/]+)\//
const EXPORT_ASPECT_REGEX = /\/([^/]+)$/

const StyledLink = styled('a')({
fontSize: 20,
display: 'inline-block',
fontFamily: 'Arial, sans-serif',
marginTop: 8,
marginBottom: 8,
})

export const CompanyLink = (props) => (
<ExportResource.Inline {...props}>
{({ company }) => (
<StyledLink
data-test="export-company-link"
href={urls.companies.detail(company.id)}
>
{company.name.toUpperCase()}
</StyledLink>
)}
</ExportResource.Inline>
)

export const ExportProjectTitle = (props) => (
<ExportResource.Inline {...props}>
{(exportProject) => exportProject.title}
</ExportResource.Inline>
)

const Export = () => {
const location = useLocation()
const matchId = location.pathname.match(EXPORT_ID_REGEX)
const exportId = matchId ? matchId[1] : null
const matchAspect = location.pathname.match(EXPORT_ASPECT_REGEX)
const aspect = matchAspect ? matchAspect[1] : null // aspect will be either 'details' or 'interactions'

return (
<DefaultLayout
superheading={<CompanyLink id={exportId} />}
heading={<ExportProjectTitle id={exportId} />}
pageTitle={`Export ${aspect}`}
breadcrumbs={[
{ link: urls.exportPipeline.index(), text: 'Home' },
{ text: <ExportProjectTitle id={exportId} /> },
]}
>
<TabNav
id="export-tab-nav"
label="Export tab nav"
layout="vertical"
routed={true}
tabs={{
[urls.exportPipeline.details(exportId)]: {
label: 'Project details',
content: <ExportDetails />,
},
[urls.exportPipeline.interactions.index(exportId)]: {
label: 'Interactions',
content: <ExportInteractionsList exportId={exportId} />,
},
}}
/>
</DefaultLayout>
)
}

export default Export
63 changes: 63 additions & 0 deletions src/client/modules/ExportPipeline/ExportInteractionsList/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react'

import { LEVEL_SIZE } from '@govuk-react/constants'
import { H2 } from 'govuk-react'

import Interactions from '../../../components/Resource/Interactions'
import { formatDate, DATE_FORMAT_FULL } from '../../../utils/date-utils'
import { CollectionItem } from '../../../components'
import urls from '../../../../lib/urls'
import { SORT_OPTIONS_EXPORT_INTERACTION } from '../constants'

const ExportInteractionsList = ({ interactions = [] }) =>
interactions.length === 0 ? null : (
<ul data-test="export-interactions-list">
{interactions.map((item) => (
<CollectionItem
key={item.id}
headingText={item.subject}
headingUrl={urls.exportPipeline.interactions.details(item.id)}
metadata={[
{
label: 'Date:',
value: formatDate(item.date, DATE_FORMAT_FULL),
},
{
label: 'Contact(s):',
value: item.contacts.map(({ name }) => name).join(', '),
},
{
label: 'Adviser(s):',
value: item.dit_participants
.map(({ adviser, team }) => `${adviser.name} - ${team.name}`)
.join(', '),
},
{
label: 'Service:',
value: item.service.name,
},
]}
/>
))}
</ul>
)

export default ({ exportId }) => (
<>
<H2 size={LEVEL_SIZE[3]}>Interactions</H2>
<p>
An interaction could be a meeting, call, email or another activity
associated with this export.
</p>
<Interactions.Paginated
id="export-interactions"
heading="interactions"
shouldPluralize={true}
noResults="You don't have any export interactions."
payload={{ company_export_id: exportId }}
sortOptions={SORT_OPTIONS_EXPORT_INTERACTION}
>
{(page) => <ExportInteractionsList interactions={page} />}
</Interactions.Paginated>
</>
)
6 changes: 6 additions & 0 deletions src/client/modules/ExportPipeline/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ export const SORT_OPTIONS = [
},
]

export const SORT_OPTIONS_EXPORT_INTERACTION = [
{ name: 'Recently created', value: '-created_on' },
{ name: 'Company name A-Z', value: 'company__name' },
{ name: 'Subject A-Z', value: 'subject' },
]

export const EXPORT_POTENTIAL_OPTIONS = [
{ label: 'High', value: 'high' },
{ label: 'Medium', value: 'medium' },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
import React from 'react'

import ExportInteractionsList from '../../../../../src/client/modules/ExportPipeline/ExportInteractionsList'
import { interactionFaker } from '../../../../functional/cypress/fakers/interactions'

describe('ExportInteractionsList', () => {
it('should render a list of export project interactions', () => {
const exportProject = {
id: '1',
title: 'Baileys export to Brazil',
}

const interaction = {
id: 123,
date: '2024-12-23',
contacts: [
{
name: 'James Brown',
},
],
dit_participants: [
{
adviser: {
name: 'David Buffer',
},
team: {
name: 'Digital Data Hub - Live Service',
},
},
],
service: {
name: 'Account management : General',
},
subject: 'Video call meeting to Baileys',
}

const interactionList = [
interaction,
interactionFaker(),
interactionFaker(),
]

cy.mountWithProvider(
<ExportInteractionsList exportId={exportProject.id} />,
{
tasks: {
Interactions: () => Promise.resolve(interactionList),
Export: () => Promise.resolve(exportProject),
},
}
)

cy.get('[data-test="export-interactions-list"]').should('exist')
cy.get('[data-test="collection-item"]').as('collectionItems')
cy.get('@collectionItems').eq(0).as('firstItem')

cy.get('@collectionItems').should('have.length', 3)

cy.get('@firstItem').within(() => {
cy.get('h3 a')
.should('have.text', 'Video call meeting to Baileys')
.and('have.attr', 'href', '/export/123/interactions/details')

const items = '[data-test="metadata-item"]'
cy.get(items).should('have.length', 4)
cy.get(items).eq(0).should('have.text', 'Date: 23 December 2024')
cy.get(items).eq(1).should('have.text', 'Contact(s): James Brown')
cy.get(items)
.eq(2)
.should(
'have.text',
'Adviser(s): David Buffer - Digital Data Hub - Live Service'
)
cy.get(items)
.eq(3)
.should('have.text', 'Service: Account management : General')
})
})

it('should render a list of multiple contacts', () => {
const interaction = {
id: 224,
date: '2024-12-23',
contacts: [
{
name: 'James Brown',
},
{
name: 'Jim Brown',
},
{
name: 'Jude Brown',
},
],
dit_participants: [],
service: {
name: 'Account management : General',
},
subject: 'Video call meeting to Baileys',
}
cy.mountWithProvider(<ExportInteractionsList />, {
tasks: {
Interactions: () => Promise.resolve([interaction]),
},
})

cy.get('[data-test="metadata-item"]')
.eq(1)
.should('have.text', 'Contact(s): James Brown, Jim Brown, Jude Brown')
})

it('should render a list of multiple advisers', () => {
const interaction = {
date: '2024-12-23',
contacts: [],
dit_participants: [
{
adviser: {
name: 'James Brown',
},
team: {
name: 'British Water',
},
},
{
adviser: {
name: 'Jim Brown',
},
team: {
name: 'Cumbria LEP',
},
},
{
adviser: {
name: 'Jude Brown',
},
team: {
name: 'Doncaster Council',
},
},
],
service: {
name: 'Account management : General',
},
subject: 'Send email to Baileys',
}
cy.mountWithProvider(<ExportInteractionsList />, {
tasks: {
Interactions: () => Promise.resolve([interaction]),
},
})

cy.get('[data-test="metadata-item"]')
.eq(2)
.should(
'have.text',
'Adviser(s): James Brown - British Water, Jim Brown - Cumbria LEP, Jude Brown - Doncaster Council'
)
})

it('should not render a list of export project interactions', () => {
cy.mountWithProvider(<ExportInteractionsList />, {
tasks: {
Interactions: () => Promise.resolve([]),
},
})

cy.get('[data-test="export-interactions-list"]').should('not.exist')
})
})
Loading

0 comments on commit 8b3663a

Please sign in to comment.