Skip to content

Commit cfc32e9

Browse files
Grading Overview: TS/Tremor -> AG/Blueprint Migration (#2893)
* added filterable columns in grading overview * some cleanup code * moved some grading overview FE components from tremor to blueprint * missing code from previous commit * halfway done for porting tanstack/tremor to ag grid/blueprint * more changes to grading table - animation whilst loading, filter/edit mode, bugfixes * code refactoring * filterable columns, backend sorting shell, and some partial removal of tanstack and tremor table * more component migrations, refactoring and preparation for backend sort * multi -> single sorting, moved over all ts/tremor components to ag/bp, removal of old code * fix table cutoff on small horizontal resolution and missing hover effects * refresh button for table * fixed richard's comments (mostly), and added a fix for josh's comment and PR * fixed edge case of attempted/submitted on filter from prev commit * mock files, change josh's pr 2nd issue to remove non-submitted filters on selecting ungraded, some refactoring * fixed wrong username and text overflow * eslint * prettier checks * backend mock changes * minor adjustments and cleanups * minor ui adjustments for better mobile compatability * preparation for P2 merge * eslint prettier * compile erros * compile error and eslint * prettier checks * minor ui adjustments * Revert change back to raw strings * typescript v5 fixes and richard's comments * more typescript v5 fixes * prettier * some refactoring and bug fixing * null value error in empty cell & wider actions col * added submitted to unpublished allowed filters * Fix format * Fix compile error post-merge * Refactor GradingFlex * Add React import * Remove unnecessary type annotations * Extract default styles outside component * Remove unused props * Refactor GradingText * Add React import * Use `classnames` utility * Use `Classes` object instead of raw string CSS API * Move constant default styles out of component * Remove unused props * Update BackendSaga.ts * Use strict inequality * Use `Object.entries` to iterate over both key and value * Refactor GradingFilterable.tsx * Refactor GradingActions * Add React import * Use `useSession` over `useTypedSelector` * Use Blueprint's `Tooltip` component over `htmlTitle` * Remove unnecessary `={true}` in props * Simplify conditionals with `&&` * Simplify conditions in conditionals For better readability. * Refactor conditions For readability. * Refactor GradingColumnCustomHeaders.tsx * Add React import * Use `classNames` utility instead of `String` * Remove unused prop export * Convert Props interface to type * Remove unnecessary type annotations and arguments * Convert conditional to `&&` * Refactor GradingColumnFilters.tsx * Add React import * Refactor GradingBadges.tsx * Add React import * Improve typing of badge colors * Use optional chaining where possible * Remove unused export * Add missing React import Also renamed a type to `Props` as it is unexported. * Fix imports and format post merge * hw review changes and child key error fix * fixed randomly broken grading table headers and merge conflicts * prettier * Reformat post-lint updates * Add TODO * Remove unused CSS class * Scope most styles to CSS modules * Migrate more styles to CSS modules * Simplify to use new actions format * Migrate more classes to CSS modules * Refactor grading badge styles to separate module file * Remove hardcoded CSS namespace * Make fix less hacky * Remove unnecessary default with enum type * Refactor column builder to separate file Use dependency injection to avoid needing a hook. * Remove unnecessary space * Improve readability * Remove unused param * Remove unnecessary `={true}` * Add TODO * Refactor grading badge * Simplify props * Remove unneded export --------- Co-authored-by: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
1 parent 72ea6de commit cfc32e9

27 files changed

+1607
-465
lines changed

src/commons/application/ApplicationTypes.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DashboardState } from '../../features/dashboard/DashboardTypes';
55
import { PlaygroundState } from '../../features/playground/PlaygroundTypes';
66
import { PlaybackStatus, RecordingStatus } from '../../features/sourceRecorder/SourceRecorderTypes';
77
import { StoriesEnvState, StoriesState } from '../../features/stories/StoriesTypes';
8+
import { freshSortState } from '../../pages/academy/grading/subcomponents/GradingSubmissionsTable';
89
import { WORKSPACE_BASE_PATHS } from '../../pages/fileSystem/createInBrowserFileSystem';
910
import { FileSystemState } from '../fileSystem/FileSystemTypes';
1011
import { SideContentManagerState, SideContentState } from '../sideContent/SideContentTypes';
@@ -441,7 +442,16 @@ export const defaultWorkspaceManager: WorkspaceManagerState = {
441442
},
442443
currentSubmission: undefined,
443444
currentQuestion: undefined,
444-
hasUnsavedChanges: false
445+
hasUnsavedChanges: false,
446+
// TODO: The below should be a separate state
447+
// instead of using the grading workspace state
448+
columnVisiblity: [],
449+
requestCounter: 0,
450+
allColsSortStates: {
451+
currentState: freshSortState,
452+
sortBy: ''
453+
},
454+
hasLoadedBefore: false
445455
},
446456
playground: {
447457
...createDefaultWorkspace('playground'),

src/commons/application/actions/SessionActions.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,14 @@ import {
33
paginationToBackendParams,
44
unpublishedToBackendParams
55
} from 'src/features/grading/GradingUtils';
6+
import { freshSortState } from 'src/pages/academy/grading/subcomponents/GradingSubmissionsTable';
67
import { OptionType } from 'src/pages/academy/teamFormation/subcomponents/TeamFormationForm';
78

8-
import { GradingOverviews, GradingQuery } from '../../../features/grading/GradingTypes';
9+
import {
10+
AllColsSortStates,
11+
GradingOverviews,
12+
GradingQuery
13+
} from '../../../features/grading/GradingTypes';
914
import { TeamFormationOverview } from '../../../features/teamFormation/TeamFormationTypes';
1015
import {
1116
Assessment,
@@ -52,13 +57,16 @@ const SessionActions = createActions('session', {
5257
* many entries, starting from what offset, to get
5358
* @param filterParams - param that contains columnFilters converted into JSON for
5459
* processing into query parameters
60+
* @param allColsSortStates - param that contains the sort states of all columns and
61+
* the col it should be sorted by
5562
*/
5663
fetchGradingOverviews: (
5764
filterToGroup = true,
5865
publishedFilter = unpublishedToBackendParams(false),
5966
pageParams = paginationToBackendParams(0, 10),
60-
filterParams = {}
61-
) => ({ filterToGroup, publishedFilter, pageParams, filterParams }),
67+
filterParams = {},
68+
allColsSortStates: AllColsSortStates = { currentState: freshSortState, sortBy: '' }
69+
) => ({ filterToGroup, publishedFilter, pageParams, filterParams, allColsSortStates }),
6270
fetchTeamFormationOverviews: (filterToGroup = true) => filterToGroup,
6371
fetchStudents: () => ({}),
6472
login: (providerId: string) => providerId,

src/commons/application/actions/__tests__/SessionActions.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@ import {
44
paginationToBackendParams,
55
unpublishedToBackendParams
66
} from 'src/features/grading/GradingUtils';
7+
import { freshSortState } from 'src/pages/academy/grading/subcomponents/GradingSubmissionsTable';
78

8-
import { GradingOverviews, GradingQuery } from '../../../../features/grading/GradingTypes';
9+
import {
10+
ColumnFields,
11+
GradingOverviews,
12+
GradingQuery
13+
} from '../../../../features/grading/GradingTypes';
914
import { TeamFormationOverview } from '../../../../features/teamFormation/TeamFormationTypes';
1015
import {
1116
Assessment,
@@ -89,7 +94,8 @@ test('fetchGradingOverviews generates correct default action object', () => {
8994
filterToGroup: true,
9095
publishedFilter: unpublishedToBackendParams(false),
9196
pageParams: paginationToBackendParams(0, 10),
92-
filterParams: {}
97+
filterParams: {},
98+
allColsSortStates: { currentState: freshSortState, sortBy: '' }
9399
}
94100
});
95101
});
@@ -99,19 +105,22 @@ test('fetchGradingOverviews generates correct action object', () => {
99105
const publishedFilter = unpublishedToBackendParams(true);
100106
const pageParams = { offset: 123, pageSize: 456 };
101107
const filterParams = { abc: 'xxx', def: 'yyy' };
108+
const allColsSortStates = { currentState: freshSortState, sortBy: ColumnFields.assessmentName };
102109
const action = SessionActions.fetchGradingOverviews(
103110
filterToGroup,
104111
publishedFilter,
105112
pageParams,
106-
filterParams
113+
filterParams,
114+
allColsSortStates
107115
);
108116
expect(action).toEqual({
109117
type: SessionActions.fetchGradingOverviews.type,
110118
payload: {
111119
filterToGroup: filterToGroup,
112120
publishedFilter: publishedFilter,
113121
pageParams: pageParams,
114-
filterParams: filterParams
122+
filterParams: filterParams,
123+
allColsSortStates: allColsSortStates
115124
}
116125
});
117126
});

src/commons/grading/GradingFlex.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { Property } from 'csstype';
2+
import React from 'react';
3+
4+
const defaultStyles: React.CSSProperties = {
5+
display: 'flex'
6+
};
7+
8+
type Props = {
9+
justifyContent?: Property.JustifyContent;
10+
alignItems?: Property.AlignItems;
11+
flexDirection?: Property.FlexDirection;
12+
children?: React.ReactNode;
13+
style?: React.CSSProperties;
14+
className?: string;
15+
};
16+
17+
const GradingFlex: React.FC<Props> = ({
18+
justifyContent,
19+
alignItems,
20+
flexDirection,
21+
children,
22+
style,
23+
className
24+
}) => {
25+
const styles: React.CSSProperties = { ...style, justifyContent, alignItems, flexDirection };
26+
return (
27+
<div className={className} style={{ ...defaultStyles, ...styles }}>
28+
{children}
29+
</div>
30+
);
31+
};
32+
33+
export default GradingFlex;

src/commons/grading/GradingText.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { Classes, Text } from '@blueprintjs/core';
2+
import classNames from 'classnames';
3+
import React from 'react';
4+
5+
const defaultStyles: React.CSSProperties = {
6+
width: 'max-content',
7+
margin: 'auto 0'
8+
};
9+
10+
type Props = {
11+
children?: React.ReactNode;
12+
style?: React.CSSProperties;
13+
isSecondaryText?: boolean;
14+
className?: string;
15+
};
16+
17+
const GradingText: React.FC<Props> = ({ children, style, isSecondaryText, className }) => {
18+
return (
19+
<Text
20+
className={classNames(Classes.UI_TEXT, className, isSecondaryText && Classes.TEXT_MUTED)}
21+
style={{ ...defaultStyles, ...style }}
22+
>
23+
{children}
24+
</Text>
25+
);
26+
};
27+
28+
export default GradingText;

src/commons/mocks/BackendMocks.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import DashboardActions from 'src/features/dashboard/DashboardActions';
55
import {
66
GradingOverviews,
77
GradingQuery,
8-
GradingQuestion
8+
GradingQuestion,
9+
SortStates
910
} from '../../features/grading/GradingTypes';
1011
import SessionActions from '../application/actions/SessionActions';
1112
import {
@@ -166,9 +167,25 @@ export function* mockBackendSaga(): SagaIterator {
166167
SessionActions.fetchGradingOverviews.type,
167168
function* (action: ReturnType<typeof actions.fetchGradingOverviews>): any {
168169
const accessToken = yield select((state: OverallState) => state.session.accessToken);
169-
const { filterToGroup, pageParams, filterParams } = action.payload;
170+
const { filterToGroup, pageParams, filterParams, allColsSortStates } = action.payload;
171+
const sortedBy = {
172+
sortBy: allColsSortStates.sortBy,
173+
sortDirection: ''
174+
};
175+
176+
Object.keys(allColsSortStates.currentState).forEach(key => {
177+
if (allColsSortStates.sortBy === key && key) {
178+
if (allColsSortStates.currentState[key] !== SortStates.NONE) {
179+
sortedBy.sortDirection = allColsSortStates.currentState[key];
180+
} else {
181+
sortedBy.sortBy = '';
182+
sortedBy.sortDirection = '';
183+
}
184+
}
185+
});
186+
170187
const gradingOverviews = yield call(() =>
171-
mockFetchGradingOverview(accessToken, filterToGroup, pageParams, filterParams)
188+
mockFetchGradingOverview(accessToken, filterToGroup, pageParams, filterParams, sortedBy)
172189
);
173190
if (gradingOverviews !== null) {
174191
yield put(actions.updateGradingOverviews(gradingOverviews));

src/commons/mocks/GradingMocks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@ export const mockFetchGradingOverview = (
100100
accessToken: string,
101101
group: boolean,
102102
pageParams: { offset: number; pageSize: number },
103-
backendParams: object
103+
backendParams: object,
104+
sortedBy: { sortBy: string; sortDirection: string }
104105
): GradingOverview[] | null => {
105106
// mocks backend role fetching
106107
const permittedRoles: Role[] = [Role.Admin, Role.Staff];

src/commons/sagas/BackendSaga.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ import {
1616
GradingOverview,
1717
GradingOverviews,
1818
GradingQuery,
19-
GradingQuestion
19+
GradingQuestion,
20+
SortStates
2021
} from '../../features/grading/GradingTypes';
2122
import { SourcecastData } from '../../features/sourceRecorder/SourceRecorderTypes';
2223
import SourcereelActions from '../../features/sourceRecorder/sourcereel/SourcereelActions';
@@ -358,15 +359,33 @@ const newBackendSagaOne = combineSagaHandlers(sagaActions, {
358359
return;
359360
}
360361

361-
const { filterToGroup, publishedFilter, pageParams, filterParams } = action.payload;
362+
const { filterToGroup, publishedFilter, pageParams, filterParams, allColsSortStates } =
363+
action.payload;
364+
365+
const sortedBy = {
366+
sortBy: allColsSortStates.sortBy,
367+
sortDirection: ''
368+
};
369+
370+
Object.entries(allColsSortStates.currentState).forEach(([key, value]) => {
371+
if (allColsSortStates.sortBy === key && key !== '') {
372+
if (value !== SortStates.NONE) {
373+
sortedBy.sortDirection = value;
374+
} else {
375+
sortedBy.sortBy = '';
376+
sortedBy.sortDirection = '';
377+
}
378+
}
379+
});
362380

363381
const gradingOverviews: GradingOverviews | null = yield call(
364382
getGradingOverviews,
365383
tokens,
366384
filterToGroup,
367385
publishedFilter,
368386
pageParams,
369-
filterParams
387+
filterParams,
388+
sortedBy
370389
);
371390
if (gradingOverviews) {
372391
yield put(actions.updateGradingOverviews(gradingOverviews));

src/commons/sagas/RequestsSaga.ts

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -625,10 +625,11 @@ export const getGradingOverviews = async (
625625
group: boolean,
626626
graded: Record<string, any> | undefined,
627627
pageParams: Record<string, any>,
628-
filterParams: Record<string, any>
628+
filterParams: Record<string, any>,
629+
sortedBy: Record<string, any>
629630
): Promise<GradingOverviews | null> => {
630631
// gradedQuery placed behind filterQuery to override progress filter if any
631-
const params = new URLSearchParams({ ...pageParams, ...filterParams, ...graded });
632+
const params = new URLSearchParams({ ...pageParams, ...filterParams, ...graded, ...sortedBy });
632633
params.append('group', `${group}`);
633634

634635
const resp = await request(`${courseId()}/admin/grading?${params.toString()}`, 'GET', {
@@ -641,50 +642,44 @@ export const getGradingOverviews = async (
641642

642643
return {
643644
count: gradingOverviews.count,
644-
data: gradingOverviews.data
645-
.map((overview: any) => {
646-
const gradingOverview: GradingOverview = {
647-
assessmentId: overview.assessment.id,
648-
assessmentNumber: overview.assessment.assessmentNumber,
649-
assessmentName: overview.assessment.title,
650-
assessmentType: overview.assessment.type,
651-
studentId: overview.student ? overview.student.id : -1,
652-
studentName: overview.student ? overview.student.name : undefined,
653-
studentNames: overview.team
654-
? overview.team.team_members.map((member: { name: any }) => member.name)
655-
: undefined,
656-
studentUsername: overview.student ? overview.student.username : undefined,
657-
studentUsernames: overview.team
658-
? overview.team.team_members.map((member: { username: any }) => member.username)
659-
: undefined,
660-
submissionId: overview.id,
661-
submissionStatus: overview.status,
662-
groupName: overview.student ? overview.student.groupName : '-',
663-
groupLeaderId: overview.student ? overview.student.groupLeaderId : undefined,
664-
isGradingPublished: overview.isGradingPublished,
665-
progress: backendParamsToProgressStatus(
666-
overview.assessment.isManuallyGraded,
667-
overview.isGradingPublished,
668-
overview.status,
669-
overview.gradedCount,
670-
overview.assessment.questionCount
671-
),
672-
questionCount: overview.assessment.questionCount,
673-
gradedCount: overview.gradedCount,
674-
// XP
675-
initialXp: overview.xp,
676-
xpAdjustment: overview.xpAdjustment,
677-
currentXp: overview.xp + overview.xpAdjustment,
678-
maxXp: overview.assessment.maxXp,
679-
xpBonus: overview.xpBonus
680-
};
681-
return gradingOverview;
682-
})
683-
.sort((subX: GradingOverview, subY: GradingOverview) =>
684-
subX.assessmentId !== subY.assessmentId
685-
? subY.assessmentId - subX.assessmentId
686-
: subY.submissionId - subX.submissionId
687-
)
645+
data: gradingOverviews.data.map((overview: any) => {
646+
const gradingOverview: GradingOverview = {
647+
assessmentId: overview.assessment.id,
648+
assessmentNumber: overview.assessment.assessmentNumber,
649+
assessmentName: overview.assessment.title,
650+
assessmentType: overview.assessment.type,
651+
studentId: overview.student ? overview.student.id : -1,
652+
studentName: overview.student ? overview.student.name : undefined,
653+
studentNames: overview.team
654+
? overview.team.team_members.map((member: { name: any }) => member.name)
655+
: undefined,
656+
studentUsername: overview.student ? overview.student.username : undefined,
657+
studentUsernames: overview.team
658+
? overview.team.team_members.map((member: { username: any }) => member.username)
659+
: undefined,
660+
submissionId: overview.id,
661+
submissionStatus: overview.status,
662+
groupName: overview.student ? overview.student.groupName : '-',
663+
groupLeaderId: overview.student ? overview.student.groupLeaderId : undefined,
664+
isGradingPublished: overview.isGradingPublished,
665+
progress: backendParamsToProgressStatus(
666+
overview.assessment.isManuallyGraded,
667+
overview.isGradingPublished,
668+
overview.status,
669+
overview.gradedCount,
670+
overview.assessment.questionCount
671+
),
672+
questionCount: overview.assessment.questionCount,
673+
gradedCount: overview.gradedCount,
674+
// XP
675+
initialXp: overview.xp,
676+
xpAdjustment: overview.xpAdjustment,
677+
currentXp: overview.xp + overview.xpAdjustment,
678+
maxXp: overview.assessment.maxXp,
679+
xpBonus: overview.xpBonus
680+
};
681+
return gradingOverview;
682+
})
688683
};
689684
};
690685

src/commons/workspace/WorkspaceActions.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createAction } from '@reduxjs/toolkit';
22
import { Context, Result } from 'js-slang';
33
import { Chapter, Variant } from 'js-slang/dist/types';
44

5+
import { AllColsSortStates, GradingColumnVisibility } from '../../features/grading/GradingTypes';
56
import { SALanguage } from '../application/ApplicationTypes';
67
import { ExternalLibraryName } from '../application/types/ExternalTypes';
78
import { Library } from '../assessment/AssessmentTypes';
@@ -264,7 +265,13 @@ const newActions = createActions('workspace', {
264265
updateChangePointSteps: (changepointSteps: number[], workspaceLocation: WorkspaceLocation) => ({
265266
changepointSteps,
266267
workspaceLocation
267-
})
268+
}),
269+
// For grading table
270+
increaseRequestCounter: 0,
271+
decreaseRequestCounter: 0,
272+
setGradingHasLoadedBefore: () => true,
273+
updateAllColsSortStates: (sortStates: AllColsSortStates) => ({ sortStates }),
274+
updateGradingColumnVisibility: (filters: GradingColumnVisibility) => ({ filters })
268275
});
269276

270277
export const updateLastDebuggerResult = createAction(

0 commit comments

Comments
 (0)