diff --git a/__tests__/api/bikes.test.ts b/__tests__/api/bikes.test.ts index a74b59e..d9e5ef0 100644 --- a/__tests__/api/bikes.test.ts +++ b/__tests__/api/bikes.test.ts @@ -93,6 +93,50 @@ describe('Bikes API', () => { expect(bike.name).toBe('My Test Bike') }) + + it('should create bike with purchaseDate in YYYY-MM-DD format', async () => { + const bikeData = { + name: 'Bike with Date', + purchaseDate: new Date('2024-01-15T00:00:00.000Z'), + } + + const bike = await prisma.bike.create({ + data: bikeData, + }) + + expect(bike).toBeDefined() + expect(bike.purchaseDate).toBeInstanceOf(Date) + expect(bike.purchaseDate?.toISOString().split('T')[0]).toBe('2024-01-15') + }) + + it('should create bike with purchaseDate', async () => { + const purchaseDate = new Date('2024-03-20') + const bikeData = { + name: 'Bike with Purchase Date', + purchaseDate: purchaseDate, + } + + const bike = await prisma.bike.create({ + data: bikeData, + }) + + expect(bike).toBeDefined() + expect(bike.purchaseDate).toBeInstanceOf(Date) + expect(bike.purchaseDate?.getTime()).toBe(purchaseDate.getTime()) + }) + + it('should create bike without purchaseDate', async () => { + const bikeData = { + name: 'Bike without Date', + } + + const bike = await prisma.bike.create({ + data: bikeData, + }) + + expect(bike).toBeDefined() + expect(bike.purchaseDate).toBeNull() + }) }) describe('GET /api/bikes', () => { diff --git a/__tests__/lib/validations.test.ts b/__tests__/lib/validations.test.ts index 4e6fd7b..b712821 100644 --- a/__tests__/lib/validations.test.ts +++ b/__tests__/lib/validations.test.ts @@ -173,6 +173,85 @@ describe('Validation Schemas', () => { expect(result.data.notes).toBeUndefined() } }) + + it('should accept purchaseDate in YYYY-MM-DD format', () => { + // The schema accepts any string and transforms it to Date + const validData = { + name: 'Test Bike', + purchaseDate: '2024-01-15', + } + + const result = bikeSchema.safeParse(validData) + if (!result.success) { + // Debug: show what went wrong + console.log('Validation failed:', JSON.stringify(result.error.errors, null, 2)) + } + expect(result.success).toBe(true) + if (result.success) { + // Transform converts string to Date + expect(result.data.purchaseDate).toBeDefined() + if (result.data.purchaseDate) { + expect(result.data.purchaseDate).toBeInstanceOf(Date) + expect(result.data.purchaseDate.toISOString().split('T')[0]).toBe('2024-01-15') + } + } + }) + + it('should accept purchaseDate in ISO datetime format', () => { + const validData = { + name: 'Test Bike', + purchaseDate: '2024-01-15T10:30:00.000Z', + } + + const result = bikeSchema.safeParse(validData) + expect(result.success).toBe(true) + if (result.success) { + // The transform converts it to Date, but Zod might return the string + // Let's check if it's a Date or a valid date string + expect(result.data.purchaseDate).toBeDefined() + if (result.data.purchaseDate instanceof Date) { + expect(result.data.purchaseDate).toBeInstanceOf(Date) + } else { + // If it's still a string, it should be parseable + expect(new Date(result.data.purchaseDate as string)).toBeInstanceOf(Date) + } + } + }) + + it('should accept purchaseDate as Date object', () => { + const validData = { + name: 'Test Bike', + purchaseDate: new Date('2024-01-15'), + } + + const result = bikeSchema.safeParse(validData) + expect(result.success).toBe(true) + if (result.success) { + expect(result.data.purchaseDate).toBeInstanceOf(Date) + } + }) + + it('should accept bike without purchaseDate', () => { + const validData = { + name: 'Test Bike', + } + + const result = bikeSchema.safeParse(validData) + expect(result.success).toBe(true) + if (result.success) { + expect(result.data.purchaseDate).toBeUndefined() + } + }) + + it('should reject invalid purchaseDate format', () => { + const invalidData = { + name: 'Test Bike', + purchaseDate: 'invalid-date', + } + + const result = bikeSchema.safeParse(invalidData) + expect(result.success).toBe(false) + }) }) describe('wearPartSchema', () => { diff --git a/app/api/bikes/[id]/route.ts b/app/api/bikes/[id]/route.ts index f89cdcd..08a22ef 100644 --- a/app/api/bikes/[id]/route.ts +++ b/app/api/bikes/[id]/route.ts @@ -56,9 +56,7 @@ export async function PUT( name: validatedData.name, brand: validatedData.brand, model: validatedData.model, - purchaseDate: validatedData.purchaseDate - ? new Date(validatedData.purchaseDate) - : null, + purchaseDate: validatedData.purchaseDate || null, notes: validatedData.notes, }, }) diff --git a/app/api/bikes/route.ts b/app/api/bikes/route.ts index 110a120..5f259eb 100644 --- a/app/api/bikes/route.ts +++ b/app/api/bikes/route.ts @@ -41,9 +41,7 @@ export async function POST(request: NextRequest) { name: validatedData.name, brand: validatedData.brand, model: validatedData.model, - purchaseDate: validatedData.purchaseDate - ? new Date(validatedData.purchaseDate) - : null, + purchaseDate: validatedData.purchaseDate || null, notes: validatedData.notes, }, }) diff --git a/lib/validations.ts b/lib/validations.ts index 98d59a5..997d623 100644 --- a/lib/validations.ts +++ b/lib/validations.ts @@ -43,7 +43,29 @@ export const bikeSchema = z.object({ .min(1, 'Name darf nicht nur aus Leerzeichen bestehen'), brand: z.string().optional().transform((val) => val?.trim() || undefined), model: z.string().optional().transform((val) => val?.trim() || undefined), - purchaseDate: z.string().datetime().optional().or(z.date().optional()), + purchaseDate: z + .union([z.date(), z.string()]) + .optional() + .transform((val) => { + if (!val || val === undefined) return undefined + if (val instanceof Date) { + return isNaN(val.getTime()) ? undefined : val + } + if (typeof val === 'string') { + // Handle YYYY-MM-DD format (from HTML date input) + if (/^\d{4}-\d{2}-\d{2}$/.test(val)) { + const date = new Date(val + 'T00:00:00.000Z') + return isNaN(date.getTime()) ? undefined : date + } + // Handle ISO datetime format or any other parseable date string + const date = new Date(val) + return isNaN(date.getTime()) ? undefined : date + } + return undefined + }) + .refine((val) => val === undefined || val instanceof Date, { + message: 'Ungültiges Datumsformat', + }), notes: z.string().optional().transform((val) => val?.trim() || undefined), })