a11y: Enhance accessibility of form and interactive components using semantic HTML, ARIA attributes, and keyboard navigation.
This commit is contained in:
@@ -19,30 +19,28 @@ pub async fn submit_assignment(
|
||||
Json(payload): Json<SubmitAssignmentPayload>,
|
||||
) -> Result<Json<CourseSubmission>, (StatusCode, String)> {
|
||||
// Check if submission already exists
|
||||
let existing: Option<CourseSubmission> = sqlx::query_as!(
|
||||
CourseSubmission,
|
||||
"SELECT * FROM course_submissions WHERE user_id = $1 AND lesson_id = $2",
|
||||
claims.sub,
|
||||
lesson_id
|
||||
let existing: Option<CourseSubmission> = sqlx::query_as(
|
||||
"SELECT * FROM course_submissions WHERE user_id = $1 AND lesson_id = $2"
|
||||
)
|
||||
.bind(claims.sub)
|
||||
.bind(lesson_id)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
||||
if let Some(_) = existing {
|
||||
// Update existing submission
|
||||
let updated = sqlx::query_as!(
|
||||
CourseSubmission,
|
||||
let updated: CourseSubmission = sqlx::query_as(
|
||||
r#"
|
||||
UPDATE course_submissions
|
||||
SET content = $1, updated_at = NOW()
|
||||
WHERE user_id = $2 AND lesson_id = $3
|
||||
RETURNING *
|
||||
"#,
|
||||
payload.content,
|
||||
claims.sub,
|
||||
lesson_id
|
||||
"#
|
||||
)
|
||||
.bind(&payload.content)
|
||||
.bind(claims.sub)
|
||||
.bind(lesson_id)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -51,19 +49,18 @@ pub async fn submit_assignment(
|
||||
}
|
||||
|
||||
// Create new submission
|
||||
let submission = sqlx::query_as!(
|
||||
CourseSubmission,
|
||||
let submission: CourseSubmission = sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO course_submissions (user_id, course_id, lesson_id, organization_id, content)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *
|
||||
"#,
|
||||
claims.sub,
|
||||
course_id,
|
||||
lesson_id,
|
||||
org_ctx.id,
|
||||
payload.content
|
||||
"#
|
||||
)
|
||||
.bind(claims.sub)
|
||||
.bind(course_id)
|
||||
.bind(lesson_id)
|
||||
.bind(org_ctx.id)
|
||||
.bind(&payload.content)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -81,8 +78,7 @@ pub async fn get_peer_review_assignment(
|
||||
// 1. Is not my own
|
||||
// 2. Has fewer than 2 reviews (configurable, but hardcoded for now)
|
||||
// 3. I haven't reviewed yet
|
||||
let submission = sqlx::query_as!(
|
||||
CourseSubmission,
|
||||
let submission: Option<CourseSubmission> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT s.*
|
||||
FROM course_submissions s
|
||||
@@ -99,12 +95,12 @@ pub async fn get_peer_review_assignment(
|
||||
HAVING COUNT(pr.id) < 2
|
||||
ORDER BY s.submitted_at ASC
|
||||
LIMIT 1
|
||||
"#,
|
||||
course_id,
|
||||
lesson_id,
|
||||
claims.sub,
|
||||
org_ctx.id
|
||||
"#
|
||||
)
|
||||
.bind(course_id)
|
||||
.bind(lesson_id)
|
||||
.bind(claims.sub)
|
||||
.bind(org_ctx.id)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -120,20 +116,20 @@ pub async fn submit_peer_review(
|
||||
Json(payload): Json<SubmitPeerReviewPayload>,
|
||||
) -> Result<Json<PeerReview>, (StatusCode, String)> {
|
||||
// Verify valid submission
|
||||
let submission = sqlx::query!(
|
||||
"SELECT user_id FROM course_submissions WHERE id = $1",
|
||||
payload.submission_id
|
||||
let submission_row = sqlx::query(
|
||||
"SELECT user_id FROM course_submissions WHERE id = $1"
|
||||
)
|
||||
.bind(payload.submission_id)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
||||
let submission = match submission {
|
||||
Some(s) => s,
|
||||
let submission_user_id = match submission_row {
|
||||
Some(row) => row.get::<Uuid, _>("user_id"),
|
||||
None => return Err((StatusCode::NOT_FOUND, "Submission not found".to_string())),
|
||||
};
|
||||
|
||||
if submission.user_id == claims.sub {
|
||||
if submission_user_id == claims.sub {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Cannot review your own submission".to_string(),
|
||||
@@ -141,11 +137,11 @@ pub async fn submit_peer_review(
|
||||
}
|
||||
|
||||
// Check if already reviewed
|
||||
let existing = sqlx::query!(
|
||||
"SELECT id FROM peer_reviews WHERE submission_id = $1 AND reviewer_id = $2",
|
||||
payload.submission_id,
|
||||
claims.sub
|
||||
let existing = sqlx::query(
|
||||
"SELECT id FROM peer_reviews WHERE submission_id = $1 AND reviewer_id = $2"
|
||||
)
|
||||
.bind(payload.submission_id)
|
||||
.bind(claims.sub)
|
||||
.fetch_optional(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -158,19 +154,18 @@ pub async fn submit_peer_review(
|
||||
}
|
||||
|
||||
// Create review
|
||||
let review = sqlx::query_as!(
|
||||
PeerReview,
|
||||
let review: PeerReview = sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO peer_reviews (submission_id, reviewer_id, score, feedback, organization_id)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING *
|
||||
"#,
|
||||
payload.submission_id,
|
||||
claims.sub,
|
||||
payload.score,
|
||||
payload.feedback,
|
||||
org_ctx.id
|
||||
"#
|
||||
)
|
||||
.bind(payload.submission_id)
|
||||
.bind(claims.sub)
|
||||
.bind(payload.score)
|
||||
.bind(&payload.feedback)
|
||||
.bind(org_ctx.id)
|
||||
.fetch_one(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -185,17 +180,16 @@ pub async fn get_my_submission_feedback(
|
||||
Path((_course_id, lesson_id)): Path<(Uuid, Uuid)>,
|
||||
) -> Result<Json<Vec<PeerReview>>, (StatusCode, String)> {
|
||||
// Get reviews for my submission on this lesson
|
||||
let reviews = sqlx::query_as!(
|
||||
PeerReview,
|
||||
let reviews: Vec<PeerReview> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT pr.*
|
||||
FROM peer_reviews pr
|
||||
JOIN course_submissions cs ON pr.submission_id = cs.id
|
||||
WHERE cs.user_id = $1 AND cs.lesson_id = $2
|
||||
"#,
|
||||
claims.sub,
|
||||
lesson_id
|
||||
"#
|
||||
)
|
||||
.bind(claims.sub)
|
||||
.bind(lesson_id)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -209,12 +203,11 @@ pub async fn list_lesson_submissions(
|
||||
State(pool): State<PgPool>,
|
||||
Path((_course_id, lesson_id)): Path<(Uuid, Uuid)>,
|
||||
) -> Result<Json<Vec<SubmissionWithReviews>>, (StatusCode, String)> {
|
||||
let submissions = sqlx::query_as!(
|
||||
SubmissionWithReviews,
|
||||
let submissions: Vec<SubmissionWithReviews> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
s.id, s.user_id, u.full_name, u.email, s.submitted_at,
|
||||
COUNT(pr.id) as "review_count!",
|
||||
COUNT(pr.id) as review_count,
|
||||
AVG(pr.score)::float8 as average_score
|
||||
FROM course_submissions s
|
||||
JOIN users u ON s.user_id = u.id
|
||||
@@ -222,10 +215,10 @@ pub async fn list_lesson_submissions(
|
||||
WHERE s.lesson_id = $1 AND s.organization_id = $2
|
||||
GROUP BY s.id, u.full_name, u.email
|
||||
ORDER BY s.submitted_at DESC
|
||||
"#,
|
||||
lesson_id,
|
||||
org_ctx.id
|
||||
"#
|
||||
)
|
||||
.bind(lesson_id)
|
||||
.bind(org_ctx.id)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
@@ -239,11 +232,10 @@ pub async fn get_submission_reviews(
|
||||
State(pool): State<PgPool>,
|
||||
Path(submission_id): Path<Uuid>,
|
||||
) -> Result<Json<Vec<PeerReview>>, (StatusCode, String)> {
|
||||
let reviews = sqlx::query_as!(
|
||||
PeerReview,
|
||||
"SELECT * FROM peer_reviews WHERE submission_id = $1",
|
||||
submission_id
|
||||
let reviews: Vec<PeerReview> = sqlx::query_as(
|
||||
"SELECT * FROM peer_reviews WHERE submission_id = $1"
|
||||
)
|
||||
.bind(submission_id)
|
||||
.fetch_all(&pool)
|
||||
.await
|
||||
.map_err(|e: sqlx::Error| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
|
||||
|
||||
Reference in New Issue
Block a user