fix: resolve Pydantic date field shadowing causing 422 on event update

The field name 'date' in EventUpdate shadowed the 'date' type from
datetime, causing Pydantic v2 to interpret Optional[date] as
Optional[None] — rejecting all non-null values with a 422 error.

Fix: rename import to 'date as _date' and use '_date' in all
annotations. Also improve error handling in admin forms to properly
display 422 validation error messages.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-19 17:19:03 +00:00
co-authored by Claude
parent b0564ba727
commit bd65455945
4 changed files with 347 additions and 6 deletions
+105
View File
@@ -0,0 +1,105 @@
import { Component, EventEmitter, inject, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { firstValueFrom } from 'rxjs';
import { Event } from '../interfaces/event';
import { EventService } from '../services/event.service';
@Component({
selector: 'app-admin-event-form',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './admin-event-form.component.html',
styleUrl: './admin-event-form.component.scss',
})
export class AdminEventFormComponent implements OnChanges {
private eventService = inject(EventService);
@Input() editItem: Event | null = null;
@Output() saved = new EventEmitter<void>();
@Output() closed = new EventEmitter<void>();
id: number | null = null;
date = '';
title = '';
description = '';
location = '';
icon = '🎵';
rsvp_url = '';
active = true;
loading = false;
error = '';
submitted = false;
ngOnChanges(changes: SimpleChanges): void {
if (changes['editItem'] && this.editItem) {
this.edit(this.editItem);
}
}
edit(event: Event): void {
this.id = event.id;
this.date = event.date;
this.title = event.title;
this.description = event.description;
this.location = event.location;
this.icon = event.icon;
this.rsvp_url = event.rsvp_url ?? '';
this.active = event.active;
}
reset(): void {
this.id = null;
this.date = '';
this.title = '';
this.description = '';
this.location = '';
this.icon = '🎵';
this.rsvp_url = '';
this.active = true;
this.error = '';
this.submitted = false;
}
onClose(): void {
this.closed.emit();
this.reset();
}
async onSubmit(): Promise<void> {
this.submitted = true;
if (!this.title || !this.date || !this.description || !this.location) {
this.error = 'Please fill in all required fields.';
return;
}
this.loading = true;
this.error = '';
const payload = {
date: this.date,
title: this.title,
description: this.description,
location: this.location,
icon: this.icon,
rsvp_url: this.rsvp_url || null,
active: this.active,
};
try {
if (this.id) {
await firstValueFrom(this.eventService.updateEvent(this.id, payload));
} else {
await firstValueFrom(this.eventService.createEvent(payload));
}
this.saved.emit();
this.reset();
} catch (err: any) {
this.error = this.formatError(err);
} finally {
this.loading = false;
}
}
}