Revert education feature from main

This commit is contained in:
blaisadmin
2026-05-30 15:50:23 -04:00
parent 505a9b8496
commit e965cb55ef
6 changed files with 2 additions and 1103 deletions
-198
View File
@@ -61,19 +61,6 @@ import {
updateVetVisitForBird,
} from './repositories/birdRepository.js';
import { createIntegrationTokenRecord, listIntegrationTokens, revokeIntegrationToken } from './repositories/integrationTokenRepository.js';
import {
deleteDailyEducation,
deleteEducationQuestion,
createEducationQuestion,
getDailyEducationForDate,
getEducationOptOut,
listDailyEducationForAdmin,
listDailyEducationQuestions,
listEducationQuestionsForAdmin,
updateEducationOptOut,
updateEducationQuestion,
upsertDailyEducation,
} from './repositories/educationRepository.js';
import {
buildBirdPhotoObjectKey,
getImageExtensionFromContentType,
@@ -112,8 +99,6 @@ import type {
AuthContext,
BillingInterval,
BillingPlan,
DailyEducationRow,
EducationQuestionRow,
BirdGender,
BirdMilestoneReminderCandidateRow,
BirdRow,
@@ -339,27 +324,6 @@ const integrationTokenCreateSchema = z.object({
expiresInDays: z.coerce.number().int().min(1).max(3650).optional(),
});
const educationQuestionSchema = z
.object({
prompt: z.string().trim().min(1).max(500),
options: z.array(z.string().trim().min(1).max(240)).min(2).max(4),
correctAnswerIndex: z.coerce.number().int().min(0).max(3),
explanation: z.string().trim().max(800).optional().or(z.literal('')),
})
.refine((value) => value.correctAnswerIndex < value.options.length, {
message: 'Correct answer must match one of the quiz options.',
path: ['correctAnswerIndex'],
});
const dailyEducationSchema = z.object({
publishDate: dateStringSchema,
fact: z.string().trim().min(1).max(2000),
});
const educationPreferenceSchema = z.object({
educationOptOut: z.boolean(),
});
const emptyToNull = (value?: string) => {
const trimmed = value?.trim() ?? '';
return trimmed ? trimmed : null;
@@ -554,25 +518,6 @@ const normalizeWorkspaceMember = (row: WorkspaceMemberRow) => ({
createdAt: row.created_at,
});
const normalizeEducationQuestion = (row: EducationQuestionRow) => ({
id: row.id,
prompt: row.prompt,
options: row.options,
correctAnswerIndex: Number(row.correct_answer_index),
explanation: row.explanation ?? null,
createdAt: row.created_at,
updatedAt: row.updated_at,
});
const normalizeDailyEducation = (row: DailyEducationRow, questions: EducationQuestionRow[] = []) => ({
id: row.id,
publishDate: row.publish_date,
fact: row.fact,
quizQuestions: questions.map(normalizeEducationQuestion),
createdAt: row.created_at,
updatedAt: row.updated_at,
});
const signBirdPhotoAccessToken = (row: BirdRow) => {
if (!row.photo_object_key) {
return '';
@@ -2437,149 +2382,6 @@ app.get('/api/admin/rescue-workspaces', requireAuth, requireSessionAuth, require
}
});
app.get('/api/admin/daily-education', requireAuth, requireSessionAuth, requireAdmin, async (_req: Request, res: Response, next: NextFunction) => {
try {
const education = await listDailyEducationForAdmin();
res.json({ education: education.map((entry) => normalizeDailyEducation(entry)) });
} catch (error) {
next(error);
}
});
app.get('/api/admin/education-questions', requireAuth, requireSessionAuth, requireAdmin, async (_req: Request, res: Response, next: NextFunction) => {
try {
const questions = await listEducationQuestionsForAdmin();
res.json({ questions: questions.map(normalizeEducationQuestion) });
} catch (error) {
next(error);
}
});
app.put('/api/admin/daily-education', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
const parsed = dailyEducationSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: 'Invalid daily education payload', details: parsed.error.flatten() });
return;
}
try {
const education = await upsertDailyEducation({
publishDate: parsed.data.publishDate,
fact: parsed.data.fact,
createdByUserId: req.auth!.user.id,
});
res.json({ education: normalizeDailyEducation(education) });
} catch (error) {
next(error);
}
});
app.post('/api/admin/education-questions', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
const parsed = educationQuestionSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: 'Invalid education question payload', details: parsed.error.flatten() });
return;
}
try {
const question = await createEducationQuestion({
question: { ...parsed.data, explanation: emptyToNull(parsed.data.explanation) },
createdByUserId: req.auth!.user.id,
});
res.status(201).json({ question: normalizeEducationQuestion(question) });
} catch (error) {
next(error);
}
});
app.put('/api/admin/education-questions/:questionId', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
const parsed = educationQuestionSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: 'Invalid education question payload', details: parsed.error.flatten() });
return;
}
try {
const question = await updateEducationQuestion(req.params.questionId, {
...parsed.data,
explanation: emptyToNull(parsed.data.explanation),
});
if (!question) {
res.status(404).json({ error: 'Education question not found.' });
return;
}
res.json({ question: normalizeEducationQuestion(question) });
} catch (error) {
next(error);
}
});
app.delete('/api/admin/education-questions/:questionId', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
try {
const deleted = await deleteEducationQuestion(req.params.questionId);
if (!deleted) {
res.status(404).json({ error: 'Education question not found.' });
return;
}
res.status(204).send();
} catch (error) {
next(error);
}
});
app.delete('/api/admin/daily-education/:educationId', requireAuth, requireSessionAuth, requireAdmin, async (req: Request, res: Response, next: NextFunction) => {
try {
const deleted = await deleteDailyEducation(req.params.educationId);
if (!deleted) {
res.status(404).json({ error: 'Daily education item not found.' });
return;
}
res.status(204).send();
} catch (error) {
next(error);
}
});
app.get('/api/education/today', requireAuth, requireSessionAuth, async (req: Request, res: Response, next: NextFunction) => {
try {
const educationOptOut = await getEducationOptOut(req.auth!.user.id);
const education = educationOptOut ? null : await getDailyEducationForDate();
const questions = education ? await listDailyEducationQuestions(education.publish_date) : [];
res.json({
educationOptOut,
education: education ? normalizeDailyEducation(education, questions) : null,
});
} catch (error) {
next(error);
}
});
app.patch('/api/education/preferences', requireAuth, requireSessionAuth, async (req: Request, res: Response, next: NextFunction) => {
const parsed = educationPreferenceSchema.safeParse(req.body);
if (!parsed.success) {
res.status(400).json({ error: 'Invalid education preference payload', details: parsed.error.flatten() });
return;
}
try {
const educationOptOut = await updateEducationOptOut(req.auth!.user.id, parsed.data.educationOptOut);
res.json({ educationOptOut });
} catch (error) {
next(error);
}
});
app.patch('/api/admin/rescue-workspaces/:workspaceId', requireAuth, requireAdmin, requireWriteAccess, async (req: Request, res: Response, next: NextFunction) => {
const parsed = z.object({ rescueVerificationStatus: rescueVerificationStatusSchema }).safeParse(req.body);