|
|
@@ -1,11 +1,176 @@
|
|
1
|
1
|
import { Component } from '@angular/core';
|
|
|
2
|
+import { RouterLink } from '@angular/router';
|
|
|
3
|
+import { KakochoService } from '../../services/kakocho-service';
|
|
|
4
|
+import { DankaService } from '../../services/dankaService';
|
|
2
|
5
|
import { AppHeader } from '../../share/header/app-header';
|
|
3
|
6
|
import { AppSideMenu } from '../../share/side-menu/app-side-menu';
|
|
4
|
7
|
|
|
|
8
|
+interface UpcomingMemorial {
|
|
|
9
|
+ id: string;
|
|
|
10
|
+ dankaId: string;
|
|
|
11
|
+ date: Date;
|
|
|
12
|
+ dateLabel: string;
|
|
|
13
|
+ title: string;
|
|
|
14
|
+ subText: string;
|
|
|
15
|
+ type: '年忌法要' | '命日';
|
|
|
16
|
+ status: '準備確認' | '要確認';
|
|
|
17
|
+}
|
|
|
18
|
+
|
|
5
|
19
|
@Component({
|
|
6
|
20
|
selector: 'app-dashboard',
|
|
7
|
|
- imports: [AppHeader, AppSideMenu],
|
|
|
21
|
+ imports: [AppHeader, AppSideMenu, RouterLink],
|
|
8
|
22
|
templateUrl: './dashboard.html',
|
|
9
|
23
|
styleUrl: './dashboard.scss',
|
|
10
|
24
|
})
|
|
11
|
|
-export class Dashboard {}
|
|
|
25
|
+export class Dashboard {
|
|
|
26
|
+ weeklyMemorialCount = 0;
|
|
|
27
|
+ todayMemorialCount = 0;
|
|
|
28
|
+ upcomingWeeklyMemorialCount = 0;
|
|
|
29
|
+ monthlyMemorialCount = 0;
|
|
|
30
|
+ upcomingMemorials: UpcomingMemorial[] = [];
|
|
|
31
|
+
|
|
|
32
|
+ private readonly targetYear = new Date().getFullYear();
|
|
|
33
|
+
|
|
|
34
|
+ constructor(
|
|
|
35
|
+ private kakochoService: KakochoService,
|
|
|
36
|
+ private dankaService: DankaService,
|
|
|
37
|
+ ) {
|
|
|
38
|
+ this.setWeeklyMemorialSummary();
|
|
|
39
|
+ this.setMonthlyMemorialSummary();
|
|
|
40
|
+ this.setUpcomingMemorials();
|
|
|
41
|
+ }
|
|
|
42
|
+
|
|
|
43
|
+ private setWeeklyMemorialSummary(): void {
|
|
|
44
|
+ const today = this.toDateOnly(new Date());
|
|
|
45
|
+ const weekEnd = this.addDays(this.getWeekStart(today), 6);
|
|
|
46
|
+
|
|
|
47
|
+ const weeklyMemorials = this.kakochoService.getKakochoList().filter((kakocho) => {
|
|
|
48
|
+ const deathDate = this.parseDate(kakocho.deathDate);
|
|
|
49
|
+ if (!deathDate) {
|
|
|
50
|
+ return false;
|
|
|
51
|
+ }
|
|
|
52
|
+
|
|
|
53
|
+ if (!this.isMemorialTarget(deathDate)) {
|
|
|
54
|
+ return false;
|
|
|
55
|
+ }
|
|
|
56
|
+
|
|
|
57
|
+ const memorialDate = new Date(this.targetYear, deathDate.getMonth(), deathDate.getDate());
|
|
|
58
|
+ return memorialDate >= today && memorialDate <= weekEnd;
|
|
|
59
|
+ });
|
|
|
60
|
+
|
|
|
61
|
+ this.weeklyMemorialCount = weeklyMemorials.length;
|
|
|
62
|
+ this.todayMemorialCount = weeklyMemorials.filter((kakocho) => {
|
|
|
63
|
+ const deathDate = this.parseDate(kakocho.deathDate);
|
|
|
64
|
+ return deathDate?.getMonth() === today.getMonth() && deathDate.getDate() === today.getDate();
|
|
|
65
|
+ }).length;
|
|
|
66
|
+ this.upcomingWeeklyMemorialCount = this.weeklyMemorialCount - this.todayMemorialCount;
|
|
|
67
|
+ }
|
|
|
68
|
+
|
|
|
69
|
+ private setMonthlyMemorialSummary(): void {
|
|
|
70
|
+ const today = this.toDateOnly(new Date());
|
|
|
71
|
+
|
|
|
72
|
+ this.monthlyMemorialCount = this.kakochoService.getKakochoList().filter((kakocho) => {
|
|
|
73
|
+ const deathDate = this.parseDate(kakocho.deathDate);
|
|
|
74
|
+ if (!deathDate || !this.isMemorialTarget(deathDate)) {
|
|
|
75
|
+ return false;
|
|
|
76
|
+ }
|
|
|
77
|
+
|
|
|
78
|
+ return deathDate.getMonth() === today.getMonth();
|
|
|
79
|
+ }).length;
|
|
|
80
|
+ }
|
|
|
81
|
+
|
|
|
82
|
+ private setUpcomingMemorials(): void {
|
|
|
83
|
+ const today = this.toDateOnly(new Date());
|
|
|
84
|
+ const endDate = this.addDays(today, 30);
|
|
|
85
|
+
|
|
|
86
|
+ this.upcomingMemorials = this.kakochoService
|
|
|
87
|
+ .getKakochoList()
|
|
|
88
|
+ .map((kakocho): UpcomingMemorial | null => {
|
|
|
89
|
+ const deathDate = this.parseDate(kakocho.deathDate);
|
|
|
90
|
+ if (!deathDate) {
|
|
|
91
|
+ return null;
|
|
|
92
|
+ }
|
|
|
93
|
+
|
|
|
94
|
+ const eventDate = new Date(this.targetYear, deathDate.getMonth(), deathDate.getDate());
|
|
|
95
|
+ if (eventDate < today || eventDate > endDate) {
|
|
|
96
|
+ return null;
|
|
|
97
|
+ }
|
|
|
98
|
+
|
|
|
99
|
+ const memorialType = this.getMemorialType(deathDate);
|
|
|
100
|
+ const danka = this.dankaService.getDankaById(kakocho.dankaId);
|
|
|
101
|
+ const type = memorialType ? '年忌法要' : '命日';
|
|
|
102
|
+ const status = memorialType ? '準備確認' : '要確認';
|
|
|
103
|
+
|
|
|
104
|
+ return {
|
|
|
105
|
+ id: kakocho.id,
|
|
|
106
|
+ dankaId: kakocho.dankaId,
|
|
|
107
|
+ date: eventDate,
|
|
|
108
|
+ dateLabel: this.formatDateLabel(eventDate, today),
|
|
|
109
|
+ title: `${danka?.householdName ?? '不明'} ${kakocho.name}様 ${memorialType || '祥月命日'}`,
|
|
|
110
|
+ subText: `${danka?.address ?? '住所未登録'} / ${kakocho.kaimyo || '戒名未登録'}`,
|
|
|
111
|
+ type,
|
|
|
112
|
+ status,
|
|
|
113
|
+ };
|
|
|
114
|
+ })
|
|
|
115
|
+ .filter((memorial): memorial is UpcomingMemorial => memorial !== null)
|
|
|
116
|
+ .sort((a, b) => a.date.getTime() - b.date.getTime() || a.title.localeCompare(b.title, 'ja'))
|
|
|
117
|
+ .slice(0, 3);
|
|
|
118
|
+ }
|
|
|
119
|
+
|
|
|
120
|
+ private isMemorialTarget(deathDate: Date): boolean {
|
|
|
121
|
+ return this.getMemorialType(deathDate) !== '';
|
|
|
122
|
+ }
|
|
|
123
|
+
|
|
|
124
|
+ private getMemorialType(deathDate: Date): string {
|
|
|
125
|
+ const yearDiff = this.targetYear - deathDate.getFullYear();
|
|
|
126
|
+
|
|
|
127
|
+ switch (yearDiff) {
|
|
|
128
|
+ case 1:
|
|
|
129
|
+ return '一周忌';
|
|
|
130
|
+ case 2:
|
|
|
131
|
+ return '三回忌';
|
|
|
132
|
+ case 6:
|
|
|
133
|
+ return '七回忌';
|
|
|
134
|
+ case 12:
|
|
|
135
|
+ return '十三回忌';
|
|
|
136
|
+ case 16:
|
|
|
137
|
+ return '十七回忌';
|
|
|
138
|
+ case 22:
|
|
|
139
|
+ return '二十三回忌';
|
|
|
140
|
+ default:
|
|
|
141
|
+ return '';
|
|
|
142
|
+ }
|
|
|
143
|
+ }
|
|
|
144
|
+
|
|
|
145
|
+ private formatDateLabel(date: Date, today: Date): string {
|
|
|
146
|
+ if (date.getTime() === today.getTime()) {
|
|
|
147
|
+ return '今日';
|
|
|
148
|
+ }
|
|
|
149
|
+
|
|
|
150
|
+ return `${date.getMonth() + 1}月${date.getDate()}日`;
|
|
|
151
|
+ }
|
|
|
152
|
+
|
|
|
153
|
+ private getWeekStart(date: Date): Date {
|
|
|
154
|
+ const day = date.getDay();
|
|
|
155
|
+ const diff = day === 0 ? -6 : 1 - day;
|
|
|
156
|
+ return this.addDays(date, diff);
|
|
|
157
|
+ }
|
|
|
158
|
+
|
|
|
159
|
+ private addDays(date: Date, days: number): Date {
|
|
|
160
|
+ const result = new Date(date);
|
|
|
161
|
+ result.setDate(result.getDate() + days);
|
|
|
162
|
+ return result;
|
|
|
163
|
+ }
|
|
|
164
|
+
|
|
|
165
|
+ private toDateOnly(date: Date): Date {
|
|
|
166
|
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
|
167
|
+ }
|
|
|
168
|
+
|
|
|
169
|
+ private parseDate(value: string): Date | null {
|
|
|
170
|
+ const [year, month, day] = value.split('-').map(Number);
|
|
|
171
|
+ if (!year || !month || !day) {
|
|
|
172
|
+ return null;
|
|
|
173
|
+ }
|
|
|
174
|
+ return new Date(year, month - 1, day);
|
|
|
175
|
+ }
|
|
|
176
|
+}
|