Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions app/composables/currentStudent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import type { Student, Form, FormGroup, FormSubmission } from '~~/prisma/generated/client'
export type StudentSettings = {
dyslexiaFont: boolean
language: string
fontSize: number
}

export const useCurrentStudent = () => {
// useState creates global state shared across all components and pages without needing constant API fetches
const student = useState<Student | null>('currentStudent', () => null)

// Computes guaranteed fallback settings from the active student
const settings = computed<StudentSettings>(() => {
const raw = student.value?.settings as Partial<StudentSettings> || {}
return {
dyslexiaFont: Boolean(raw.dyslexiaFont),
language: raw.language || 'en',
fontSize: Number(raw.fontSize) || 1,
}
})

// Fetch a student directly from the DB by ID
const loadStudent = async (id: number) => {
try {
student.value = await $fetch<Student>(`/api/student/${id}`)
} catch (e) {
console.error("Failed to load student", e)
}
}

// Save the new settings to the DB and instantly update the sitewide global state
const saveSettings = async (newSettings: Partial<StudentSettings>) => {
if (!student.value?.id) return

const updatedSettings = { ...settings.value, ...newSettings }

try {
const updatedStudent = await $fetch<Student>(`/api/student/${student.value.id}`, {
method: 'PUT',
body: {
settings: updatedSettings
}
})

// Update the global state with the response, automatically passing it to pages like Home.vue
student.value = updatedStudent
} catch (error) {
console.error('Failed to save settings:', error)
}
}

return {
student,
settings,
loadStudent,
saveSettings
}
}
52 changes: 52 additions & 0 deletions app/composables/useCurrentFormGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { FormGroup, Form } from '~~/prisma/generated/client'

export type CurrentFormGroupState = {
activeFormGroup: FormGroup | null
forms: Form[]
}

export const useCurrentFormGroup = () => {
const FormGroup = useState<CurrentFormGroupState>('currentFormGroupState', () => ({
activeFormGroup: null,
forms: []
}))

const loadActiveFormGroup = async () => {
try {
const formGroupAPIResponse = await $fetch<FormGroup | FormGroup[]>('/api/formgroup?active=true')

// Handle if the API returns an array or single item
const activeFg = Array.isArray(formGroupAPIResponse) ? formGroupAPIResponse[0] : formGroupAPIResponse

if (activeFg) {
FormGroup.value.activeFormGroup = activeFg

try {
const formsAPIResponse = await $fetch<Form[]>('/api/form', {
query: { formGroup: activeFg.id }
})

FormGroup.value.forms = Array.isArray(formsAPIResponse) ? formsAPIResponse : []
} catch (error) {
console.error('Failed to load forms for active form group:', error)
FormGroup.value.forms = []
}
} else {
FormGroup.value.activeFormGroup = null
FormGroup.value.forms = []
}
} catch (error) {
console.error('Failed to load active form group:', error)
FormGroup.value.activeFormGroup = null
FormGroup.value.forms = []
}
}

const totalFormsInGroup = computed(() => FormGroup.value.forms.length)

return {
FormGroup,
loadActiveFormGroup,
totalFormsInGroup
}
}
77 changes: 77 additions & 0 deletions app/composables/useCurrentStudentProgress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { FormSubmission } from '~~/prisma/generated/client'
import { useCurrentStudent } from './currentStudent'
import { useCurrentFormGroup } from './useCurrentFormGroup'

export const useCurrentStudentProgress = () => {
const { student } = useCurrentStudent()
const { FormGroup: formGroupState, totalFormsInGroup: totalFormsInGroup } = useCurrentFormGroup()

//State to hold the student's submissions for the active form group
const submissions = useState<FormSubmission[]>('currentStudentSubmissions', () => [])

//Computed state for an array of completed form IDs within the active form group
const completedFormIds = computed(() => {
const activeFormIds = new Set(formGroupState.value.forms.map((f) => f.id))
return submissions.value
.filter((sub) => activeFormIds.has(sub.form))
.map((sub) => sub.form)
})

// Computed progress metrics
const tickets = computed(() => completedFormIds.value.length)
const isFormGroupCompleted = computed(() => {
return totalFormsInGroup.value > 0 && tickets.value >= totalFormsInGroup.value
})

// Fetch student's progress (form submissions)
const loadProgress = async () => {
if (!student.value?.id) {
submissions.value = []
return
}

try {
const response = await $fetch<FormSubmission[]>('/api/formSubmission', {
query: { student: student.value.id }
})
submissions.value = Array.isArray(response) ? response : []
} catch (error) {
console.error('Failed to load student progress:', error)
submissions.value = []
}
}

// Log a new form submission to track progress
const logFormSubmission = async (formId: number) => {
if (!student.value?.id) {
console.error('Cannot log form submission: No student is currently active.')
return null
}

try {
const newSubmission = await $fetch<FormSubmission>('/api/formSubmission', {
method: 'POST',
body: {
student: student.value.id,
form: formId
}
})

// Update local state without needing a new fetch
submissions.value.push(newSubmission)
return newSubmission
} catch (error) {
console.error('Failed to log form submission:', error)
return null
}
}

return {
submissions,
completedFormIds,
tickets,
isFormGroupCompleted,
loadProgress,
logFormSubmission
}
}
Loading