feat: add PluginBlock component for rendering external web components in sandboxed iframes

feat: implement PluginsPage for managing plugins with create, toggle, and delete functionalities

feat: create PedagogicalAnalyticsPage for displaying course analytics including quality metrics, discrimination index, and curricular suggestions

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-27 12:22:05 -04:00
parent 675fa1e299
commit f6a3f6aedf
23 changed files with 2580 additions and 24 deletions
+43 -7
View File
@@ -15,9 +15,12 @@ pub struct BackgroundTask {
pub id: Uuid,
pub title: String,
pub course_title: Option<String>,
pub task_type: String, // 'transcription'
pub task_type: String,
pub status: String,
pub progress: i32,
pub processed_items: i64,
pub failed_items: i64,
pub error_message: Option<String>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
@@ -25,7 +28,8 @@ pub async fn get_background_tasks(
State(pool): State<PgPool>,
) -> Result<Json<Vec<BackgroundTask>>, (StatusCode, String)> {
let query = r#"
SELECT id, title, course_title, task_type, status, progress, updated_at
SELECT id, title, course_title, task_type, status, progress,
processed_items, failed_items, error_message, updated_at
FROM (
SELECT
l.id,
@@ -34,6 +38,9 @@ pub async fn get_background_tasks(
'lesson_transcription' as task_type,
l.transcription_status as status,
0 as progress,
0::bigint as processed_items,
0::bigint as failed_items,
NULL::text as error_message,
l.updated_at
FROM lessons l
JOIN modules m ON l.module_id = m.id
@@ -49,6 +56,9 @@ pub async fn get_background_tasks(
t.task_type,
t.status,
t.progress,
t.processed_items::bigint,
t.failed_items::bigint,
t.error_message,
t.updated_at
FROM background_tasks t
WHERE t.task_type = 'zip_rag_import'
@@ -289,13 +299,39 @@ pub async fn cancel_task(
State(pool): State<PgPool>,
Path(id): Path<Uuid>,
) -> Result<StatusCode, (StatusCode, String)> {
// Only cancel transcription in lessons
let _ = sqlx::query(
"UPDATE lessons SET transcription_status = 'idle' WHERE id = $1"
// Try to cancel a transcription lesson first
let lesson_result = sqlx::query(
"UPDATE lessons SET transcription_status = 'idle' WHERE id = $1 AND transcription_status IN ('queued', 'processing')"
)
.bind(id)
.execute(&pool)
.await;
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(StatusCode::NO_CONTENT)
if lesson_result.rows_affected() > 0 {
return Ok(StatusCode::NO_CONTENT);
}
// Try to cancel a zip_rag_import background task
let task_result = sqlx::query(
r#"
UPDATE background_tasks
SET status = 'failed',
error_message = 'Cancelado manualmente',
updated_at = NOW()
WHERE id = $1
AND task_type = 'zip_rag_import'
AND status IN ('queued', 'processing')
"#
)
.bind(id)
.execute(&pool)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
if task_result.rows_affected() > 0 {
return Ok(StatusCode::NO_CONTENT);
}
Ok(StatusCode::NOT_FOUND)
}