Skip to content
This repository was archived by the owner on Oct 20, 2022. It is now read-only.
Draft
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
50 changes: 50 additions & 0 deletions cypress/integration/calls.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
AllocationTimeUnits,
CreateInstrumentMutationVariables,
TemplateGroupId,
UpdateCallInput,
} from '../../src/generated/sdk';
import initialDBData from '../support/initialDBData';

Expand Down Expand Up @@ -480,6 +481,48 @@ context('Calls tests', () => {
);
});

it('A user-officer should be able to deactivate/activate a call', () => {
cy.contains('Calls').click();

cy.contains(newCall.shortCode)
.parent()
.find('[aria-label="Deactivate call"]')
.click();

cy.get('[data-cy="confirm-ok"]').click();
cy.notification({ variant: 'success', text: 'successfully' });

cy.get('[data-cy="calls-table"]').should(
'not.contain',
newCall.shortCode
);

cy.get('[data-cy="call-status-filter"]').click();
cy.get('[role="listbox"]').contains('Inactive').click();

cy.finishedLoading();

cy.contains(newCall.shortCode)
.parent()
.find('[aria-label="Activate call"]')
.click();

cy.get('[data-cy="confirm-ok"]').click();
cy.notification({ variant: 'success', text: 'successfully' });

cy.get('[data-cy="calls-table"]').should(
'not.contain',
newCall.shortCode
);

cy.get('[data-cy="call-status-filter"]').click();
cy.get('[role="listbox"]').contains('Active').click();

cy.finishedLoading();

cy.get('[data-cy="calls-table"]').should('contain', newCall.shortCode);
});

it('A user-officer should not be able to set negative availability time on instrument per call', () => {
cy.assignInstrumentToCall({
callId: createdCallId,
Expand Down Expand Up @@ -592,6 +635,13 @@ context('Calls tests', () => {
...newInactiveCall,
esiTemplateId: esiTemplateId,
proposalWorkflowId: workflowId,
}).then((result) => {
if (result.createCall.call?.id) {
cy.updateCall({
...result.createCall.call,
isActive: false,
} as UpdateCallInput);
}
});

cy.contains('Calls').click();
Expand Down
2 changes: 1 addition & 1 deletion cypress/integration/proposals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ context('Proposal tests', () => {
cy.contains(newProposalTitle).should('not.exist');
});

it('User should not be able to create and submit proposal with inactive call', () => {
it('User should not be able to create and submit proposal on a call that is ended', () => {
createTopicAndQuestionToExistingTemplate();
cy.login('user');
cy.visit('/');
Expand Down
2 changes: 1 addition & 1 deletion src/components/DashBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ const Dashboard: React.FC = () => {
)?.isEnabled;

const { currentRole } = useContext(UserContext);
const { calls } = useCallsData({ isActive: true });
const { calls } = useCallsData({ isActive: true, isEnded: false });

useEffect(() => {
if (isTabletOrMobile) {
Expand Down
52 changes: 48 additions & 4 deletions src/components/call/CallsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Archive from '@mui/icons-material/Archive';
import Unarchive from '@mui/icons-material/Unarchive';
import { Typography } from '@mui/material';
import React, { useState } from 'react';
import { useQueryParams } from 'use-query-params';
Expand All @@ -9,12 +11,18 @@ import SuperMaterialTable, {
DefaultQueryParams,
UrlQueryParamsType,
} from 'components/common/SuperMaterialTable';
import { Call, InstrumentWithAvailabilityTime, UserRole } from 'generated/sdk';
import {
Call,
InstrumentWithAvailabilityTime,
UserRole,
UpdateCallInput,
} from 'generated/sdk';
import { useFormattedDateTime } from 'hooks/admin/useFormattedDateTime';
import { useCallsData } from 'hooks/call/useCallsData';
import { tableIcons } from 'utils/materialIcons';
import useDataApiWithFeedback from 'utils/useDataApiWithFeedback';
import { FunctionType } from 'utils/utilTypes';
import withConfirm, { WithConfirmProps } from 'utils/withConfirm';

import AssignedInstrumentsTable from './AssignedInstrumentsTable';
import AssignInstrumentsToCall from './AssignInstrumentsToCall';
Expand All @@ -30,7 +38,7 @@ const getFilterStatus = (callStatus: string | CallStatus) =>
? undefined // if set to ALL we don't filter by status
: callStatus === CallStatus.ACTIVE;

const CallsTable: React.FC = () => {
const CallsTable: React.FC<WithConfirmProps> = ({ confirm }) => {
const { api } = useDataApiWithFeedback();
const { timezone, toFormattedDateTime } = useFormattedDateTime({
shouldUseTimeZone: true,
Expand Down Expand Up @@ -125,6 +133,35 @@ const CallsTable: React.FC = () => {
}
};

const changeCallActiveStatus = (call: Call) => {
const shouldActivateCall = !call.isActive;
confirm(
async () => {
const data = await api({
toastSuccessMessage: `Call ${
shouldActivateCall ? 'activated' : 'deactivated'
} successfully`,
}).updateCall({
...call,
isActive: shouldActivateCall,
} as UpdateCallInput);

if (!data.updateCall.rejection) {
const newCallsArray = calls.filter(
(objectItem) => objectItem.id !== call.id
);
setCalls(newCallsArray);
}
},
{
title: `${shouldActivateCall ? 'Activate' : 'Deactivate'} call`,
description: `Are you sure you want to ${
shouldActivateCall ? 'activate' : 'deactivate'
} this call?`,
}
)();
};

const deleteCall = async (id: number | string) => {
return await api({ toastSuccessMessage: 'Call deleted successfully' })
.deleteCall({
Expand Down Expand Up @@ -252,7 +289,8 @@ const CallsTable: React.FC = () => {
setData={setCalls}
delete={deleteCall}
hasAccess={{
create: isUserOfficer,
create:
isUserOfficer && urlQueryParams.callStatus !== CallStatus.INACTIVE,
update: isUserOfficer,
remove: isUserOfficer,
}}
Expand Down Expand Up @@ -282,6 +320,12 @@ const CallsTable: React.FC = () => {
setAssigningInstrumentsCallId((rowData as Call).id),
position: 'row',
},
(rowData) => ({
icon: rowData.isActive ? Archive : Unarchive,
tooltip: `${rowData.isActive ? 'Deactivate' : 'Activate'} call`,
onClick: (): void => changeCallActiveStatus(rowData as Call),
position: 'row',
}),
]}
urlQueryParams={urlQueryParams}
setUrlQueryParams={setUrlQueryParams}
Expand All @@ -290,4 +334,4 @@ const CallsTable: React.FC = () => {
);
};

export default CallsTable;
export default withConfirm(CallsTable);
17 changes: 12 additions & 5 deletions src/components/proposal/ProposalSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import {
} from 'components/questionary/QuestionaryContext';
import ProposalQuestionaryReview from 'components/review/ProposalQuestionaryReview';
import { UserRole } from 'generated/sdk';
import { useDataApi } from 'hooks/common/useDataApi';
import { useDownloadPDFProposal } from 'hooks/proposal/useDownloadPDFProposal';
import { isCallEnded } from 'utils/helperFunctions';
import useDataApiWithFeedback from 'utils/useDataApiWithFeedback';
import withConfirm, { WithConfirmType } from 'utils/withConfirm';

import { ProposalContextType } from './ProposalContainer';
Expand All @@ -30,9 +31,13 @@ function ProposalReview({ confirm }: ProposalSummaryProps) {
throw new Error(createMissingContextErrorMessage());
}

const api = useDataApi();
const { api } = useDataApiWithFeedback();
const isUserOfficer = useCheckAccess([UserRole.USER_OFFICER]);
const isCallActive = state.proposal?.call?.isActive ?? true;

const callHasEnded = isCallEnded(
state.proposal.call?.startCall,
state.proposal.call?.endCall
);

const [loadingSubmitMessage, setLoadingSubmitMessage] =
useState<boolean>(true);
Expand All @@ -50,7 +55,7 @@ function ProposalReview({ confirm }: ProposalSummaryProps) {
proposal.questionary.steps.every((step) => step.isCompleted);

const submitDisabled =
(!isUserOfficer && !isCallActive) || // disallow submit for non user officers if the call ended
(!isUserOfficer && callHasEnded) || // disallow submit for non user officers if the call ended
!allStepsComplete ||
proposal.submitted;

Expand Down Expand Up @@ -126,7 +131,9 @@ function ProposalReview({ confirm }: ProposalSummaryProps) {
confirm(
async () => {
setIsSubmitting(true);
const result = await api().submitProposal({
const result = await api({
toastSuccessMessage: 'Proposal submitted successfully',
}).submitProposal({
proposalPk: state.proposal.primaryKey,
});
if (!result.submitProposal.proposal) {
Expand Down
9 changes: 7 additions & 2 deletions src/components/proposal/ProposalTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { UserContext } from 'context/UserContextProvider';
import { Call } from 'generated/sdk';
import { useDownloadPDFProposal } from 'hooks/proposal/useDownloadPDFProposal';
import { ProposalData } from 'hooks/proposal/useProposalData';
import { isCallEnded } from 'utils/helperFunctions';
import { tableIcons } from 'utils/materialIcons';
import { tableLocalization } from 'utils/materialLocalization';
import { timeAgo } from 'utils/Time';
Expand Down Expand Up @@ -177,9 +178,13 @@ const ProposalTable = ({
}}
actions={[
(rowData) => {
const isCallActive = rowData.call?.isActive ?? true;
const callHasEnded = isCallEnded(
rowData.call?.startCall,
rowData.call?.endCall
);

const readOnly =
!isCallActive ||
callHasEnded ||
(rowData.submitted &&
rowData.status?.shortCode !== 'EDITABLE_SUBMITTED');

Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { ProposalSubmissionState } from 'models/questionary/proposal/ProposalSubmissionState';
import { ProposalWithQuestionary } from 'models/questionary/proposal/ProposalWithQuestionary';
import { QuestionarySubmissionState } from 'models/questionary/QuestionarySubmissionState';
import { isCallEnded } from 'utils/helperFunctions';

import { QuestionaryWizardStep } from '../../DefaultWizardStepFactory';

export class ProposalQuestionaryWizardStep extends QuestionaryWizardStep {
isItemWithQuestionaryEditable(state: QuestionarySubmissionState) {
const { proposal } = state as ProposalSubmissionState;

const isCallActive = proposal.call?.isActive ?? true;
const callHasEnded = isCallEnded(
proposal.call?.startCall,
proposal.call?.endCall
);
const proposalStatus = this.getProposalStatus(proposal);

if (proposalStatus === 'EDITABLE_SUBMITTED') {
return true;
}

if (isCallActive) {
return proposalStatus === 'DRAFT';
} else {
if (callHasEnded) {
return false;
} else {
return proposalStatus === 'DRAFT';
}
}

Expand Down
Loading