Skip to content
Draft

H4 #348

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f282ce8
initial
AidanLoran May 9, 2026
fa5197b
Merge branch 'main' of https://github.com/UTDallasEPICS/Reading-MLK i…
AidanLoran May 30, 2026
34bc092
almost finished splitting listForms action and updating admin composable
AidanLoran Jun 1, 2026
ca13656
match from group by date, prisma inclusions, and listforms action ext…
AidanLoran Jun 2, 2026
07b97b5
useAdmin updated for listForms, preupdate for other composables
AidanLoran Jun 2, 2026
359d688
removing tests, fixing +consolidating handlers
AidanLoran Jun 2, 2026
bc3a57a
test handler gone + index.ts trimming
AidanLoran Jun 4, 2026
8eb26b0
form put handler split
AidanLoran Jun 4, 2026
7141cac
admin.ts put form endpoint updated
AidanLoran Jun 4, 2026
885dc0a
removed unused deleteForm action
AidanLoran Jun 4, 2026
5253f7d
fixed boolean coercion issue for form/list.get
AidanLoran Jun 4, 2026
aead746
Merge branch 'main' of https://github.com/UTDallasEPICS/Reading-MLK i…
AidanLoran Jun 9, 2026
97b8f5e
listFormGroups, getOnlyActiveForms, getFormGroup split
AidanLoran Jun 10, 2026
7aef679
ResolveDateByRange + other fixes
AidanLoran Jun 10, 2026
f3c4343
formgroup and form post handlers
AidanLoran Jun 11, 2026
b782fc5
beginning of endpoint swap for form post
AidanLoran Jun 11, 2026
1407945
form component post
AidanLoran Jun 11, 2026
bb322aa
formgroup and formcomponent put handlers
AidanLoran Jun 11, 2026
c8d0cb2
formgroup and component delete handlers
AidanLoran Jun 11, 2026
953cb01
handlers fixed, need to update endpoints, migrate helpers, and merge …
AidanLoran Jun 11, 2026
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
74 changes: 37 additions & 37 deletions app/composables/useAdmin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Place this at: app/composables/useAdmin.ts
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import type { Form } from '~~/prisma/generated/client'

dayjs.extend(utc)
export const useAdmin = () => {
Expand Down Expand Up @@ -81,19 +82,21 @@ export const useAdmin = () => {
}

const mapApiFormToUi = (form: any) => {
const questionList = Array.isArray(form.questions) ? form.questions : []
const questionList = Array.isArray(form.Components) ? form.Components : []
questionList.slice().sort((left: any, right: any) => left.order - right.order)

//replace direct type casting with better alternative, maybe zod coercing?
return {
id: Number(form.id),
weekStart: parseDateToYmd(form.weekStart || form.startDate || ''),
day: form.day || 'Monday',
title: form.title || `Form ${form.id}`,
date: form.date || formatDate(form.startDate || ''),
status: form.status || (form.published ? 'Active' : 'Unpublished'),
questions: questionList.map((question: any, index: number) => ({
id: Number(question.id ?? index + 1),
type: question.type || question.questionType || 'text',
text: question.text || question.questionText || '',
weekStart: parseDateToYmd(form.startDate), //update to dayjs format function
day: dayjs(form.startDate).format('dddd'),
title: form.title,
date: formatDate(form.startDate || ''), //update to dayjs format function
status: form.published ? 'Active' : 'Unpublished',
questions: questionList.map((question: any) => ({
id: question.id,
type: question.questionType || 'text',
text: question.questionText || '',
textEs: question.questionOptions?.textEs || '',
reference: question.questionOptions?.reference || '',
referenceEs: question.questionOptions?.referenceEs || '',
Expand Down Expand Up @@ -220,10 +223,8 @@ export const useAdmin = () => {

const loadPublishedForms = async () => {
try {
const forms = await callFormApi<any[]>('GET', {
action: 'listForms',
weeklyDate: historyWeekStart.value || undefined,
})
const forms = await $fetch<any[]>('/api/form/list',
{ query: { weeklyDate: historyWeekStart.value || undefined} })
publishedForms.value = (forms ?? []).map(mapApiFormToUi)
} catch (error) {
console.error('Failed to load forms', error)
Expand All @@ -241,23 +242,20 @@ export const useAdmin = () => {
const fallbackWeekEnd = dayjs.utc(fallbackWeekStart).add(6, 'day').format('YYYY-MM-DD')

try {
const result = await callFormApi<{
found: boolean
startDate: string | null
endDate: string | null
}>('GET', {
action: 'resolveFormGroupRangeByDate',
weeklyDate: historyWeekStart.value,
const result = await useFetch('/api/formGroup/matchByDate', {
method: 'GET',
query: {date: historyWeekStart.value}
})

if (!result?.found) {
if (!result.data.value) {
historyGroupStartDate.value = fallbackWeekStart
historyGroupEndDate.value = fallbackWeekEnd
return
}
result.data.value

historyGroupStartDate.value = parseDateToYmd(result.startDate || '')
historyGroupEndDate.value = parseDateToYmd(result.endDate || '') || fallbackWeekEnd
historyGroupStartDate.value = parseDateToYmd(result.data.value.startDate || '')
historyGroupEndDate.value = parseDateToYmd(result.data.value.endDate || '') || fallbackWeekEnd
} catch (error) {
console.error('Failed to resolve form group range', error)
historyGroupStartDate.value = fallbackWeekStart
Expand Down Expand Up @@ -324,12 +322,13 @@ export const useAdmin = () => {
if (!startDate) throw new Error('Invalid week start date')
startDate.setDate(startDate.getDate() + dayIndex)

await callFormApi('PUT', {}, {
action: 'updateForm',
id: editingFormId.value,
startDate: formatYmdLocal(startDate),
published: true,
title: formTitle.value,
await useFetch(`/api/form/${editingFormId.value}`, {
method: 'PUT',
body: {
startDate: formatYmdLocal(startDate), //swap to dayjs
published: true,
title: formTitle.value,
}
})

const existingForm = publishedForms.value.find((form) => Number(form.id) === Number(editingFormId.value))
Expand Down Expand Up @@ -394,15 +393,16 @@ export const useAdmin = () => {
}

startDate.setDate(startDate.getDate() + dayIndex)

const createdFormResponse = await callFormApi<any>('POST', {}, {
action: 'createForm',
startDate: formatYmdLocal(startDate),
published: true,
title: formTitle.value,
const createdFormResponse: any = await useFetch('api/form', {
method: 'POST',
body: {
startDate: dayjs(startDate).toISOString(),
published: true,
title: formTitle.value,
}
})

const createdForm = createdFormResponse?.data
const createdForm: Form = createdFormResponse?.data.value

if (!createdForm?.id) {
continue
Expand Down
44 changes: 21 additions & 23 deletions app/composables/useCurrentFormGroup.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FormGroup, Form, FormComponent } from '~~/prisma/generated/client'
import dayjs from 'dayjs'

export type CurrentFormGroupState = {
activeFormGroup: FormGroup | null
Expand All @@ -13,40 +14,38 @@ export const useCurrentFormGroup = () => {
formComponents: {}
}))

const loadFormComponents = async (formId: number) => {
try {
const componentsAPIResponse = await $fetch<FormComponent[]>('/api/formComponent', {
query: { form: formId }
})
FormGroup.value.formComponents[formId] = Array.isArray(componentsAPIResponse) ? componentsAPIResponse : []
} catch (error) {
console.error(`Failed to load form components for form ${formId}:`, error)
FormGroup.value.formComponents[formId] = []
}
}

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: { action: 'getOnlyActiveFormsinGroup', formGroup: activeFg.id }
const formsAPIResponse = await useFetch('/api/form/list', {
method: 'GET',
query: { published: 1, formGroup: activeFg.id }
})

FormGroup.value.forms = Array.isArray(formsAPIResponse) ? formsAPIResponse : []

// Load form components for each form in parallel

FormGroup.value.formComponents = {}
await Promise.all(
FormGroup.value.forms.map(form => loadFormComponents(form.id))
)
FormGroup.value.forms = []

if (formsAPIResponse.data.value) {
FormGroup.value.formComponents = {}
FormGroup.value.forms = formsAPIResponse.data.value.map((form) => {
const {Components, FormGroup: _removedFormGroupField, startDate, endDate, ...restOfForm} = form

FormGroup.value.formComponents[form.id] = Components ?? []

return {
...restOfForm,
startDate: dayjs(startDate).toDate(),
endDate: endDate ? dayjs(endDate).toDate() : null,
}
})
}
} catch (error) {
console.error('Failed to load forms for active form group:', error)
FormGroup.value.forms = []
Expand All @@ -70,7 +69,6 @@ export const useCurrentFormGroup = () => {
return {
FormGroup,
loadActiveFormGroup,
loadFormComponents,
totalFormsInGroup
}
}
4 changes: 4 additions & 0 deletions app/composables/useRaffleSpin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,16 @@ export const useRaffleSpin = () => {
const raffleWinner = ref<Student | null>(null)
const spinCount = ref(0)

//match form group by date, raffle winner include added
const loadRaffleFormGroup = async () => {
const val = raffleWeekStart.value
const dateStr = val instanceof Date ? val.toISOString().split('T')[0] : String(val).split('T')[0]
//merge formGroup/list.ts with index.ts
const data = await $fetch<FormGroup>(`/api/formGroup?date=${dateStr}`, { method: 'GET' })
raffleFormGroup.value = data || null
}

//wtf is it even calling
const loadRaffleForms = async () => {
if (!raffleFormGroup.value) {
raffleForms.value = []
Expand Down Expand Up @@ -59,6 +62,7 @@ export const useRaffleSpin = () => {
const studentId = winningSubmission.student

try {
//make formGroup/index.put.ts
await $fetch(`/api/formGroup`, {
method: 'PUT',
body: {
Expand Down
4 changes: 2 additions & 2 deletions server/api/announcement/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export default defineEventHandler(async (event) => {
return await prisma.announcement.create({
data: {
content: body.data.content,
postDate: new Date(body.data.postDate),
expiryDate: body.data.expiryDate ? new Date(body.data.expiryDate) : null,
postDate: body.data.postDate,
expiryDate: body.data.expiryDate ?? body.data.expiryDate,
author: body.data.author,
}
})
Expand Down
31 changes: 31 additions & 0 deletions server/api/form/[id].put.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Prisma } from '~~/prisma/generated/client'
import { auth } from '~~/server/utils/auth'
import { prisma } from '~~/server/utils/prisma'
import { getQuery, setResponseStatus, type H3Event } from 'h3'
import { formUpdateSchema } from '~~/server/utils/schemas'
import { z } from 'zod'

export default eventHandler(async (event) => {

//require sessions
const idParam = getRouterParam(event, 'id')
if (!idParam) {
throw createError({
statusCode: 400,
message: "Missing ID"
})
}

const body = formUpdateSchema.safeParse(await readBody(event))

if (!body.success) {throw createError({ statusCode: 400, message: body.error.message })}

const id = z.coerce.number().safeParse(idParam)

if (!id.success) {throw createError({ statusCode: 400, message: 'Invalid form ID'})}

return await prisma.form.update({
where: { id: id.data },
data: body.data
})
})
Empty file removed server/api/form/[id].ts
Empty file.
57 changes: 57 additions & 0 deletions server/api/form/index.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { prisma } from '../../utils/prisma'
import { Prisma } from '~~/prisma/generated/client'
import { getQuery, createError } from 'h3'
import { requireAdmin, requireSession } from '../../utils/require-session'
import { formInclude } from '../../utils/prismaInclusions'
import { formCreateSchema } from '../../utils/schemas'

export default defineEventHandler (async (event) => {

//require admin
const body = await readBody(event)

if (!body) {
throw createError({
statusCode: 400,
message: "Missing Request Body"
})
}

const form = formCreateSchema.safeParse(body)

if (!form.success) {
throw createError({
statusCode: 400,
message: form.error.message
})
}

const requestedGroupExists = await prisma.formGroup.findUnique({
where: {id: form.data.formGroup}, }).then(Boolean)

if (!requestedGroupExists) {
throw createError({
statusCode: 404,
message: "FormGroup Not Found"
})
}

const existingMax = await prisma.form.aggregate({
where: { formGroup: form.data.formGroup },
_max: { order: true },
})

form.data.order = ((existingMax._max.order ?? -1) + 1)

const created = await prisma.form.create({
data: form.data as Prisma.FormUncheckedCreateInput,
include: formInclude
})

return {
success: true,
message: 'Form Created',
data: created
}

})
Loading