diff --git a/src/pages/approval-workflows/[id]/index.tsx b/src/pages/approval-workflows/[id]/index.tsx
index 64a8026e..c6cdc340 100644
--- a/src/pages/approval-workflows/[id]/index.tsx
+++ b/src/pages/approval-workflows/[id]/index.tsx
@@ -557,7 +557,7 @@ export default function Home({ user, initialWorkflow, id, workflowAssignees, wor
{currentWorkflow.steps[selectedStepIndex].examChanges?.length ? (
currentWorkflow.steps[selectedStepIndex].examChanges!.map((change, index) => (
-
+
{change}
))
diff --git a/src/utils/exam.differences.ts b/src/utils/exam.differences.ts
index 6a1cf303..b860e11f 100644
--- a/src/utils/exam.differences.ts
+++ b/src/utils/exam.differences.ts
@@ -1,9 +1,10 @@
import { Exam } from "@/interfaces/exam";
import { diff, Diff } from "deep-diff";
-const EXCLUDED_KEYS = new Set(["_id", "id", "createdAt", "createdBy", "entities", "isDiagnostic", "private", "access", "requiresApproval"]);
+const EXCLUDED_KEYS = new Set
(["_id", "id", "createdAt", "createdBy", "entities", "isDiagnostic", "private", "requiresApproval", "exerciseID", "questionID"]);
const PATH_LABELS: Record = {
+ access: "Access Type",
parts: "Parts",
exercises: "Exercises",
userSolutions: "User Solutions",
@@ -20,6 +21,9 @@ const PATH_LABELS: Record = {
prefix: "Prefix",
suffix: "Suffix",
topic: "Topic",
+ allowRepetition: "Allow Repetition",
+ maxWords: "Max Words",
+ minTimer: "Timer",
};
export function generateExamDifferences(oldExam: Exam, newExam: Exam): string[] {
@@ -28,9 +32,7 @@ export function generateExamDifferences(oldExam: Exam, newExam: Exam): string[]
}
function formatDifference(change: Diff): string | undefined {
- if (!change.path) {
- return;
- }
+ if (!change.path) return;
if (change.path.some((segment) => EXCLUDED_KEYS.has(segment))) {
return;
@@ -85,30 +87,53 @@ function formatValue(value: any): string {
if (typeof value === "object") {
try {
- return JSON.stringify(
- value,
- (key, val) => {
- if (EXCLUDED_KEYS.has(key)) {
- return undefined;
- }
- return val;
- },
- 2 // optional indentation for readability
- );
+ const sanitized = removeExcludedKeysDeep(value, EXCLUDED_KEYS);
+
+ const renamed = renameKeysDeep(sanitized, PATH_LABELS);
+
+ return JSON.stringify(renamed, null, 2);
} catch {
return String(value);
}
}
-
return JSON.stringify(value);
}
+function removeExcludedKeysDeep(obj: any, excludedKeys: Set): any {
+ if (Array.isArray(obj)) {
+ return obj.map((item) => removeExcludedKeysDeep(item, excludedKeys));
+ } else if (obj && typeof obj === "object") {
+ const newObj: any = {};
+ for (const key of Object.keys(obj)) {
+ if (excludedKeys.has(key)) {
+ // Skip this key entirely
+ continue;
+ }
+ newObj[key] = removeExcludedKeysDeep(obj[key], excludedKeys);
+ }
+ return newObj;
+ }
+ return obj;
+}
+
+function renameKeysDeep(obj: any, renameMap: Record): any {
+ if (Array.isArray(obj)) {
+ return obj.map((item) => renameKeysDeep(item, renameMap));
+ } else if (obj && typeof obj === "object") {
+ const newObj: any = {};
+ for (const key of Object.keys(obj)) {
+ const newKey = renameMap[key] ?? key; // Use friendly label if available
+ newObj[newKey] = renameKeysDeep(obj[key], renameMap);
+ }
+ return newObj;
+ }
+ return obj;
+}
+
/**
- * Convert an array of path segments into a friendlier string.
- * Example:
- * ["parts", 0, "exercises", 1, "prompt"]
- * becomes:
- * "Parts → [#1] → Exercises → [#2] → Prompt"
+ * Convert an array of path segments into a user-friendly string.
+ * e.g. ["parts", 0, "exercises", 1, "prompt"]
+ * → "Parts → [#1] → Exercises → [#2] → Prompt"
*/
function pathToHumanReadable(pathSegments: Array): string {
return pathSegments