three60/src/routes/[accessToken]/+page.server.ts
2024-12-15 09:11:40 +01:00

95 lines
No EOL
4.1 KiB
TypeScript

import { error, redirect } from '@sveltejs/kit';
import { db } from '../../db';
import { surveyAccessTable, surveyAnswersTable, surveySkillsTable, surveysTable } from '../../db/schema';
import type { PageServerLoad } from './$types';
import { eq } from 'drizzle-orm';
import debug from 'debug';
const log_load = debug('survey:load');
const log_store = debug('survey:store');
export const load: PageServerLoad = async ({ params, url }) => {
const results = await db.select()
.from(surveyAccessTable)
.where(eq(surveyAccessTable.accessToken, params.accessToken))
.innerJoin(surveysTable, eq(surveysTable.id, surveyAccessTable.surveyId))
.limit(1);
if (results.length === 0) {
log_load('Survey not found: %s', params.accessToken);
error(404, 'Survey not found');
}
if (await db.$count(surveyAnswersTable, eq(surveyAnswersTable.participantId, results[0].survey_access_table.id)) > 0) {
log_load('Answers already submitted: %s', params.accessToken);
error(400, 'Answers already submitted');
}
const survey = results[0].surveys_table;
const skills = await db.select().from(surveySkillsTable).where(eq(surveySkillsTable.surveyId, survey.id));
return {
title: survey.title,
description: survey.description,
skills: skills.map(skills => ({
id: skills.id,
title: skills.title,
description: skills.description,
}))
}
}
export const actions = {
default: async ({ request, params }) => {
const results = await db.select()
.from(surveyAccessTable)
.where(eq(surveyAccessTable.accessToken, params.accessToken))
.innerJoin(surveysTable, eq(surveysTable.id, surveyAccessTable.surveyId))
.limit(1);
if (results.length === 0) {
log_store('Survey not found: %s', params.accessToken);
error(404, 'Survey not found');
}
if (await db.$count(surveyAnswersTable, eq(surveyAnswersTable.participantId, results[0].survey_access_table.id)) > 0) {
log_store('Answers already submitted: %s', params.accessToken);
error(400, 'Answers already submitted');
}
const survey = results[0].surveys_table;
const skills = await db.select().from(surveySkillsTable).where(eq(surveySkillsTable.surveyId, survey.id));
const formData = await request.formData();
// validate that the form doesn't contain invalid skill IDs
const skillEntries = [...formData.entries()].filter(([key, _]) => !key.startsWith('disable-'));
const allIdsValid = skillEntries.every(([key, _]) => skills.some(skill => skill.id.toString() === key));
const deselectedIds = [...formData.entries().filter(([key, _]) => key.startsWith('disable-')).map(([key, _]) => parseInt(key.replace('disable-', '')))];
if (!allIdsValid || skillEntries.length + deselectedIds.length !== skills.length) {
let missing_ids = skills.filter(skill => !skillEntries.some(([key, _]) => key === skill.id.toString()) && !deselectedIds.includes(skill.id));
let invalid_ids = skillEntries.filter(([key, _]) => !skills.some(skill => skill.id.toString() === key)).map(([key, _]) => key);
log_store.log("Invalid (%o) or missing (%o) skill IDs", invalid_ids, missing_ids);
error(400, 'Invalid skill ID');
}
const answers = skillEntries.map(([key, value]) => ({
participantId: results[0].survey_access_table.id,
skillId: parseInt(key),
rating: parseFloat(value.toString()) * 10 // convert to int to store in the database without rounding issues etc.
}));
if (answers.some(answer => isNaN(answer.rating) || answer.rating < 0 || answer.rating > 30)) {
log_store(
"Invalid answer: %o",
answers.filter(answer => isNaN(answer.rating) || answer.rating < 0 || answer.rating > 30)
);
error(400, 'Invalid answer');
}
await db.insert(surveyAnswersTable).values([...answers]);
redirect(303, `/${params.accessToken}/thanks`);
}
}