Add freemium variant C: archetype persistence, dashboard banner, lesson gate

- User.archetype/utmSource/utmMedium/utmCampaign fields + migration
- register flow reads ?archetype/?utm_* and saves them on the User row
- dashboard ArchetypeBanner: per-archetype 'твой путь' block
- lesson gate after wow-moment lesson (WOW_MOMENT_LESSON_ID=obs-start-l2-006) → tripwire + full-course offer

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-23 11:26:34 +05:00
parent bb6c806cdd
commit 7bf3935c81
12 changed files with 241 additions and 3 deletions
+14 -1
View File
@@ -37,7 +37,7 @@ export async function POST(request: NextRequest) {
return jsonError("Неверный формат запроса", 400);
}
const { name, email, password, website, cfTurnstileResponse } = body;
const { name, email, password, website, cfTurnstileResponse, archetype, utmSource, utmMedium, utmCampaign } = body;
// Honeypot — bots fill this field, humans don't see it
if (website) {
@@ -102,6 +102,19 @@ export async function POST(request: NextRequest) {
select: { id: true },
}),
]);
// Тип второго мозга из квиза + UTM — сохраняем на юзере для персонализации
const VALID_ARCHETYPES = ["architect", "gardener", "librarian", "pragmatist"];
if (user && (archetype || utmSource || utmMedium || utmCampaign)) {
await prisma.user.update({
where: { id: user.id },
data: {
archetype: VALID_ARCHETYPES.includes(String(archetype)) ? String(archetype) : null,
utmSource: utmSource ? String(utmSource) : null,
utmMedium: utmMedium ? String(utmMedium) : null,
utmCampaign: utmCampaign ? String(utmCampaign) : null,
},
});
}
if (user && course) {
await prisma.courseEnrollment.upsert({
where: { userId_courseId: { userId: user.id, courseId: course.id } },