96 lines
3.0 KiB
TypeScript
96 lines
3.0 KiB
TypeScript
import { supabase } from '@/lib/supabase'
|
|
|
|
const ANSWER_VALUES = ['A', 'B', 'C', 'D'] as const
|
|
|
|
interface TestResultData {
|
|
testId: number | null
|
|
selectedParts: number[]
|
|
score: number
|
|
total: number
|
|
timeUsed: number
|
|
answers: { questionId: number; selected: number | null; correct: boolean }[]
|
|
}
|
|
|
|
/** Fire-and-forget: save test result. Failures are logged but don't block UI. */
|
|
export async function saveTestResult(userId: string, data: TestResultData): Promise<void> {
|
|
const { data: attempt, error: attemptError } = await supabase
|
|
.from('user_test_attempt')
|
|
.insert({
|
|
user_id: userId,
|
|
test_id: data.testId,
|
|
selected_parts: data.selectedParts,
|
|
time_limit_minutes: 10,
|
|
submitted_at: new Date().toISOString(),
|
|
time_spent_seconds: data.timeUsed,
|
|
total_correct: data.score,
|
|
total_questions: data.total,
|
|
})
|
|
.select('id')
|
|
.single()
|
|
|
|
if (attemptError) {
|
|
console.error('Failed to save test attempt:', attemptError.message)
|
|
return
|
|
}
|
|
|
|
const answerRows = data.answers.map(a => ({
|
|
attempt_id: attempt.id,
|
|
question_id: a.questionId,
|
|
selected_value: a.selected !== null ? ANSWER_VALUES[a.selected] : null,
|
|
is_correct: a.correct,
|
|
}))
|
|
|
|
const { error: answersError } = await supabase.from('user_answer').insert(answerRows)
|
|
if (answersError) console.error('Failed to save answers:', answersError.message)
|
|
}
|
|
|
|
/** Fire-and-forget: save writing submission with AI feedback. */
|
|
export async function saveWritingSubmission(
|
|
userId: string,
|
|
content: string,
|
|
feedback: object,
|
|
): Promise<void> {
|
|
const { error } = await supabase.from('writing_submissions').insert({
|
|
user_id: userId,
|
|
content,
|
|
feedback,
|
|
})
|
|
if (error) console.error('Failed to save writing submission:', error.message)
|
|
}
|
|
|
|
/** Count today's writing submissions for server-side rate limiting (authenticated users). */
|
|
export async function countTodayWritingSubmissions(userId: string): Promise<number> {
|
|
const today = new Date().toISOString().split('T')[0]
|
|
const { count, error } = await supabase
|
|
.from('writing_submissions')
|
|
.select('*', { count: 'exact', head: true })
|
|
.eq('user_id', userId)
|
|
.gte('created_at', `${today}T00:00:00.000Z`)
|
|
if (error) return 0
|
|
return count ?? 0
|
|
}
|
|
|
|
/** Fetch test history for a user (most recent first, max 20). */
|
|
export async function fetchTestHistory(userId: string) {
|
|
const { data, error } = await supabase
|
|
.from('user_test_attempt')
|
|
.select('id, selected_parts, time_spent_seconds, total_correct, total_questions, score, submitted_at, created_at')
|
|
.eq('user_id', userId)
|
|
.order('created_at', { ascending: false })
|
|
.limit(20)
|
|
if (error) throw error
|
|
return data ?? []
|
|
}
|
|
|
|
/** Fetch writing history for a user (most recent first, max 20). */
|
|
export async function fetchWritingHistory(userId: string) {
|
|
const { data, error } = await supabase
|
|
.from('writing_submissions')
|
|
.select('id, content, feedback, created_at')
|
|
.eq('user_id', userId)
|
|
.order('created_at', { ascending: false })
|
|
.limit(20)
|
|
if (error) throw error
|
|
return data ?? []
|
|
}
|