feat: QA fixes, new APIs (assignments, rubrics, custom exams), Generation page enhancements
- Fix ELAI video generation (correct render endpoint, script splitting for 60s limit) - Fix speaking script generation error handling and empty response display - Add custom exam list API (GET /api/exam/custom/list) - Add assignments REST API (list, create, get) - Add rubrics REST API (list, create) - Enhance Generation page: dynamic exam structures, auto-module selection, preview dialog, audio player - Improve submit feedback with exam ID and status in toast notifications - Fix ExamsListPage to show both custom exams and exam sessions - Connect RubricsPage to backend API with fallback data - Add Dockerfile, docker-compose.yml, requirements.txt for deployment - Fix placement, grading, scoring, and auth controllers - Add ErrorBoundary component for frontend resilience - Add QA report and credentials documentation Made-with: Cursor
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -12,6 +12,23 @@ import { useGenerateOutline, useGenerateChapterContent, usePublishWorkbench } fr
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import type { WorkbenchGeneratedOutline, WorkbenchGeneratedChapter } from "@/types/courseware";
|
||||
|
||||
function sanitizeHtml(dirty: string): string {
|
||||
const div = document.createElement("div");
|
||||
div.textContent = "";
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(dirty, "text/html");
|
||||
const scripts = doc.querySelectorAll("script, iframe, object, embed, link[rel=import]");
|
||||
scripts.forEach((el) => el.remove());
|
||||
doc.querySelectorAll("*").forEach((el) => {
|
||||
for (const attr of Array.from(el.attributes)) {
|
||||
if (attr.name.startsWith("on") || attr.value.trim().toLowerCase().startsWith("javascript:")) {
|
||||
el.removeAttribute(attr.name);
|
||||
}
|
||||
}
|
||||
});
|
||||
return doc.body.innerHTML;
|
||||
}
|
||||
|
||||
type Complexity = "beginner" | "intermediate" | "advanced";
|
||||
|
||||
export default function AiWorkbench() {
|
||||
@@ -160,7 +177,7 @@ export default function AiWorkbench() {
|
||||
<CardDescription>Review the detailed content before publishing.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="prose prose-sm max-w-none p-4 rounded-lg border bg-muted/30" dangerouslySetInnerHTML={{ __html: generatedContent.content }} />
|
||||
<div className="prose prose-sm max-w-none p-4 rounded-lg border bg-muted/30" dangerouslySetInnerHTML={{ __html: sanitizeHtml(generatedContent.content) }} />
|
||||
{generatedContent.exercises.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<h3 className="font-medium">Exercises ({generatedContent.exercises.length})</h3>
|
||||
|
||||
Reference in New Issue
Block a user