import { ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { DankaService } from '../../services/dankaService'; import { FamilyService } from '../../services/family-service'; import { EventService } from '../../services/event-service'; import { EventStatus, EventTarget, EventType } from '../../models/event'; import { AppHeader } from '../../share/header/app-header'; import { AppSideMenu } from '../../share/side-menu/app-side-menu'; @Component({ selector: 'app-event', imports: [AppHeader, AppSideMenu, FormsModule], templateUrl: './event.html', styleUrl: './event.scss', }) export class EventPage implements OnInit{ eventTargets: EventTarget[] = []; isLoading = true; targetYear: number = new Date().getFullYear(); selectedEventType: EventType | 'all' = 'all'; selectedStatus: EventStatus | 'all' = 'all'; searchKeyword = ''; eventStatuses: EventStatus[] = ['未案内', '案内済']; yearOptions: number[] = [ this.targetYear - 1, this.targetYear, this.targetYear + 1, this.targetYear + 2, ]; eventTypeFilters: { label: string; value: EventType | 'all' }[] = [ { label: 'すべて', value: 'all' }, { label: '稚児行列', value: '稚児行列' }, { label: '七五三', value: '七五三' }, { label: '成人式', value: '成人式' }, { label: '米寿', value: '米寿' }, ]; statusFilters: { label: string; value: EventStatus | 'all' }[] = [ { label: 'すべて', value: 'all' }, { label: '未案内', value: '未案内' }, { label: '案内済', value: '案内済' }, ]; private eventRequestId = 0; constructor( private dankaService: DankaService, private familyService: FamilyService, private eventService: EventService, private cdr: ChangeDetectorRef, ) { } ngOnInit(): void { this.init(); } async init(): Promise { await this.createEventTargetList(); } async createEventTargetList(): Promise { const requestId = ++this.eventRequestId; const targetYear = this.targetYear; const selectedEventType = this.selectedEventType; const eventTargets: EventTarget[] = []; if (this.eventTargets.length === 0) { this.isLoading = true; this.cdr.detectChanges(); } const families = await this.familyService.getFamilyList(); for (const family of families) { const birthDate = this.parseDate(family.birthDate); if (!birthDate) continue; const age = targetYear - birthDate.getFullYear(); const eventTypes = this.getEventTypes(age); if (eventTypes.length === 0) continue; // ★ここが修正ポイント(await) const danka = await this.dankaService.getDankaById(family.dankaId); for (const eventType of eventTypes) { if ( selectedEventType !== 'all' && selectedEventType !== eventType ) { continue; } const id = `${family.id}-${eventType}`; const defaultStatus: EventStatus = Number(family.id) % 2 === 0 ? '案内済' : '未案内'; eventTargets.push({ id, dankaId: family.dankaId, name: family.name, furigana: family.furigana, householdName: danka?.householdName ?? '不明', relationship: family.relationship || '未登録', birthDate: this.formatDateForValue(birthDate), age, eventType, note: family.note, status: this.eventService.getEventStatus(id, defaultStatus), }); } } if (requestId !== this.eventRequestId) { return; } this.eventTargets = eventTargets.sort( (a, b) => this.getEventSortOrder(a.eventType) - this.getEventSortOrder(b.eventType) || a.age - b.age || a.name.localeCompare(b.name, 'ja'), ); this.isLoading = false; this.cdr.detectChanges(); } changeEventType(eventType: EventType | 'all'): void { this.selectedEventType = eventType; this.createEventTargetList(); } get filteredEventTargets(): EventTarget[] { const keyword = this.searchKeyword.trim(); return this.eventTargets.filter((target) => { const matchesStatus = this.selectedStatus === 'all' || target.status === this.selectedStatus; const matchesKeyword = !keyword || [ target.name, target.furigana, target.householdName, target.relationship, target.birthDate, target.eventType, target.note, target.status, ].some((value) => this.includesKeyword(value, keyword)); return matchesStatus && matchesKeyword; }); } changeStatus(target: EventTarget, status: EventStatus): void { target.status = status; this.eventService.saveEventStatus(target.id, status); } private getEventTypes(age: number): EventType[] { const eventTypes: EventType[] = []; if (age >= 3 && age <= 12) { eventTypes.push('稚児行列'); } if ([3, 5, 7].includes(age)) { eventTypes.push('七五三'); } if (age === 20) { eventTypes.push('成人式'); } if (age === 88) { eventTypes.push('米寿'); } return eventTypes; } private getEventSortOrder(eventType: EventType): number { return ['稚児行列', '七五三', '成人式', '米寿'].indexOf(eventType); } private parseDate(value: unknown): Date | null { if (!value) { return null; } if (value instanceof Date) { return value; } if (typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') { return value.toDate(); } if (typeof value !== 'string') { return null; } const [year, month, day] = value.split('-').map(Number); if (!year || !month || !day) { return null; } return new Date(year, month - 1, day); } private includesKeyword(value: unknown, keyword: string): boolean { return String(value ?? '').includes(keyword); } private formatDateForValue(date: Date): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; } }