feat: certificates, landing pages, ai tutor, website update, compliance

This commit is contained in:
Rotimi Best
2026-05-18 03:47:22 +01:00
parent c31ad63955
commit ac3a2c14a7
644 changed files with 56050 additions and 5439 deletions
@@ -102,6 +102,14 @@ export const ZAgentHistorySaveBody = z.object({
export type TAgentHistorySaveBody = z.infer<typeof ZAgentHistorySaveBody>;
// ─── PATCH /agent/history/:conversationId (rename only) ────────────────────
export const ZAgentHistoryRenameBody = z.object({
title: z.string().min(1).max(120)
});
export type TAgentHistoryRenameBody = z.infer<typeof ZAgentHistoryRenameBody>;
// ─── DELETE /agent/history/:conversationId ───────────────────────────────────
export const ZAgentHistoryDeleteParam = z.object({
@@ -130,3 +138,24 @@ export const ZAgentGenerateCourseTitleBody = z.object({
});
export type TAgentGenerateCourseTitleBody = z.infer<typeof ZAgentGenerateCourseTitleBody>;
// ─── POST /agent/summarize ────────────────────────────────────────────────────
export const ZAgentSummarizeBody = z.object({
messages: z.array(z.any()),
courseId: ZAgentCourseId
});
export type TAgentSummarizeBody = z.infer<typeof ZAgentSummarizeBody>;
// ─── POST /agent/generate-text ───────────────────────────────────────────────
export const ZAgentGenerateTextBody = z.object({
prompt: z.string().min(1).max(1000),
tone: z.enum(['professional', 'casual', 'expert', 'friendly']),
format: z.enum(['plain', 'html']).default('plain'),
context: z.string().max(500).optional(),
courseId: z.string().uuid().optional()
});
export type TAgentGenerateTextBody = z.infer<typeof ZAgentGenerateTextBody>;
@@ -1 +1,2 @@
export * from './agent';
export * from './tutor-config';
@@ -0,0 +1,73 @@
import { z } from 'zod';
/**
* Zod schema for the resolved AI tutor settings shape.
*
* Mirrors `AiTutorSettings` exported from @cio/ai-assistant. Used for org-level
* settings updates and as the source for `ZCourseAiTutorOverride` (which is the
* `.partial()` variant plus an `inheritFromOrg` flag).
*/
export const TutorPersonaIdSchema = z.enum(['friendly', 'formal', 'encouraging', 'socratic', 'custom']);
export const TutorResponseLengthSchema = z.enum(['short', 'medium', 'long']);
export const TutorAssessmentModeSchema = z.enum(['direct_answer', 'hint_only', 'block_during_exercise']);
export const TutorCodePolicySchema = z.enum(['allowed', 'hints_only', 'forbidden']);
export const TutorGroundingScopeSchema = z.enum(['lesson', 'course', 'workspace']);
const ZAiTutorEscalation = z.object({
enabled: z.boolean(),
email: z.union([z.literal(''), z.string().email()])
});
export const ZAiTutorSettings = z.object({
enabled: z.boolean(),
persona: TutorPersonaIdSchema,
customPersona: z.string().max(500).default(''),
responseLength: TutorResponseLengthSchema,
welcomeMessage: z.string().max(500).default(''),
disclaimerFooter: z.string().max(200).default(''),
thingsToSay: z.string().max(2000).default(''),
thingsNotToSay: z.string().max(2000).default(''),
forbiddenTopics: z.array(z.string().min(1).max(60)).max(20).default([]),
groundingScope: TutorGroundingScopeSchema,
requireCitations: z.boolean(),
assessmentMode: TutorAssessmentModeSchema,
revealSolutionsAfterAttempts: z.number().int().min(0).max(20),
codePolicy: TutorCodePolicySchema,
blockOffTopic: z.boolean(),
profanityFilter: z.boolean(),
escalation: ZAiTutorEscalation
});
export type TAiTutorSettings = z.infer<typeof ZAiTutorSettings>;
/** Partial — used for org-level PUT and as the base for course override. */
export const ZAiTutorSettingsUpdate = ZAiTutorSettings.partial();
export type TAiTutorSettingsUpdate = z.infer<typeof ZAiTutorSettingsUpdate>;
/** Per-course override: any subset of settings + an inheritance flag. */
export const ZCourseAiTutorOverride = ZAiTutorSettings.partial().extend({
inheritFromOrg: z.boolean().optional()
});
export type TCourseAiTutorOverride = z.infer<typeof ZCourseAiTutorOverride>;
// ─── Fair-use admin queries ──────────────────────────────────────────────────
export const ZTutorUsagePeriod = z.enum(['current', 'previous', 'last90']);
export type TTutorUsagePeriod = z.infer<typeof ZTutorUsagePeriod>;
export const ZTutorUsageQuery = z.object({
period: ZTutorUsagePeriod.default('current'),
search: z.string().max(120).optional(),
sort: z.enum(['messages', 'tokens', 'capPct']).default('messages'),
page: z.coerce.number().int().min(1).default(1),
limit: z.coerce.number().int().min(1).max(100).default(20)
});
export type TTutorUsageQuery = z.infer<typeof ZTutorUsageQuery>;
export const ZTutorUsageUserParam = z.object({
userId: z.string().uuid()
});
export type TTutorUsageUserParam = z.infer<typeof ZTutorUsageUserParam>;