7 Revize

Autor SHA1 Zpráva Datum
  poohr ea7a64d6d3 [add] před 3 týdny
  poohr be903b11b0 [update] před 3 týdny
  poohr 73f60f54d3 [add] před 3 týdny
  poohr b8acdf78e2 [add] před 3 týdny
  poohr 0cbe21ca8f [add] před 3 týdny
  poohr ff76813ab7 [fix] před 3 týdny
  poohr 320d6e65a0 [add] před 3 týdny

+ 7
- 2
src/app/app.routes.ts Zobrazit soubor

@@ -5,7 +5,8 @@ import { DankaDetail } from './pages/danka-detail/danka-detail';
5 5
 import { DankaEdit } from './pages/danka-edit/danka-edit';
6 6
 import { FamilyEdit } from './pages/family-edit/family-edit';
7 7
 import { KakochoEdit } from './pages/kakocho-edit/kakocho-edit';
8
-import  {MemorialList} from './pages/memorial-list/memorial-list';
8
+import { MemorialList } from './pages/memorial-list/memorial-list';
9
+import { Search } from './pages/search/search';
9 10
 
10 11
 export const routes: Routes = [
11 12
   {
@@ -46,6 +47,10 @@ export const routes: Routes = [
46 47
   },
47 48
   {
48 49
     path: 'memorial-list',
49
-    component:MemorialList,
50
+    component: MemorialList,
51
+  },
52
+  {
53
+    path: 'search',
54
+    component: Search,
50 55
   },
51 56
 ];

+ 140
- 0
src/app/pages/search/search.html Zobrazit soubor

@@ -0,0 +1,140 @@
1
+<app-header></app-header>
2
+
3
+<div class="breadcrumb">
4
+  ホーム &gt; まとめて検索
5
+</div>
6
+
7
+<div class="search-page">
8
+  <app-side-menu></app-side-menu>
9
+
10
+  <main class="search-main">
11
+    <section class="search-panel">
12
+      <div class="page-title-row">
13
+        <h1>まとめて検索</h1>
14
+        <div class="top-action-list">
15
+          <button type="button" class="top-button" onclick="history.back()">
16
+            戻る
17
+          </button>
18
+          <a routerLink="/" class="top-button">
19
+            ホーム
20
+          </a>
21
+        </div>
22
+      </div>
23
+
24
+      <section class="search-condition-area">
25
+        <div class="search-input-row">
26
+          <div class="search-input-box">
27
+            <span class="search-icon">⌕</span>
28
+            <input type="text" [(ngModel)]="searchKeyword"
29
+                   placeholder="さとう / 佐藤 / 釈 / 2024 / 母 / 三回忌"
30
+                   (keydown.enter)="searchAll()"/>
31
+          </div>
32
+          <button type="button" class="search-button" (click)="searchAll()">
33
+            検索
34
+          </button>
35
+          <button type="button" class="filter-button-main" (click)="clearSearch()">
36
+            クリア
37
+          </button>
38
+          <a routerLink="/danka-list" class="back-list-button">
39
+            一覧へ戻る
40
+          </a>
41
+        </div>
42
+
43
+        <div class="search-filter-list">
44
+          @for (filter of searchTypeFilters; track filter.value) {
45
+            <button
46
+              type="button"
47
+              class="search-filter-button"
48
+              [class.active]="selectedSearchType === filter.value"
49
+              (click)="changeSearchType(filter.value)">
50
+              {{ filter.label }}
51
+            </button>
52
+          }
53
+        </div>
54
+      </section>
55
+
56
+      <section class="search-result-panel">
57
+        <h2>検索結果 {{ totalResultCount }} 件</h2>
58
+
59
+        @if (totalResultCount > 0) {
60
+          @if (dankaResults.length > 0) {
61
+            <section class="result-group">
62
+              <h3>檀家(世帯)</h3>
63
+
64
+              @for (danka of dankaResults; track danka.id) {
65
+                <a class="result-row danka-result-row"
66
+                   [routerLink]="['/danka-detail', danka.id]">
67
+                  <div class="result-main-text">
68
+                    {{ danka.householdName }}
69
+                  </div>
70
+                  <div>
71
+                    世帯主: {{ danka.householder }}
72
+                  </div>
73
+                  <div>
74
+                    住所: {{ danka.address }}
75
+                  </div>
76
+                  <div class="result-link-text">
77
+                    開く
78
+                  </div>
79
+                </a>
80
+              }
81
+            </section>
82
+          }
83
+
84
+          @if (familyResults.length > 0) {
85
+            <section class="result-group">
86
+              <h3>家族</h3>
87
+              @for (family of familyResults; track family.id) {
88
+                <a class="result-row family-result-row"
89
+                   [routerLink]="['/danka', family.dankaId, 'family', family.id, 'edit']">
90
+                  <div class="result-main-text">
91
+                    {{ family.name }}
92
+                  </div>
93
+                  <div>
94
+                    ふりがな: {{ family.furigana }}
95
+                  </div>
96
+                  <div>
97
+                    関係: {{ family.relationship }}
98
+                  </div>
99
+                  <div>
100
+                    生年月日: {{ family.birthDate || '未登録' }}
101
+                  </div>
102
+                </a>
103
+              }
104
+            </section>
105
+          }
106
+
107
+          @if (kakochoResults.length > 0) {
108
+            <section class="result-group">
109
+              <h3>過去帳</h3>
110
+
111
+              @for (kakocho of kakochoResults; track kakocho.id) {
112
+                <a class="result-row kakocho-result-row"
113
+                   [routerLink]="['/danka-detail', kakocho.dankaId]"
114
+                   [queryParams]="{ tab: 'kakocho' }">
115
+                  <div class="result-main-text">
116
+                    {{ kakocho.kaimyo }}
117
+                  </div>
118
+                  <div>
119
+                    俗名: {{ kakocho.name }}
120
+                  </div>
121
+                  <div>
122
+                    没年月日: {{ kakocho.deathDate }}
123
+                  </div>
124
+                  <div>
125
+                    {{ kakocho.note || '備考なし' }}
126
+                  </div>
127
+                </a>
128
+              }
129
+            </section>
130
+          }
131
+        } @else {
132
+          <div class="empty-result">
133
+            検索結果はありません。
134
+          </div>
135
+        }
136
+      </section>
137
+    </section>
138
+  </main>
139
+</div>
140
+

+ 359
- 0
src/app/pages/search/search.scss Zobrazit soubor

@@ -0,0 +1,359 @@
1
+:host {
2
+  position: relative;
3
+  display: block;
4
+  min-height: 100vh;
5
+  background: #f4eee4;
6
+  color: #2f2720;
7
+}
8
+
9
+.breadcrumb {
10
+  position: absolute;
11
+  top: 28px;
12
+  left: 50%;
13
+  transform: translateX(-50%);
14
+  color: #7b6b5c;
15
+  font-size: 14px;
16
+  z-index: 2;
17
+}
18
+
19
+.search-page {
20
+  display: flex;
21
+  align-items: flex-start;
22
+  gap: 8px;
23
+  background: #f4eee4;
24
+}
25
+
26
+.search-main {
27
+  flex: 1;
28
+  padding-right: 34px;
29
+  box-sizing: border-box;
30
+}
31
+
32
+.search-panel {
33
+  min-height: 650px;
34
+  padding: 26px 34px 36px;
35
+  background: #ffffff;
36
+  border: 2px solid #d8caba;
37
+  border-radius: 76px;
38
+  box-sizing: border-box;
39
+}
40
+
41
+.page-title-row {
42
+  display: flex;
43
+  justify-content: space-between;
44
+  align-items: flex-start;
45
+  gap: 24px;
46
+}
47
+
48
+.page-title-row h1 {
49
+  margin: 0;
50
+  color: #2f2720;
51
+  font-size: 32px;
52
+  line-height: 1.2;
53
+  font-weight: 800;
54
+  letter-spacing: 0.02em;
55
+}
56
+
57
+.top-action-list {
58
+  display: flex;
59
+  gap: 8px;
60
+}
61
+
62
+.top-button {
63
+  width: 92px;
64
+  height: 36px;
65
+  border: 2px solid #d8caba;
66
+  border-radius: 6px;
67
+  background: #ffffff;
68
+  color: #2f2720;
69
+  font-size: 15px;
70
+  font-weight: 800;
71
+  text-decoration: none;
72
+  display: flex;
73
+  align-items: center;
74
+  justify-content: center;
75
+  box-sizing: border-box;
76
+  cursor: pointer;
77
+}
78
+
79
+.top-button:hover {
80
+  background: #f6efe6;
81
+}
82
+
83
+.search-condition-area {
84
+  margin-top: 8px;
85
+}
86
+
87
+.search-input-row {
88
+  display: grid;
89
+  grid-template-columns: minmax(0, 1fr) 120px 140px 150px;
90
+  gap: 18px;
91
+  align-items: center;
92
+}
93
+
94
+.search-input-box {
95
+  height: 56px;
96
+  border: 2px solid #d8caba;
97
+  border-radius: 8px;
98
+  background: #fffdf9;
99
+  display: flex;
100
+  align-items: center;
101
+  padding: 0 16px;
102
+  box-sizing: border-box;
103
+}
104
+
105
+.search-icon {
106
+  width: 30px;
107
+  color: #7b6b5c;
108
+  font-size: 24px;
109
+  font-weight: 800;
110
+}
111
+
112
+.search-input-box input {
113
+  width: 100%;
114
+  border: none;
115
+  outline: none;
116
+  background: transparent;
117
+  color: #2f2720;
118
+  font-size: 18px;
119
+  font-family: inherit;
120
+}
121
+
122
+.search-input-box input::placeholder {
123
+  color: #7b6b5c;
124
+}
125
+
126
+.search-button,
127
+.filter-button-main,
128
+.back-list-button {
129
+  height: 56px;
130
+  border-radius: 8px;
131
+  font-size: 18px;
132
+  font-weight: 800;
133
+  cursor: pointer;
134
+  box-sizing: border-box;
135
+}
136
+
137
+.search-button {
138
+  border: 2px solid #8a6543;
139
+  background: #8a6543;
140
+  color: #ffffff;
141
+}
142
+
143
+.search-button:hover {
144
+  background: #765639;
145
+}
146
+
147
+.filter-button-main,
148
+.back-list-button {
149
+  border: 2px solid #d8caba;
150
+  background: #ffffff;
151
+  color: #2f2720;
152
+  text-decoration: none;
153
+  display: flex;
154
+  align-items: center;
155
+  justify-content: center;
156
+}
157
+
158
+.filter-button-main:hover,
159
+.back-list-button:hover {
160
+  background: #f6efe6;
161
+}
162
+
163
+.search-filter-list {
164
+  display: flex;
165
+  flex-wrap: wrap;
166
+  gap: 8px;
167
+  margin-top: 16px;
168
+}
169
+
170
+.search-filter-button {
171
+  min-width: 120px;
172
+  height: 38px;
173
+  padding: 0 18px;
174
+  border: 2px solid #d8caba;
175
+  border-radius: 6px;
176
+  background: #f1e7d8;
177
+  color: #2f2720;
178
+  font-size: 15px;
179
+  font-weight: 700;
180
+  cursor: pointer;
181
+  box-sizing: border-box;
182
+}
183
+
184
+.search-filter-button:hover {
185
+  background: #eadfce;
186
+}
187
+
188
+.search-filter-button.active {
189
+  background: #8a6543;
190
+  border-color: #8a6543;
191
+  color: #ffffff;
192
+}
193
+
194
+.search-result-panel {
195
+  margin-top: 16px;
196
+  padding: 28px 28px 34px;
197
+  border: 2px solid #d8caba;
198
+  border-radius: 62px;
199
+  background: #fffdf9;
200
+  box-sizing: border-box;
201
+}
202
+
203
+.search-result-panel h2 {
204
+  margin: 0 0 12px;
205
+  color: #2f2720;
206
+  font-size: 24px;
207
+  font-weight: 800;
208
+}
209
+
210
+.result-group {
211
+  margin-top: 16px;
212
+}
213
+
214
+.result-group h3 {
215
+  margin: 0 0 6px;
216
+  color: #6f6257;
217
+  font-size: 17px;
218
+  font-weight: 800;
219
+}
220
+
221
+.result-row {
222
+  min-height: 56px;
223
+  margin-top: 6px;
224
+  padding: 0 12px;
225
+  border: 2px solid #d8caba;
226
+  border-radius: 8px;
227
+  background: #ffffff;
228
+  color: #2f2720;
229
+  text-decoration: none;
230
+  display: grid;
231
+  align-items: center;
232
+  column-gap: 18px;
233
+  box-sizing: border-box;
234
+}
235
+
236
+.result-row:hover {
237
+  background: #f6efe6;
238
+}
239
+
240
+.danka-result-row {
241
+  grid-template-columns: 1.1fr 1.4fr 1.8fr 80px;
242
+}
243
+
244
+.family-result-row {
245
+  grid-template-columns: 1.1fr 1.6fr 1.2fr 1.6fr;
246
+}
247
+
248
+.kakocho-result-row {
249
+  grid-template-columns: 1.2fr 1.4fr 1.4fr 1.2fr;
250
+}
251
+
252
+.result-main-text {
253
+  font-weight: 800;
254
+}
255
+
256
+.result-link-text {
257
+  color: #3f6f45;
258
+  font-weight: 800;
259
+}
260
+
261
+.empty-result {
262
+  min-height: 72px;
263
+  padding: 22px;
264
+  border: 2px solid #d8caba;
265
+  border-radius: 10px;
266
+  background: #ffffff;
267
+  color: #7b6b5c;
268
+  font-size: 16px;
269
+  font-weight: 700;
270
+  box-sizing: border-box;
271
+}
272
+
273
+.inside-note {
274
+  margin: 16px 0 0;
275
+  color: #7b6b5c;
276
+  font-size: 14px;
277
+}
278
+
279
+.bottom-note {
280
+  width: 700px;
281
+  margin: 18px 0 22px 36px;
282
+  padding: 4px 12px;
283
+  border: 2px solid #d8caba;
284
+  border-radius: 4px;
285
+  background: #eadfce;
286
+  color: #7b6b5c;
287
+  font-size: 14px;
288
+  box-sizing: border-box;
289
+}
290
+
291
+@media (max-width: 1100px) {
292
+  .search-input-row {
293
+    grid-template-columns: 1fr 120px;
294
+  }
295
+
296
+  .filter-button-main,
297
+  .back-list-button {
298
+    width: 100%;
299
+  }
300
+
301
+  .search-result-panel {
302
+    border-radius: 36px;
303
+  }
304
+
305
+  .result-row {
306
+    overflow-x: auto;
307
+  }
308
+
309
+  .danka-result-row,
310
+  .family-result-row,
311
+  .kakocho-result-row {
312
+    min-width: 860px;
313
+  }
314
+}
315
+
316
+@media (max-width: 800px) {
317
+  .breadcrumb {
318
+    position: static;
319
+    transform: none;
320
+    padding: 16px 20px 0;
321
+  }
322
+
323
+  .search-page {
324
+    flex-direction: column;
325
+  }
326
+
327
+  .search-main {
328
+    width: 100%;
329
+    padding: 16px 20px 32px;
330
+  }
331
+
332
+  .search-panel {
333
+    padding: 24px 20px 30px;
334
+    border-radius: 32px;
335
+  }
336
+
337
+  .page-title-row {
338
+    flex-direction: column;
339
+  }
340
+
341
+  .page-title-row h1 {
342
+    font-size: 26px;
343
+  }
344
+
345
+  .search-input-row {
346
+    grid-template-columns: 1fr;
347
+    gap: 10px;
348
+  }
349
+
350
+  .search-filter-button {
351
+    flex: 1;
352
+    min-width: 100px;
353
+  }
354
+
355
+  .bottom-note {
356
+    width: auto;
357
+    margin: 10px 20px 22px;
358
+  }
359
+}

+ 22
- 0
src/app/pages/search/search.spec.ts Zobrazit soubor

@@ -0,0 +1,22 @@
1
+import { ComponentFixture, TestBed } from '@angular/core/testing';
2
+
3
+import { Search } from './search';
4
+
5
+describe('Search', () => {
6
+  let component: Search;
7
+  let fixture: ComponentFixture<Search>;
8
+
9
+  beforeEach(async () => {
10
+    await TestBed.configureTestingModule({
11
+      imports: [Search],
12
+    }).compileComponents();
13
+
14
+    fixture = TestBed.createComponent(Search);
15
+    component = fixture.componentInstance;
16
+    await fixture.whenStable();
17
+  });
18
+
19
+  it('should create', () => {
20
+    expect(component).toBeTruthy();
21
+  });
22
+});

+ 107
- 0
src/app/pages/search/search.ts Zobrazit soubor

@@ -0,0 +1,107 @@
1
+import { Component } from '@angular/core';
2
+import { FormBuilder, FormsModule } from '@angular/forms';
3
+import { Router, RouterLink } from '@angular/router';
4
+import { Danka } from '../../models/danka';
5
+import { Family } from '../../models/family';
6
+import { Kakocho } from '../../models/kakocho';
7
+import { DankaService } from '../../services/dankaService';
8
+import { FamilyService } from '../../services/family-service';
9
+import { KakochoService } from '../../services/kakocho-service';
10
+import { AppHeader } from '../../share/header/app-header';
11
+import { AppSideMenu } from '../../share/side-menu/app-side-menu';
12
+
13
+@Component({
14
+  selector: 'app-search',
15
+  imports: [AppHeader, AppSideMenu, FormsModule, RouterLink],
16
+  templateUrl: './search.html',
17
+  styleUrl: './search.scss',
18
+})
19
+export class Search {
20
+  searchKeyword = '';
21
+  selectedSearchType = 'all';
22
+  searchTypeFilters = [
23
+    { label: 'すべて', value: 'all' },
24
+    { label: '檀家(世帯)', value: 'danka' },
25
+    { label: '家族', value: 'family' },
26
+    { label: '過去帳', value: 'kakocho' },
27
+  ];
28
+  dankaResults: Danka[] = [];
29
+  familyResults: Family[] = [];
30
+  kakochoResults: Kakocho[] = [];
31
+  totalResultCount = 0;
32
+
33
+  constructor(
34
+    private dankaService: DankaService,
35
+    private familyService: FamilyService,
36
+    private kakochoService: KakochoService,
37
+  ) {}
38
+
39
+  // フィルタータブの選択処理
40
+  changeSearchType(searchType: string): void {
41
+    this.selectedSearchType = searchType;
42
+    this.searchAll();
43
+  }
44
+
45
+  // 全検索の処理
46
+  searchAll() {
47
+    const keyword = this.searchKeyword.trim();
48
+    this.dankaResults = [];
49
+    this.familyResults = [];
50
+    this.kakochoResults = [];
51
+    this.totalResultCount = 0;
52
+    if (keyword === '') {
53
+      return;
54
+    }
55
+    // 檀家・家族・過去帳の情報を取得
56
+    const dankaList = this.dankaService.getDankaList();
57
+    const familyList = this.familyService.getFamilyList();
58
+    const kakochoList = this.kakochoService.getKakochoList();
59
+    //檀家のキーワード検索
60
+    if (this.selectedSearchType === 'all' || this.selectedSearchType === 'danka') {
61
+      this.dankaResults = dankaList.filter(
62
+        (danka) =>
63
+          danka.householdName.includes(keyword) ||
64
+          danka.householder.includes(keyword) ||
65
+          danka.postalCode.includes(keyword) ||
66
+          danka.address.includes(keyword) ||
67
+          danka.phones.some((phone) => phone.tel.includes(keyword) || phone.note.includes(keyword)),
68
+      );
69
+    }
70
+    //家族のキーワード検索
71
+    if (this.selectedSearchType === 'all' || this.selectedSearchType === 'family') {
72
+      this.familyResults = familyList.filter(
73
+        (family) =>
74
+          family.name.includes(keyword) ||
75
+          family.furigana.includes(keyword) ||
76
+          family.relationship.includes(keyword) ||
77
+          family.birthDate.includes(keyword) ||
78
+          family.note.includes(keyword),
79
+      );
80
+    }
81
+    //過去帳のキーワード検索
82
+    if (this.selectedSearchType === 'all' || this.selectedSearchType === 'kakocho') {
83
+      this.kakochoResults = kakochoList.filter(
84
+        (kakocho) =>
85
+          kakocho.name.includes(keyword) ||
86
+          kakocho.furigana.includes(keyword) ||
87
+          kakocho.relationship.includes(keyword) ||
88
+          kakocho.kaimyo.includes(keyword) ||
89
+          kakocho.deathDate.includes(keyword) ||
90
+          kakocho.ageAtDeath.includes(keyword) ||
91
+          kakocho.note.includes(keyword),
92
+      );
93
+    }
94
+    //検索結果の件数カウント
95
+    this.totalResultCount =
96
+      this.dankaResults.length + this.familyResults.length + this.kakochoResults.length;
97
+  }
98
+
99
+  clearSearch(): void {
100
+    this.searchKeyword = '';
101
+    this.selectedSearchType = 'all';
102
+    this.dankaResults = [];
103
+    this.familyResults = [];
104
+    this.kakochoResults = [];
105
+    this.totalResultCount = 0;
106
+  }
107
+}

+ 5
- 5
src/app/services/family-service.ts Zobrazit soubor

@@ -26,11 +26,6 @@ export class FamilyService {
26 26
     },
27 27
   ];
28 28
 
29
-  //サービスの家族一覧の取得
30
-  // getDankaList(): Danka[] {
31
-  //   return this.dankaList;
32
-  // }
33
-
34 29
   //檀家と紐づいている家族情報の取得
35 30
   getFamiliesByDankaId(dankaId: string): Family[] {
36 31
     return this.families.filter((family) => family.dankaId === dankaId);
@@ -58,4 +53,9 @@ export class FamilyService {
58 53
     }
59 54
     this.families.splice(index, 1);
60 55
   }
56
+
57
+  //家族情報を全件取得する処理
58
+  getFamilyList(): Family[] {
59
+    return this.families;
60
+  }
61 61
 }

+ 4
- 3
src/app/share/side-menu/app-side-menu.html Zobrazit soubor

@@ -23,9 +23,10 @@
23 23
         家系図
24 24
       </a>
25 25
 
26
-      <a routerLink="/user-setting" routerLinkActive="active" class="menu-button">
27
-        利用者設定
28
-      </a>
26
+<!--  ユーザー数が増えた際に実装    -->
27
+<!--      <a routerLink="/user-setting" routerLinkActive="active" class="menu-button">-->
28
+<!--        利用者設定-->
29
+<!--      </a>-->
29 30
     </nav>
30 31
   </div>
31 32
 </aside>

Loading…
Zrušit
Uložit