From 36852185b5f04496c5b4971c798063b946bfc1f3 Mon Sep 17 00:00:00 2001 From: salmanabdurrahman Date: Fri, 14 Nov 2025 21:18:21 +0700 Subject: [PATCH 1/2] feat(onboarding): simplify onboarding data and integrate ai summary - make dependencyLevel and checkinTime optional - add aiSummary to onboarding response - update prisma schema to allow null dependencyLevel - update onboarding validation schema to allow optional fields - reset aiChatHistory on seed --- .../migration.sql | 2 ++ prisma/schema.prisma | 2 +- src/api/auth/auth.controller.ts | 7 ++++++- src/api/auth/auth.service.ts | 8 ++++---- src/api/auth/auth.validation.ts | 13 +++++++++++-- src/database/seed/index.ts | 2 ++ 6 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 prisma/migrations/20251114134933_simplify_schema_for_mvp/migration.sql diff --git a/prisma/migrations/20251114134933_simplify_schema_for_mvp/migration.sql b/prisma/migrations/20251114134933_simplify_schema_for_mvp/migration.sql new file mode 100644 index 0000000..7df9007 --- /dev/null +++ b/prisma/migrations/20251114134933_simplify_schema_for_mvp/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "public"."UserProfile" ALTER COLUMN "dependencyLevel" DROP NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ce0dc0b..01fe291 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -122,7 +122,7 @@ model EducationContent { model UserProfile { id String @id @default(uuid()) answers Json - dependencyLevel String + dependencyLevel String? aiSummary String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/api/auth/auth.controller.ts b/src/api/auth/auth.controller.ts index efa622b..c2cb949 100644 --- a/src/api/auth/auth.controller.ts +++ b/src/api/auth/auth.controller.ts @@ -1,5 +1,6 @@ import type { Request, Response } from 'express'; import { saveOnboardingData, verifyGoogleTokenAndLogin } from './auth.service.js'; +import { analyzeOnboardingAnswers } from '../ai/ai.service.js'; import { asyncHandler } from '../../handler/async.handler.js'; import { errorResponse, successResponse } from '../../core/response.js'; @@ -24,6 +25,10 @@ export const onboardingHandler = asyncHandler(async (req: Request, res: Response } const profile = await saveOnboardingData(userId, onboardingData); + const aiSummary = await analyzeOnboardingAnswers(onboardingData); - return successResponse(res, 201, 'Data onboarding berhasil disimpan', profile); + return successResponse(res, 201, 'Data onboarding berhasil disimpan', { + profile, + aiSummary, + }); }); diff --git a/src/api/auth/auth.service.ts b/src/api/auth/auth.service.ts index 7627994..2d29851 100644 --- a/src/api/auth/auth.service.ts +++ b/src/api/auth/auth.service.ts @@ -50,9 +50,9 @@ export async function saveOnboardingData( userId: string, data: { answers: Record; - dependencyLevel: string; + dependencyLevel?: string; userWhy?: string; - checkinTime: string; + checkinTime?: string; } ) { const { answers, dependencyLevel, userWhy, checkinTime } = data; @@ -69,12 +69,12 @@ export async function saveOnboardingData( const userProfile = await prisma.userProfile.create({ data: { answers, - dependencyLevel, + dependencyLevel: dependencyLevel || null, userId, }, }); - const formattedCheckinTime = parseCheckinTime(checkinTime); + const formattedCheckinTime = parseCheckinTime(checkinTime || '09:00'); if (userWhy || formattedCheckinTime) { await prisma.user.update({ diff --git a/src/api/auth/auth.validation.ts b/src/api/auth/auth.validation.ts index 65c8bef..357b2e0 100644 --- a/src/api/auth/auth.validation.ts +++ b/src/api/auth/auth.validation.ts @@ -9,7 +9,12 @@ export const googleLoginSchema = z.object({ export const onboardingSchema = z.object({ body: z.object({ answers: z.record(z.string(), z.unknown()), - dependencyLevel: z.string().min(1, 'Tingkat ketergantungan harus diisi').trim(), + dependencyLevel: z + .string() + .min(1, 'Tingkat ketergantungan harus diisi') + .trim() + .optional() + .refine(val => !val || val.length > 0, { message: 'Tingkat ketergantungan harus diisi' }), userWhy: z .string() .optional() @@ -19,6 +24,10 @@ export const onboardingSchema = z.object({ .regex(/^([01]\d|2[0-3]):([0-5]\d)$/, { message: 'Format waktu check-in tidak valid, gunakan format HH:mm', }) - .trim(), + .trim() + .optional() + .refine(val => !val || /^([01]\d|2[0-3]):([0-5]\d)$/.test(val), { + message: 'Format waktu check-in tidak valid, gunakan format HH:mm', + }), }), }); diff --git a/src/database/seed/index.ts b/src/database/seed/index.ts index 5eab877..b215aa2 100644 --- a/src/database/seed/index.ts +++ b/src/database/seed/index.ts @@ -15,6 +15,7 @@ async function main() { console.log('[database]: Resetting existing data...'); await prisma.communityComment.deleteMany(); + await prisma.communityPostLike.deleteMany(); await prisma.communityPost.deleteMany(); await prisma.journal.deleteMany(); await prisma.checkin.deleteMany(); @@ -23,6 +24,7 @@ async function main() { await prisma.educationContent.deleteMany(); await prisma.dailyMotivation.deleteMany(); await prisma.dailyChallenge.deleteMany(); + await prisma.aiChatHistory.deleteMany(); await prisma.user.deleteMany(); console.log('[database]: Starting full seeding process...'); From 4754ca37c8d8786e0f4796e57b5e99479adfa20b Mon Sep 17 00:00:00 2001 From: salmanabdurrahman Date: Fri, 14 Nov 2025 21:38:37 +0700 Subject: [PATCH 2/2] fix(api): handle ai summary generation failure - add try-catch block for ai summary generation - return error message if ai summary fails - remove redundant refine validation in onboarding schema --- src/api/auth/auth.controller.ts | 11 +++++++++-- src/api/auth/auth.validation.ts | 12 ++---------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/api/auth/auth.controller.ts b/src/api/auth/auth.controller.ts index c2cb949..f088a0f 100644 --- a/src/api/auth/auth.controller.ts +++ b/src/api/auth/auth.controller.ts @@ -25,10 +25,17 @@ export const onboardingHandler = asyncHandler(async (req: Request, res: Response } const profile = await saveOnboardingData(userId, onboardingData); - const aiSummary = await analyzeOnboardingAnswers(onboardingData); + + let aiSummary; + try { + aiSummary = await analyzeOnboardingAnswers(onboardingData.answers); + } catch (error) { + console.error('AI summary generation failed:', error); + aiSummary = { error: 'AI summary tidak tersedia saat ini' }; + } return successResponse(res, 201, 'Data onboarding berhasil disimpan', { - profile, + ...profile, aiSummary, }); }); diff --git a/src/api/auth/auth.validation.ts b/src/api/auth/auth.validation.ts index 357b2e0..d81cc3c 100644 --- a/src/api/auth/auth.validation.ts +++ b/src/api/auth/auth.validation.ts @@ -9,12 +9,7 @@ export const googleLoginSchema = z.object({ export const onboardingSchema = z.object({ body: z.object({ answers: z.record(z.string(), z.unknown()), - dependencyLevel: z - .string() - .min(1, 'Tingkat ketergantungan harus diisi') - .trim() - .optional() - .refine(val => !val || val.length > 0, { message: 'Tingkat ketergantungan harus diisi' }), + dependencyLevel: z.string().min(1, 'Tingkat ketergantungan harus diisi').trim().optional(), userWhy: z .string() .optional() @@ -25,9 +20,6 @@ export const onboardingSchema = z.object({ message: 'Format waktu check-in tidak valid, gunakan format HH:mm', }) .trim() - .optional() - .refine(val => !val || /^([01]\d|2[0-3]):([0-5]\d)$/.test(val), { - message: 'Format waktu check-in tidak valid, gunakan format HH:mm', - }), + .optional(), }), });