Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/task deletion #6557

Merged
merged 6 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/client/components/Dashboard/my-tasks/MyTasksTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import styled from 'styled-components'

import { formatMediumDate } from '../../../utils/date'
import urls from '../../../../lib/urls'
import { STATUS } from '../../../modules/Tasks/TaskForm/constants'

export const transformAdvisersListItem = (advisers) => {
return advisers.map((adviser, index) => (
Expand Down Expand Up @@ -46,7 +47,11 @@ const rows = ({ results }) => {
<ul>{transformAdvisersListItem(task.advisers)}</ul>
</Table.Cell>
<Table.Cell setWidth="10%">
{task.archived ? 'Completed' : 'Active'}
{task.archived
? 'Deleted'
: task.status == STATUS.COMPLETED
? 'Completed'
: 'Active'}
</Table.Cell>
</Table.Row>
))
Expand Down
7 changes: 5 additions & 2 deletions src/client/components/Dashboard/my-tasks/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
ME_OTHERS_LIST_OPTIONS,
SHOW_ALL_OPTION,
} from './constants'
import { STATUS } from '../../../modules/Tasks/TaskForm/constants'

export const ID = 'getMyTasks'
export const TASK_GET_MY_TASKS = 'TASK_GET_MY_TASKS'
Expand All @@ -30,9 +31,10 @@ const sortbyMapping = {
company_ascending: 'company.name:asc',
project_ascending: 'investment_project.name:asc',
}

const statusMapping = {
active: { archived: false },
completed: { archived: true },
active: { status: STATUS.ACTIVE },
completed: { status: STATUS.COMPLETED },
}

export const state2props = ({ router, ...state }) => {
Expand All @@ -50,6 +52,7 @@ export const state2props = ({ router, ...state }) => {
sortby: 'due_date:asc',
company: undefined,
project: undefined,
status: undefined,
}

const assignedToMapping = {
Expand Down
4 changes: 3 additions & 1 deletion src/client/components/Dashboard/my-tasks/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ export const getMyTasks = ({
not_created_by,
advisers,
not_advisers,
archived,
archived = false,
sortby = 'due_date:asc',
company,
project,
status,
}) =>
apiProxyAxios
.post('/v4/search/task', {
Expand All @@ -24,5 +25,6 @@ export const getMyTasks = ({
sortby,
company,
investment_project: project,
status,
})
.then(({ data }) => data)
51 changes: 41 additions & 10 deletions src/client/modules/Tasks/TaskDetails/TaskButtons.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ import styled from 'styled-components'

import { Form } from '../../../components'
import urls from '../../../../lib/urls'
import { TASK_ARCHIVE_TASK, buttonState2props } from './state'
import { GREY_3, TEXT_COLOUR } from '../../../utils/colours'
import {
TASK_DELETE,
TASK_SAVE_STATUS_COMPLETE,
buttonState2props,
} from './state'
import { GREY_3, RED, TEXT_COLOUR } from '../../../utils/colours'
import { STATUS } from '../TaskForm/constants'

const ButtonWrapper = styled.div`
min-height: 71px;
Expand All @@ -21,28 +26,33 @@ const ButtonWrapper = styled.div`
}
`

const DeleteTaskWrapper = styled.div`
* {
margin-left: ${SPACING.SCALE_1};
}
`

export const TaskButtons = ({ task, returnUrl }) => (
<>
<GridRow>
{!task.archived && (
{!task.archived && task.status == STATUS.ACTIVE && (
<Form
id="archive-task-form"
analyticsFormName="archiveTaskForm"
id="task-save-status-complete-form"
data-test="task-save-status-complete-form"
analyticsFormName="taskSaveStatusCompleteForm"
redirectTo={() => urls.dashboard.myTasks()}
cancelRedirectTo={false}
submissionTaskName={TASK_ARCHIVE_TASK}
submissionTaskName={TASK_SAVE_STATUS_COMPLETE}
transformPayload={() => ({
taskId: task.id,
})}
flashMessage={() => 'Task marked as complete'}
submitButtonLabel="Mark as complete"
initialValues={task}
>
{() => <></>}
</Form>
/>
)}
<ButtonWrapper>
{!task.archived && (
{!task.archived && task.status == STATUS.ACTIVE && (
<Button
buttonColour={GREY_3}
buttonTextColour={TEXT_COLOUR}
Expand All @@ -62,6 +72,27 @@ export const TaskButtons = ({ task, returnUrl }) => (
>
Create similar task
</Button>
</ButtonWrapper>
{!task.archived && (
<DeleteTaskWrapper>
<Form
id="task-delete-form"
data-test="task-delete-form"
analyticsFormName="taskDeleteForm"
redirectTo={() => urls.dashboard.myTasks()}
cancelRedirectTo={false}
submissionTaskName={TASK_DELETE}
transformPayload={() => ({
taskId: task.id,
})}
submitButtonColour={RED}
flashMessage={() => 'Task deleted'}
submitButtonLabel="Delete task"
initialValues={task}
/>
</DeleteTaskWrapper>
)}
<ButtonWrapper>
<Link
data-test="task-back-link"
href={returnUrl ?? urls.dashboard.myTasks()}
Expand Down
19 changes: 16 additions & 3 deletions src/client/modules/Tasks/TaskDetails/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import TaskDetailsTable from './TaskDetailsTable'
import { ID, TASK_GET_TASK_DETAILS, state2props } from './state'
import { TASK_DETAILS_LOADED } from '../../../actions'
import Task from '../../../components/Task'
import { STATUS } from '../TaskForm/constants'

const StyledTag = styled(Tag)`
display: 'block',
Expand All @@ -19,18 +20,30 @@ const StyledTag = styled(Tag)`

const TaskDetails = ({ task, breadcrumbs }) => {
const { taskId } = useParams()

const taskTitle = task ? task.title : ''
const archivedTag = task?.archived && (
const statusTag = task?.status == STATUS.COMPLETED && (
<StyledTag colour="green" data-test="activity-kind-label">
COMPLETED
</StyledTag>
)
const archivedTag = task?.archived && (
<StyledTag colour="red" data-test="activity-kind-label">
DELETED
</StyledTag>
)
const subheading =
archivedTag || statusTag ? (
<>
{archivedTag} {statusTag}
</>
) : (
''
)

return (
<DefaultLayout
heading={taskTitle}
subheading={archivedTag}
subheading={subheading}
pageTitle={taskTitle}
breadcrumbs={breadcrumbs}
useReactRouter={false}
Expand Down
3 changes: 3 additions & 0 deletions src/client/modules/Tasks/TaskDetails/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import { getTaskBreadcrumbs } from '../TaskForm/state'
export const ID = 'taskDetails'

export const TASK_GET_TASK_DETAILS = 'TASK_GET_TASK_DETAILS'
export const TASK_SAVE_STATUS_COMPLETE = 'TASK_SAVE_STATUS_COMPLETE'
export const TASK_DELETE = 'TASK_DELETE'
export const TASK_SAVE_STATUS_ACTIVE = 'TASK_SAVE_STATUS_ACTIVE'
export const TASK_ARCHIVE_TASK = 'TASK_ARCHIVE_TASK'

export const state2props = (state) => {
Expand Down
10 changes: 8 additions & 2 deletions src/client/modules/Tasks/TaskDetails/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@ import { apiProxyAxios } from '../../../components/Task/utils'
export const getTaskDetail = (id) =>
apiProxyAxios.get(`v4/task/${id}`).then(({ data }) => data)

export const archiveTask = ({ taskId }) =>
apiProxyAxios.post(`/v4/task/${taskId}/archive`, { reason: 'completed' })
export const saveTaskStatusComplete = ({ taskId }) =>
apiProxyAxios.post(`/v4/task/${taskId}/status-complete`)

export const saveTaskStatusActive = ({ taskId }) =>
apiProxyAxios.post(`/v4/task/${taskId}/status-active`)

export const deleteTask = ({ taskId }) =>
apiProxyAxios.post(`/v4/task/${taskId}/archive`, { reason: 'deleted' })
7 changes: 7 additions & 0 deletions src/client/modules/Tasks/TaskForm/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,10 @@ export const OPTIONS = {
{ label: 'Someone else', value: OPTION_SOMEONE_ELSE },
],
}

const STATUS_ACTIVE = 'active'
const STATUS_COMPLETED = 'complete'
export const STATUS = {
ACTIVE: STATUS_ACTIVE,
COMPLETED: STATUS_COMPLETED,
}
15 changes: 12 additions & 3 deletions src/client/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,17 @@ import { getOmisCompanies } from './modules/Omis/CreateOrder/CompanySelect/tasks
import { TASK_CREATE_ORDER } from './modules/Omis/CreateOrder/state'
import { createOrder } from './modules/Omis/CreateOrder/tasks'

import { archiveTask, getTaskDetail } from './modules/Tasks/TaskDetails/tasks'
import {
TASK_ARCHIVE_TASK,
saveTaskStatusActive,
saveTaskStatusComplete,
getTaskDetail,
deleteTask,
} from './modules/Tasks/TaskDetails/tasks'
import {
TASK_SAVE_STATUS_ACTIVE,
TASK_SAVE_STATUS_COMPLETE,
TASK_GET_TASK_DETAILS,
TASK_DELETE,
} from './modules/Tasks/TaskDetails/state'
import { saveTaskDetail } from './modules/Tasks/TaskForm/tasks'
import { TASK_SAVE_TASK_DETAILS } from './modules/Tasks/TaskForm/state'
Expand Down Expand Up @@ -647,7 +654,9 @@ export const tasks = {
[TASK_EDIT_OMIS_INTERNAL_INFORMATION]: updateOrder,
[TASK_GET_TASK_DETAILS]: getTaskDetail,
[TASK_GET_INVESTMENT_PROJECT]: investmentProjectTasks.getInvestmentProject,
[TASK_ARCHIVE_TASK]: archiveTask,
[TASK_SAVE_STATUS_COMPLETE]: saveTaskStatusComplete,
[TASK_SAVE_STATUS_ACTIVE]: saveTaskStatusActive,
[TASK_DELETE]: deleteTask,
[TASK_RECONCILE_OMIS_PAYMENT]: savePayment,
[TASK_SAVE_TASK_DETAILS]: saveTaskDetail,
[TASK_EDIT_INVOICE_DETAILS]: updateOrder,
Expand Down
4 changes: 4 additions & 0 deletions src/lib/urls.js
Original file line number Diff line number Diff line change
Expand Up @@ -730,5 +730,9 @@ module.exports = {
createInteraction: url('/tasks/create?interactionId=', ':interactionId'),
createCopyTask: url('/tasks/create?copyTaskId=', ':copyTaskId'),
edit: url('/tasks', '/:taskId/edit'),
statusComplete: url('/tasks', '/:taskId/status-complete'),
statusActive: url('/tasks', '/:taskId/status-active'),
archive: url('/tasks', '/:taskId/archive'),
unarchive: url('/tasks', '/:taskId/unarchive'),
},
}
5 changes: 5 additions & 0 deletions test/a11y/cypress/config/urlTestExclusions.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ export const urlTestExclusions = [
{ url: '/investments/projects/:investmentId/propositions/:propositionId' },
{ url: '/companies/:companyId/hierarchies/ghq/:globalHqId/add' },
{ url: '/companies/:companyId/hierarchies/ghq/remove' },
// API calls with redirect
{ url: '/tasks/:taskId/status-complete' },
{ url: '/tasks/:taskId/status-active' },
{ url: '/tasks/:taskId/archive' },
{ url: '/tasks/:taskId/unarchive' },
// 501 errors
{ url: '/api-proxy/v4/company/:companyId/export-win' },
{ url: '/investments/projects/:projectId/edit-ukcompany/:companyId' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import urls from '../../../../../../src/lib/urls'

import { keysToSnakeCase } from '../../../../../functional/cypress/fakers/utils'
import Provider from '../../provider'
import { STATUS } from '../../../../../../src/client/modules/Tasks/TaskForm/constants'

describe('My Tasks on the Dashboard', () => {
const Component = (props) => (
Expand All @@ -24,7 +25,7 @@ describe('My Tasks on the Dashboard', () => {
)
// Create 3 tasks of which one is Archived
const myTasksList = taskWithInvestmentProjectListFaker()
myTasksList[1].archived = true
myTasksList[1].status = STATUS.COMPLETED

const myTaskResults = myTasksList.map((task) => keysToSnakeCase(task))
const myTasks = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,75 @@ describe('Task buttons', () => {
})

context('When a task is completed', () => {
const task = taskWithInvestmentProjectFaker({ archived: true })
const task = taskWithInvestmentProjectFaker({ status: 'complete' })

beforeEach(() => {
cy.mount(<Component task={task} />)
})

it('should not show the Mark as complete button', () => {
cy.get('[data-test="submit-button"]').should(
'not.contain.text',
'Mark as complete'
)
})

it('should not show the Edit link', () => {
cy.get('[data-test="edit-form-button"]').should('not.exist')
})

it('should show the Create similar task link', () => {
cy.get('[data-test="create-similar-task-button"]').should('exist')
})

it('should show the Back link to dashboard when no return url exists', () => {
assertLink('task-back-link', urls.dashboard.myTasks())
})
})

context('When a task is not archived/deleted', () => {
const task = taskWithInvestmentProjectFaker()

beforeEach(() => {
cy.mount(<Component task={task} />)
})

it('should show the Delete task button', () => {
cy.get('[data-test="submit-button"]').should(
'contain.text',
'Delete task'
)
})

it('should show the Edit link with expected url', () => {
assertLink('edit-form-button', urls.tasks.edit(task.id))
})

it('should show the Create similar task button with expected url', () => {
assertLink(
'create-similar-task-button',
urls.tasks.createCopyTask(task.id)
)
})

it('should show the Back link to dashboard when no return url exists', () => {
assertLink('task-back-link', urls.dashboard.myTasks())
})
})

context('When a task is archived/deleted', () => {
const task = taskWithInvestmentProjectFaker({ archived: true })

beforeEach(() => {
cy.mount(<Component task={task} />)
})

it('should not show the Delete task nor Mark as complete buttons', () => {
cy.get('[data-test="edit-form-button"]').should('not.exist')
})

it('should not show the Edit link', () => {
cy.get('[data-test="submit-button"]').should('not.exist')
cy.get('[data-test="edit-form-button"]').should('not.exist')
})

it('should show the Create similar task link', () => {
Expand Down
2 changes: 2 additions & 0 deletions test/functional/cypress/fakers/task.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { pick } from 'lodash'
import { listFaker } from './utils'
import { companyFaker } from './companies'
import { interactionFaker } from './interactions'
import { STATUS } from '../../../../src/client/modules/Tasks/TaskForm/constants'

const basicAdviserFaker = (overrides = {}) => ({
name: faker.person.fullName(),
Expand All @@ -29,6 +30,7 @@ const taskFaker = (overrides = {}) => ({
modifiedBy: basicAdviserFaker(),
modifiedOn: faker.date.past().toISOString(),
createdOn: faker.date.past().toISOString(),
status: STATUS.ACTIVE,

...overrides,
})
Expand Down
Loading
Loading