Нема описа
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
  2. import { FormsModule } from '@angular/forms';
  3. import { DankaService } from '../../services/dankaService';
  4. import { FamilyService } from '../../services/family-service';
  5. import { EventService } from '../../services/event-service';
  6. import { EventStatus, EventTarget, EventType } from '../../models/event';
  7. import { AppHeader } from '../../share/header/app-header';
  8. import { AppSideMenu } from '../../share/side-menu/app-side-menu';
  9. @Component({
  10. selector: 'app-event',
  11. imports: [AppHeader, AppSideMenu, FormsModule],
  12. templateUrl: './event.html',
  13. styleUrl: './event.scss',
  14. })
  15. export class EventPage implements OnInit{
  16. eventTargets: EventTarget[] = [];
  17. isLoading = true;
  18. targetYear: number = new Date().getFullYear();
  19. selectedEventType: EventType | 'all' = 'all';
  20. selectedStatus: EventStatus | 'all' = 'all';
  21. searchKeyword = '';
  22. eventStatuses: EventStatus[] = ['未案内', '案内済'];
  23. yearOptions: number[] = [
  24. this.targetYear - 1,
  25. this.targetYear,
  26. this.targetYear + 1,
  27. this.targetYear + 2,
  28. ];
  29. eventTypeFilters: { label: string; value: EventType | 'all' }[] = [
  30. { label: 'すべて', value: 'all' },
  31. { label: '稚児行列', value: '稚児行列' },
  32. { label: '七五三', value: '七五三' },
  33. { label: '成人式', value: '成人式' },
  34. { label: '米寿', value: '米寿' },
  35. ];
  36. statusFilters: { label: string; value: EventStatus | 'all' }[] = [
  37. { label: 'すべて', value: 'all' },
  38. { label: '未案内', value: '未案内' },
  39. { label: '案内済', value: '案内済' },
  40. ];
  41. private eventRequestId = 0;
  42. constructor(
  43. private dankaService: DankaService,
  44. private familyService: FamilyService,
  45. private eventService: EventService,
  46. private cdr: ChangeDetectorRef,
  47. ) { }
  48. ngOnInit(): void {
  49. this.init();
  50. }
  51. async init(): Promise<void> {
  52. await this.createEventTargetList();
  53. }
  54. async createEventTargetList(): Promise<void> {
  55. const requestId = ++this.eventRequestId;
  56. const targetYear = this.targetYear;
  57. const selectedEventType = this.selectedEventType;
  58. const eventTargets: EventTarget[] = [];
  59. if (this.eventTargets.length === 0) {
  60. this.isLoading = true;
  61. this.cdr.detectChanges();
  62. }
  63. const families = await this.familyService.getFamilyList();
  64. for (const family of families) {
  65. const birthDate = this.parseDate(family.birthDate);
  66. if (!birthDate) continue;
  67. const age = targetYear - birthDate.getFullYear();
  68. const eventTypes = this.getEventTypes(age);
  69. if (eventTypes.length === 0) continue;
  70. // ★ここが修正ポイント(await)
  71. const danka = await this.dankaService.getDankaById(family.dankaId);
  72. for (const eventType of eventTypes) {
  73. if (
  74. selectedEventType !== 'all' &&
  75. selectedEventType !== eventType
  76. ) {
  77. continue;
  78. }
  79. const id = `${family.id}-${eventType}`;
  80. const defaultStatus: EventStatus =
  81. Number(family.id) % 2 === 0 ? '案内済' : '未案内';
  82. eventTargets.push({
  83. id,
  84. dankaId: family.dankaId,
  85. name: family.name,
  86. furigana: family.furigana,
  87. householdName: danka?.householdName ?? '不明',
  88. relationship: family.relationship || '未登録',
  89. birthDate: this.formatDateForValue(birthDate),
  90. age,
  91. eventType,
  92. note: family.note,
  93. status: this.eventService.getEventStatus(id, defaultStatus),
  94. });
  95. }
  96. }
  97. if (requestId !== this.eventRequestId) {
  98. return;
  99. }
  100. this.eventTargets = eventTargets.sort(
  101. (a, b) =>
  102. this.getEventSortOrder(a.eventType) -
  103. this.getEventSortOrder(b.eventType) ||
  104. a.age - b.age ||
  105. a.name.localeCompare(b.name, 'ja'),
  106. );
  107. this.isLoading = false;
  108. this.cdr.detectChanges();
  109. }
  110. changeEventType(eventType: EventType | 'all'): void {
  111. this.selectedEventType = eventType;
  112. this.createEventTargetList();
  113. }
  114. get filteredEventTargets(): EventTarget[] {
  115. const keyword = this.searchKeyword.trim();
  116. return this.eventTargets.filter((target) => {
  117. const matchesStatus = this.selectedStatus === 'all' || target.status === this.selectedStatus;
  118. const matchesKeyword =
  119. !keyword ||
  120. [
  121. target.name,
  122. target.furigana,
  123. target.householdName,
  124. target.relationship,
  125. target.birthDate,
  126. target.eventType,
  127. target.note,
  128. target.status,
  129. ].some((value) => this.includesKeyword(value, keyword));
  130. return matchesStatus && matchesKeyword;
  131. });
  132. }
  133. changeStatus(target: EventTarget, status: EventStatus): void {
  134. target.status = status;
  135. this.eventService.saveEventStatus(target.id, status);
  136. }
  137. private getEventTypes(age: number): EventType[] {
  138. const eventTypes: EventType[] = [];
  139. if (age >= 3 && age <= 12) {
  140. eventTypes.push('稚児行列');
  141. }
  142. if ([3, 5, 7].includes(age)) {
  143. eventTypes.push('七五三');
  144. }
  145. if (age === 20) {
  146. eventTypes.push('成人式');
  147. }
  148. if (age === 88) {
  149. eventTypes.push('米寿');
  150. }
  151. return eventTypes;
  152. }
  153. private getEventSortOrder(eventType: EventType): number {
  154. return ['稚児行列', '七五三', '成人式', '米寿'].indexOf(eventType);
  155. }
  156. private parseDate(value: unknown): Date | null {
  157. if (!value) {
  158. return null;
  159. }
  160. if (value instanceof Date) {
  161. return value;
  162. }
  163. if (typeof value === 'object' && 'toDate' in value && typeof value.toDate === 'function') {
  164. return value.toDate();
  165. }
  166. if (typeof value !== 'string') {
  167. return null;
  168. }
  169. const [year, month, day] = value.split('-').map(Number);
  170. if (!year || !month || !day) {
  171. return null;
  172. }
  173. return new Date(year, month - 1, day);
  174. }
  175. private includesKeyword(value: unknown, keyword: string): boolean {
  176. return String(value ?? '').includes(keyword);
  177. }
  178. private formatDateForValue(date: Date): string {
  179. const year = date.getFullYear();
  180. const month = String(date.getMonth() + 1).padStart(2, '0');
  181. const day = String(date.getDate()).padStart(2, '0');
  182. return `${year}-${month}-${day}`;
  183. }
  184. }