Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
998523d
Implement Versioning and History of Editor Code for Assessment Worksp…
Nocaxe Mar 8, 2026
ef29046
Create tests for versioning and history feature
Nocaxe Mar 9, 2026
fae8626
Prevent submit answer from displaying success message, as submit stat…
Nocaxe Mar 9, 2026
9de2544
Fix updateVersionName API route and getVersionHistory return value
Nocaxe Mar 9, 2026
30b0d51
Fix bug causing double saving, now only submits answer, and allows th…
Nocaxe Mar 9, 2026
de8311a
Change restored versions to be named "(name)-restored" instead of "(t…
Nocaxe Mar 9, 2026
d35c2fe
Prevent autosave in team assessments
Nocaxe Mar 11, 2026
2fc355b
Implement UI for Versioning and History feature
Nocaxe Mar 13, 2026
bd20063
Implement version preview before restoring
Nocaxe Mar 13, 2026
de13c58
Fix bug causing duplicate versions to be saved
Nocaxe Mar 15, 2026
c9367d6
Prevent save status indicator from showing in team assessments
Nocaxe Mar 15, 2026
ed49c48
Update test to not check for success message upon autosave
Nocaxe Mar 15, 2026
0635aae
Update snapshots to align with changes
Nocaxe Mar 15, 2026
bc361e3
Reformatted imports
Nocaxe Mar 15, 2026
13849ea
Fix bug where save status is not correctly checked and updated, alway…
Nocaxe Mar 16, 2026
784c395
Refactor restoreVersionSaga and prevent restoring from auto-submittin…
Nocaxe Mar 16, 2026
f504c5c
Fix bug where renaming updates the timestamp
Nocaxe Mar 16, 2026
cebe414
Fix bug where restoring was renaming the wrong version
Nocaxe Mar 16, 2026
1714803
Merge branch 'master' into Versioning-and-History
martin-henz Mar 16, 2026
78129b8
Merge branch 'master' into Versioning-and-History
Nocaxe Mar 23, 2026
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
8 changes: 7 additions & 1 deletion src/commons/application/ApplicationTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,13 @@ export const createDefaultWorkspace = (workspaceLocation: WorkspaceLocation): Wo
debuggerContext: {} as DebuggerContext,
lastDebuggerResult: undefined,
files: {},
updateUserRoleCallback: () => {}
updateUserRoleCallback: () => {},
versionHistory: {
versions: [],
isLoading: false,
isHistoryPanelOpen: false
},
saveStatus: 'idle'
});

const defaultFileName = 'program.js';
Expand Down
76 changes: 59 additions & 17 deletions src/commons/assessmentWorkspace/AssessmentWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
import { ControlBarResetButton } from '../controlBar/ControlBarResetButton';
import { ControlBarRunButton } from '../controlBar/ControlBarRunButton';
import { ControlButtonSaveButton } from '../controlBar/ControlBarSaveButton';
import { ControlBarSaveStatusIndicator } from '../controlBar/ControlBarSaveStatusIndicator';
import { ControlBarVersionHistoryButton } from '../controlBar/ControlBarVersionHistoryButton';
import { VersionHistoryPanel } from '../controlBar/VersionHistoryPanel';
import ControlButton from '../ControlButton';
import {
convertEditorTabStateToProps,
Expand Down Expand Up @@ -127,7 +130,9 @@
output,
replValue,
currentAssessment: storedAssessmentId,
currentQuestion: storedQuestionId
currentQuestion: storedQuestionId,
versionHistory,
saveStatus
} = useTypedSelector(store => store.workspaces[workspaceLocation]);

const dispatch = useDispatch();
Expand All @@ -148,7 +153,11 @@
handleCheckLastModifiedAt,
handleUpdateHasUnsavedChanges,
handleEnableTokenCounter,
handleDisableTokenCounter
handleDisableTokenCounter,
handleFetchVersionHistory,
handleToggleHistoryPanel,
handleRestoreVersion,
handleNameVersion
} = useMemo(() => {
return {
handleTeamOverviewFetch: (assessmentId: number) =>
Expand Down Expand Up @@ -185,7 +194,15 @@
handleEnableTokenCounter: () =>
dispatch(WorkspaceActions.enableTokenCounter(workspaceLocation)),
handleDisableTokenCounter: () =>
dispatch(WorkspaceActions.disableTokenCounter(workspaceLocation))
dispatch(WorkspaceActions.disableTokenCounter(workspaceLocation)),
handleFetchVersionHistory: () =>
dispatch(WorkspaceActions.fetchVersionHistory(workspaceLocation)),
handleToggleHistoryPanel: () =>
dispatch(WorkspaceActions.toggleHistoryPanel(workspaceLocation)),
handleRestoreVersion: (versionId: string) =>
dispatch(WorkspaceActions.restoreVersion(workspaceLocation, versionId)),
handleNameVersion: (versionId: string, name: string) =>
dispatch(WorkspaceActions.nameVersion(workspaceLocation, versionId, name))
};
}, [dispatch]);

Expand All @@ -198,7 +215,7 @@
dispatch(WorkspaceActions.updateEditorValue(workspaceLocation, 0, code));
dispatch(LeaderboardActions.clearCode());
}
}, [dispatch]);

Check warning on line 218 in src/commons/assessmentWorkspace/AssessmentWorkspace.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

React Hook useEffect has missing dependencies: 'code', 'initialRunCompleted', 'props.fromContestLeaderboard', and 'votingId'. Either include them or remove the dependency array

useEffect(() => {
if (assessmentOverview && assessmentOverview.maxTeamSize > 1) {
Expand Down Expand Up @@ -410,6 +427,7 @@
);
handleClearContext(question.library, true);
handleUpdateHasUnsavedChanges(false);
handleFetchVersionHistory();

const chapter = question.library.chapter;
const questionType = question.type;
Expand Down Expand Up @@ -778,25 +796,34 @@
/>
);

// Define the function to check if the Save button should be disabled
const shouldDisableSaveButton = (): boolean | undefined => {
const isIndividualAssessment: boolean = assessmentOverview?.maxTeamSize === 1;
if (isIndividualAssessment) {
return false;
}
return !teamFormationOverview;
};
const isTeamAssessment: boolean = assessmentOverview?.maxTeamSize !== 1;

const saveButton =
props.canSave && question.type === QuestionTypes.programming ? (
isTeamAssessment && question.type === QuestionTypes.programming ? (
<ControlButtonSaveButton
key="save"
hasUnsavedChanges={hasUnsavedChanges}
isDisabled={shouldDisableSaveButton()}
onClickSave={onClickSave}
key="save"
isDisabled={!props.canSave}
/>
) : null;

const versionHistoryButton =
question.type !== QuestionTypes.mcq ? (
<ControlBarVersionHistoryButton
onClick={() => {
handleFetchVersionHistory();
handleToggleHistoryPanel();
}}
key="version_history"
/>
) : null;

const saveStatusIndicator =
question.type !== QuestionTypes.mcq && !isTeamAssessment ? (
<ControlBarSaveStatusIndicator saveStatus={saveStatus} key="save_status" />
) : null;

const chapterSelect = (
<ControlBarChapterSelect
handleChapterSelect={() => {}}
Expand All @@ -811,8 +838,15 @@
return {
editorButtons:
!isMobileBreakpoint || isVscode
? [runButton, saveButton, resetButton, chapterSelect]
: [saveButton, resetButton],
? [
runButton,
saveButton,
resetButton,
versionHistoryButton,
saveStatusIndicator,
chapterSelect
]
: [resetButton],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

It seems the save button for team assessments has been unintentionally removed from the mobile view. The editorButtons array for mobile only contains the resetButton. For team assessments, where auto-save is disabled, the manual save button is still required. The versionHistoryButton and saveStatusIndicator should also likely be available on mobile.

I suggest including these buttons in the mobile view. React will filter out any null buttons automatically.

Suggested change
: [resetButton],
: [saveButton, resetButton, versionHistoryButton, saveStatusIndicator],

flowButtons: [previousButton, questionView, nextButton]
};
};
Expand Down Expand Up @@ -943,7 +977,7 @@
>
<DialogBody>
<Markdown content="Are you sure you want to reset the template?" />
<Markdown content="*Note this will not affect the saved copy of your program, unless you save over it.*" />
<Markdown content="*Note this will not affect your version history.*" />
</DialogBody>
<DialogFooter
actions={
Expand Down Expand Up @@ -1050,6 +1084,14 @@
{submissionOverlay}
{overlay}
{resetTemplateOverlay}
<VersionHistoryPanel
versions={versionHistory.versions}
isOpen={versionHistory.isHistoryPanelOpen}
isLoading={versionHistory.isLoading}
onClose={handleToggleHistoryPanel}
onRestore={handleRestoreVersion}
onRename={handleNameVersion}
/>
{isVscode || !isMobileBreakpoint ? (
<Workspace {...workspaceProps} />
) : (
Expand Down
Loading
Loading