Skip to content

Commit ff4206f

Browse files
authored
Assign entries for voting (#2889)
* Implement assign entries for voting with request * Create style for assign entries button * Code quality improvements * Add handleAssignEntries function to configure cell * Create new AssignEntriesButton component and configure controls css module
1 parent 7ef667a commit ff4206f

File tree

11 files changed

+141
-12
lines changed

11 files changed

+141
-12
lines changed

src/commons/assessment/AssessmentTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export type AssessmentOverview = {
6464
isPublished?: boolean;
6565
hasVotingFeatures: boolean;
6666
hasTokenCounter?: boolean;
67+
isVotingPublished?: boolean;
6768
maxXp: number;
6869
earlySubmissionXp: number;
6970
number?: string; // For mission control

src/commons/sagas/BackendSaga.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
GradingQuestion
1717
} from '../../features/grading/GradingTypes';
1818
import {
19+
ASSIGN_ENTRIES_FOR_VOTING,
1920
CHANGE_DATE_ASSESSMENT,
2021
CHANGE_TEAM_SIZE_ASSESSMENT,
2122
CONFIGURE_ASSESSMENT,
@@ -1386,6 +1387,28 @@ function* BackendSaga(): SagaIterator {
13861387
yield call(showSuccessMessage, 'Updated successfully!', 1000);
13871388
}
13881389
);
1390+
1391+
yield takeEvery(
1392+
ASSIGN_ENTRIES_FOR_VOTING,
1393+
function* (action: ReturnType<typeof actions.assignEntriesForVoting>): any {
1394+
const tokens: Tokens = yield selectTokens();
1395+
const id = action.payload.id;
1396+
1397+
const resp: Response | null = yield updateAssessment(
1398+
id,
1399+
{
1400+
assignEntriesForVoting: true
1401+
},
1402+
tokens
1403+
);
1404+
if (!resp || !resp.ok) {
1405+
return yield handleResponseError(resp);
1406+
}
1407+
1408+
yield put(actions.fetchAssessmentOverviews());
1409+
yield call(showSuccessMessage, 'Updated successfully!', 1000);
1410+
}
1411+
);
13891412
}
13901413

13911414
function* handleReautogradeResponse(resp: Response | null): any {

src/commons/sagas/RequestsSaga.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ export const updateAssessment = async (
11401140
maxTeamSize?: number;
11411141
hasTokenCounter?: boolean;
11421142
hasVotingFeatures?: boolean;
1143+
assignEntriesForVoting?: boolean;
11431144
},
11441145
tokens: Tokens
11451146
): Promise<Response | null> => {

src/features/groundControl/GroundControlActions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { createAction } from '@reduxjs/toolkit';
22

33
import {
4+
ASSIGN_ENTRIES_FOR_VOTING,
45
CHANGE_DATE_ASSESSMENT,
56
CHANGE_TEAM_SIZE_ASSESSMENT,
67
CONFIGURE_ASSESSMENT,
@@ -39,3 +40,7 @@ export const configureAssessment = createAction(
3940
payload: { id, hasVotingFeatures, hasTokenCounter }
4041
})
4142
);
43+
44+
export const assignEntriesForVoting = createAction(ASSIGN_ENTRIES_FOR_VOTING, (id: number) => ({
45+
payload: { id }
46+
}));

src/features/groundControl/GroundControlTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ export const DELETE_ASSESSMENT = 'DELETE_ASSESSMENT';
44
export const PUBLISH_ASSESSMENT = 'PUBLISH_ASSESSMENT';
55
export const UPLOAD_ASSESSMENT = 'UPLOAD_ASSESSMENT';
66
export const CONFIGURE_ASSESSMENT = 'CONFIGURE_ASSESSMENT';
7+
export const ASSIGN_ENTRIES_FOR_VOTING = 'ASSIGN_ENTRIES_FOR_VOTING';

src/pages/academy/groundControl/GroundControl.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export type DispatchProps = {
4040
hasVotingFeatures: boolean,
4141
hasTokenCounter: boolean
4242
) => void;
43+
handleAssignEntriesForVoting: (id: number) => void;
4344
handleFetchCourseConfigs: () => void;
4445
};
4546

@@ -163,7 +164,8 @@ const GroundControl: React.FC<Props> = props => {
163164
field: 'placeholderConfigure' as any,
164165
cellRenderer: ConfigureCell,
165166
cellRendererParams: {
166-
handleConfigureAssessment: props.handleConfigureAssessment
167+
handleConfigureAssessment: props.handleConfigureAssessment,
168+
handleAssignEntriesForVoting: props.handleAssignEntriesForVoting
167169
},
168170
width: 80,
169171
filter: false,

src/pages/academy/groundControl/GroundControlContainer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '../../../commons/application/actions/SessionActions';
88
import { OverallState } from '../../../commons/application/ApplicationTypes';
99
import {
10+
assignEntriesForVoting,
1011
changeDateAssessment,
1112
changeTeamSizeAssessment,
1213
configureAssessment,
@@ -28,7 +29,8 @@ const mapDispatchToProps: MapDispatchToProps<DispatchProps, {}> = (dispatch: Dis
2829
handleUploadAssessment: uploadAssessment,
2930
handlePublishAssessment: publishAssessment,
3031
handleFetchCourseConfigs: fetchCourseConfig,
31-
handleConfigureAssessment: configureAssessment
32+
handleConfigureAssessment: configureAssessment,
33+
handleAssignEntriesForVoting: assignEntriesForVoting
3234
},
3335
dispatch
3436
);

src/pages/academy/groundControl/subcomponents/GroundControlConfigureCell.tsx

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,40 @@ import React, { useCallback, useState } from 'react';
1313

1414
import { AssessmentOverview } from '../../../../commons/assessment/AssessmentTypes';
1515
import ControlButton from '../../../../commons/ControlButton';
16+
import AssignEntriesButton from './configureControls/AssignEntriesButton';
1617

1718
type Props = {
1819
handleConfigureAssessment: (
1920
id: number,
2021
hasVotingFeatures: boolean,
2122
hasTokenCounter: boolean
2223
) => void;
24+
handleAssignEntriesForVoting: (id: number) => void;
2325
data: AssessmentOverview;
2426
};
2527

26-
const ConfigureCell: React.FC<Props> = ({ handleConfigureAssessment, data }) => {
28+
const ConfigureCell: React.FC<Props> = ({
29+
handleConfigureAssessment,
30+
handleAssignEntriesForVoting,
31+
data
32+
}) => {
2733
const [isDialogOpen, setDialogState] = useState(false);
2834
const [hasVotingFeatures, setHasVotingFeatures] = useState(!!data.hasVotingFeatures);
2935
const [hasTokenCounter, setHasTokenCounter] = useState(!!data.hasTokenCounter);
3036
const [isTeamAssessment, setIsTeamAssessment] = useState(false);
37+
const [isVotingPublished] = useState(!!data.isVotingPublished);
3138

3239
const handleOpenDialog = useCallback(() => setDialogState(true), []);
3340
const handleCloseDialog = useCallback(() => setDialogState(false), []);
3441

42+
// Updates assessment overview with changes to hasVotingFeatures and hasTokenCounter
3543
const handleConfigure = useCallback(() => {
3644
const { id } = data;
3745
handleConfigureAssessment(id, hasVotingFeatures, hasTokenCounter);
3846
handleCloseDialog();
3947
}, [data, handleCloseDialog, handleConfigureAssessment, hasTokenCounter, hasVotingFeatures]);
4048

49+
// Toggles in configuration pannel
4150
const toggleHasTokenCounter = useCallback(() => setHasTokenCounter(prev => !prev), []);
4251
const toggleVotingFeatures = useCallback(() => setHasVotingFeatures(prev => !prev), []);
4352
const toggleIsTeamAssessment = useCallback(() => setIsTeamAssessment(prev => !prev), []);
@@ -117,11 +126,10 @@ const ConfigureCell: React.FC<Props> = ({ handleConfigureAssessment, data }) =>
117126
label="Export Score Leaderboard (Coming soon!)"
118127
/>
119128
</div>
120-
<Switch
121-
className="publish-voting"
122-
disabled={true}
123-
inline
124-
label="Publish Voting (Coming soon!)"
129+
<AssignEntriesButton
130+
handleAssignEntriesForVoting={handleAssignEntriesForVoting}
131+
assessmentId={data.id}
132+
isVotingPublished={isVotingPublished}
125133
/>
126134
</div>
127135
</Collapse>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { Button, ButtonGroup, Icon } from '@blueprintjs/core';
2+
import { IconNames, InfoSign } from '@blueprintjs/icons';
3+
import { useCallback, useState } from 'react';
4+
import ControlButton from 'src/commons/ControlButton';
5+
import classes from 'src/styles/ConfigureControls.module.scss';
6+
7+
type Props = {
8+
handleAssignEntriesForVoting: (id: number) => void;
9+
assessmentId: number;
10+
isVotingPublished: boolean;
11+
};
12+
13+
const AssignEntriesButton: React.FC<Props> = ({
14+
handleAssignEntriesForVoting,
15+
assessmentId,
16+
isVotingPublished
17+
}) => {
18+
const [confirmAssignEntries, setConfirmAssignEntries] = useState(false);
19+
20+
// OnClick and Handler functions for confirmation warnings when assigning entries for voting
21+
const onAssignClick = useCallback(() => setConfirmAssignEntries(true), []);
22+
const handleConfirmAssign = useCallback(() => {
23+
handleAssignEntriesForVoting(assessmentId);
24+
}, [assessmentId, handleAssignEntriesForVoting]);
25+
const handleCancelAssign = useCallback(() => setConfirmAssignEntries(false), []);
26+
27+
return (
28+
<>
29+
<div className={classes['current-voting-status']}>
30+
<InfoSign />
31+
<p className={classes['voting-status-text']}>
32+
Current Voting Status: Entries have {!isVotingPublished && <b>not </b>} been assigned
33+
</p>
34+
</div>
35+
{!confirmAssignEntries ? (
36+
<div className={'control-button-container'}>
37+
<ControlButton
38+
icon={IconNames.RESET}
39+
onClick={onAssignClick}
40+
label={`${!isVotingPublished ? 'Assign' : 'Reassign'} entries for voting`}
41+
/>
42+
</div>
43+
) : (
44+
<div className={classes['confirm-assign-voting']}>
45+
<Icon icon="reset" />
46+
<p className={classes['confirm-assign-text']}>
47+
Are you sure you want to <b>{isVotingPublished ? 're-assign' : 'assign'} entries?</b>
48+
</p>
49+
<ButtonGroup>
50+
<Button small intent="success" onClick={handleConfirmAssign}>
51+
Assign
52+
</Button>
53+
<Button small intent="danger" onClick={handleCancelAssign}>
54+
Cancel
55+
</Button>
56+
</ButtonGroup>
57+
</div>
58+
)}
59+
{isVotingPublished && (
60+
<p className={classes['reassign-voting-warning']}>
61+
<b>All existing votes</b> will be <b>deleted</b> upon reassigning entries!
62+
</p>
63+
)}
64+
</>
65+
);
66+
};
67+
68+
export default AssignEntriesButton;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* Container for warnings upon clicking assign entries button */
2+
.reassign-voting-warning {
3+
font-size: 11px;
4+
margin-left: 38px;
5+
}
6+
7+
.confirm-assign-voting,
8+
.current-voting-status {
9+
max-height: 30px;
10+
display: flex;
11+
align-items: center;
12+
gap: 6px;
13+
margin-left: 15px;
14+
15+
.confirm-assign-text {
16+
margin-top: 9px;
17+
}
18+
19+
.voting-status-text {
20+
margin-top: 10px;
21+
}
22+
}

0 commit comments

Comments
 (0)