From 486de79264ce3255378f77efb4ba51da1c344274 Mon Sep 17 00:00:00 2001 From: Kyle Kemp Date: Fri, 26 Apr 2024 16:05:38 -0500 Subject: [PATCH] closes #10 --- .../below-the-fold.component.html | 3 + src/app/app-routing.module.ts | 4 ++ src/app/card/card.page.ts | 3 - src/app/faq.service.ts | 71 +++++++++++++++---- src/app/faq/faq-routing.module.ts | 17 +++++ src/app/faq/faq.module.ts | 22 ++++++ src/app/faq/faq.page.html | 57 +++++++++++++++ src/app/faq/faq.page.scss | 22 ++++++ src/app/faq/faq.page.ts | 50 +++++++++++++ src/app/meta.service.ts | 4 +- 10 files changed, 235 insertions(+), 18 deletions(-) create mode 100644 src/app/faq/faq-routing.module.ts create mode 100644 src/app/faq/faq.module.ts create mode 100644 src/app/faq/faq.page.html create mode 100644 src/app/faq/faq.page.scss create mode 100644 src/app/faq/faq.page.ts diff --git a/src/app/_shared/components/below-the-fold/below-the-fold.component.html b/src/app/_shared/components/below-the-fold/below-the-fold.component.html index 246ccf3..55f02cf 100644 --- a/src/app/_shared/components/below-the-fold/below-the-fold.component.html +++ b/src/app/_shared/components/below-the-fold/below-the-fold.component.html @@ -31,6 +31,9 @@
Support

+
+ FAQ +
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 1ed8a6c..1c94c9c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -33,6 +33,10 @@ const routes: Routes = [ loadChildren: () => import('./sets/sets.module').then((m) => m.SetsPageModule), }, + { + path: 'faq', + loadChildren: () => import('./faq/faq.module').then((m) => m.FaqPageModule), + }, { path: '**', redirectTo: '', diff --git a/src/app/card/card.page.ts b/src/app/card/card.page.ts index 0244546..db05b8b 100644 --- a/src/app/card/card.page.ts +++ b/src/app/card/card.page.ts @@ -51,7 +51,6 @@ export class CardPage implements OnInit { this.template = compiledTemplate(cardData); this.cardData.set(cardData); - this.loadFAQ(); } search(query: string) { @@ -61,6 +60,4 @@ export class CardPage implements OnInit { searchTag(tag: string) { this.search(`tag:"${tag}"`); } - - loadFAQ() {} } diff --git a/src/app/faq.service.ts b/src/app/faq.service.ts index db893f1..5db60b8 100644 --- a/src/app/faq.service.ts +++ b/src/app/faq.service.ts @@ -1,4 +1,11 @@ -import { effect, inject, Injectable, Injector, signal } from '@angular/core'; +import { + effect, + inject, + Injectable, + Injector, + signal, + type WritableSignal, +} from '@angular/core'; import type { ICardFAQ, ICardFAQEntry } from '../../interfaces'; import { LocaleService } from './locale.service'; import { MetaService } from './meta.service'; @@ -14,14 +21,17 @@ export class FAQService { private isReady = signal(false); public ready$ = this.isReady.asReadonly(); - private allFAQs: Array<{ - productId: string; - locale: string; - url: string; - }> = []; + private allFAQs: WritableSignal< + Array<{ + productId: string; + locale: string; + url: string; + }> + > = signal([]); - private faqByProductIdAndLocale: Record> = - {}; + private faqByProductIdAndLocale: WritableSignal< + Record> + > = signal({}); private faqByProductLocaleCard: Record< string, @@ -29,7 +39,7 @@ export class FAQService { > = {}; public async init() { - this.allFAQs = this.metaService.getAllFAQs(); + this.allFAQs.set(this.metaService.getAllFAQs()); effect( () => { @@ -41,16 +51,23 @@ export class FAQService { } private loadLocaleFAQs(locale: string) { - this.allFAQs.forEach(async (faq) => { + const baseFAQs = this.faqByProductIdAndLocale(); + + this.allFAQs().forEach(async (faq) => { if (faq.locale !== locale) return; - if (this.faqByProductIdAndLocale[faq.productId]?.[faq.locale]) return; + if (baseFAQs[faq.productId]?.[faq.locale]) return; - this.faqByProductIdAndLocale[faq.productId] ??= {}; + baseFAQs[faq.productId] ??= {}; const faqData = await fetch(faq.url); const realData = await faqData.json(); - this.faqByProductIdAndLocale[faq.productId][faq.locale] = realData; + baseFAQs[faq.productId][faq.locale] = realData; + + this.faqByProductIdAndLocale.set({ + ...this.faqByProductIdAndLocale(), + ...baseFAQs, + }); realData.forEach((cardFAQ: ICardFAQ) => { this.faqByProductLocaleCard[faq.productId] ??= {}; @@ -63,6 +80,34 @@ export class FAQService { if (!this.isReady()) this.isReady.set(true); } + public getFAQs(): Array<{ + productId: string; + locale: string; + faq: ICardFAQ[]; + }> { + if (!this.isReady()) return []; + + const faqData = this.faqByProductIdAndLocale(); + const locale = this.localeService.currentLocale(); + + return Object.keys(faqData) + .map((productId) => ({ + productId, + locale, + faq: faqData[productId][locale], + })) + .filter((p) => p.faq) + .flat(); + } + + public getProductFAQ( + productId: string, + locale: string + ): ICardFAQ[] | undefined { + const faq = this.faqByProductIdAndLocale(); + return faq?.[productId]?.[locale]; + } + public getCardFAQ(productId: string, card: string): ICardFAQEntry[] { return ( this.faqByProductLocaleCard[productId]?.[ diff --git a/src/app/faq/faq-routing.module.ts b/src/app/faq/faq-routing.module.ts new file mode 100644 index 0000000..5eaead2 --- /dev/null +++ b/src/app/faq/faq-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { type Routes, RouterModule } from '@angular/router'; + +import { FaqPage } from './faq.page'; + +const routes: Routes = [ + { + path: '', + component: FaqPage, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class FaqPageRoutingModule {} diff --git a/src/app/faq/faq.module.ts b/src/app/faq/faq.module.ts new file mode 100644 index 0000000..ffe1422 --- /dev/null +++ b/src/app/faq/faq.module.ts @@ -0,0 +1,22 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; + +import { IonicModule } from '@ionic/angular'; + +import { FaqPageRoutingModule } from './faq-routing.module'; + +import { SharedModule } from '../_shared/shared.module'; +import { FaqPage } from './faq.page'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + IonicModule, + FaqPageRoutingModule, + SharedModule, + ], + declarations: [FaqPage], +}) +export class FaqPageModule {} diff --git a/src/app/faq/faq.page.html b/src/app/faq/faq.page.html new file mode 100644 index 0000000..72c175c --- /dev/null +++ b/src/app/faq/faq.page.html @@ -0,0 +1,57 @@ + + + + + +
+ + @if(currentFAQ(); as faq) { + + + + + @for(entry of faq; track $index) { + + + {{ entry.card }} + + + @for(faqEntry of entry.faq; track $index) { + + +

Q: {{ faqEntry.q }}

+

A: {{ faqEntry.a }}

+
+
+ } +
+ } +
+
+
+
+ } + + @else { + + + @for(faq of faqs(); track $index) { + + + + {{ metaService.getProductNameByProductId(faq.productId) }} + + + + {{ faq.faq.length | number }} FAQs + + + + } + + + } +
+ + +
\ No newline at end of file diff --git a/src/app/faq/faq.page.scss b/src/app/faq/faq.page.scss new file mode 100644 index 0000000..cdf5e0c --- /dev/null +++ b/src/app/faq/faq.page.scss @@ -0,0 +1,22 @@ +.faq-tile { + min-height: 200px; + padding: 4px; + + border: 2px solid #000; + + cursor: pointer; + user-select: none; + + &:hover { + border: 2px solid var(--ion-color-primary); + } + + ion-card-content { + display: flex; + justify-content: center; + align-items: center; + + font-size: 2em; + padding: 1em; + } +} diff --git a/src/app/faq/faq.page.ts b/src/app/faq/faq.page.ts new file mode 100644 index 0000000..07e7c62 --- /dev/null +++ b/src/app/faq/faq.page.ts @@ -0,0 +1,50 @@ +import { Component, computed, inject, signal } from '@angular/core'; +import { ActivatedRoute, Router } from '@angular/router'; +import type { ICardFAQ } from '../../../interfaces'; +import { FAQService } from '../faq.service'; +import { MetaService } from '../meta.service'; + +@Component({ + selector: 'app-faq', + templateUrl: './faq.page.html', + styleUrls: ['./faq.page.scss'], +}) +export class FaqPage { + private router = inject(Router); + private route = inject(ActivatedRoute); + private faqService = inject(FAQService); + public metaService = inject(MetaService); + + private locale = signal(''); + private productId = signal(''); + + public faqs = computed(() => this.faqService.getFAQs()); + public currentFAQ = computed(() => + this.faqService.getProductFAQ(this.productId(), this.locale()) + ); + + constructor() {} + + ionViewDidEnter() { + this.locale.set(this.route.snapshot.queryParamMap.get('locale') ?? ''); + this.productId.set( + this.route.snapshot.queryParamMap.get('productId') ?? '' + ); + + if (!this.locale() || !this.productId()) { + this.router.navigate(['/faq']); + return; + } + } + + loadFAQ(faq: { productId: string; locale: string; faq: ICardFAQ[] }) { + this.router.navigate([], { + relativeTo: this.route, + queryParams: { locale: faq.locale, productId: faq.productId }, + queryParamsHandling: 'merge', + }); + + this.locale.set(faq.locale); + this.productId.set(faq.productId); + } +} diff --git a/src/app/meta.service.ts b/src/app/meta.service.ts index c93f287..7278c6e 100644 --- a/src/app/meta.service.ts +++ b/src/app/meta.service.ts @@ -31,9 +31,9 @@ export class MetaService { this.allProducts.forEach((product) => { this.productNamesByProductId[product.id] = product.name; this.templatesByProductId[product.id] = product.cardTemplate; - this.rulesByProductId[product.id] = product.external.rules; + this.rulesByProductId[product.id] = product.external?.rules ?? ''; this.filtersByProductId[product.id] = product.filters; - this.faqByProductId[product.id] = product.external.faq; + this.faqByProductId[product.id] = product.external?.faq ?? {}; }); }