Revert education feature from main
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user