diff --git a/packages/components/plh/index.ts b/packages/components/plh/index.ts index 22ff38bdc..a869e3844 100644 --- a/packages/components/plh/index.ts +++ b/packages/components/plh/index.ts @@ -1,19 +1,27 @@ import { PlhParentPointCounterComponent } from "./parent-point-counter/parent-point-counter.component"; import { PlhParentPointBoxComponent } from "./parent-point-box/parent-point-box.component"; import { PlhModuleListItemComponent } from "./plh-kids-kw/components/module-list-item/module-list-item.component"; +import { PlhActivityCheckInComponent } from "./plh-kids-kw/components/activity-check-in/activity-check-in.component"; -export { PlhParentPointCounterComponent, PlhParentPointBoxComponent, PlhModuleListItemComponent }; +export { + PlhParentPointCounterComponent, + PlhParentPointBoxComponent, + PlhModuleListItemComponent, + PlhActivityCheckInComponent, +}; export const PLH_COMPONENTS = [ PlhParentPointCounterComponent, PlhParentPointBoxComponent, PlhModuleListItemComponent, + PlhActivityCheckInComponent, ]; export const PLH_COMPONENT_MAPPING = { parent_point_counter: PlhParentPointCounterComponent, parent_point_box: PlhParentPointBoxComponent, plh_module_list_item: PlhModuleListItemComponent, + plh_activity_check_in: PlhActivityCheckInComponent, }; export type PLHComponentName = keyof typeof PLH_COMPONENT_MAPPING; diff --git a/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.html b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.html new file mode 100644 index 000000000..98b084857 --- /dev/null +++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.html @@ -0,0 +1,50 @@ +@if (daysLeft >= 1) { +
+
+
+
+
+
+
+
+
In {{ daysLeft }} day{{ daysLeft > 1 ? "s" : "" }}
+
+ +
+ +
+
+
+

{{ params.title }}

+
+
+ +
+
+
+
+} @else { +
+
+
+ +
+
+
+

{{ params.title }}

+
+
+ +
+
+
+
+} diff --git a/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.scss b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.spec.ts b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.spec.ts new file mode 100644 index 000000000..36f89a12d --- /dev/null +++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing"; +import { IonicModule } from "@ionic/angular"; + +import { PlhActivityCheckInComponent } from "./activity-check-in.component"; + +describe("ActivityCheckInComponent", () => { + let component: PlhActivityCheckInComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PlhActivityCheckInComponent], + imports: [IonicModule.forRoot()], + }).compileComponents(); + + fixture = TestBed.createComponent(PlhActivityCheckInComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it("should create", () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.ts b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.ts new file mode 100644 index 000000000..aae901e1c --- /dev/null +++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.ts @@ -0,0 +1,127 @@ +import { Component, OnInit } from "@angular/core"; +import { TemplateBaseComponent } from "src/app/shared/components/template/components/base"; +import { TemplateTranslateService } from "src/app/shared/components/template/services/template-translate.service"; +import { getNumberParamFromTemplateRow, getStringParamFromTemplateRow } from "src/app/shared/utils"; + +interface IActivityCheckInParams { + /* TEMPLATE PARAMETER: "activity_id". The activity identifier attached at the bottom of the component */ + id: string; + /* TEMPLATE PARAMETER: "title". The title attached at the bottom of the component */ + title?: string; + /* TEMPLATE PARAMETER: "locked_icon_asset". The icon that shows when the activity is locked */ + lockedIconAsset?: string; + /* TEMPLATE PARAMETER: "locked_image_asset". The illustration that shows when the activity is locked */ + lockedImageAsset?: string; + /* TEMPLATE PARAMETER: "unlocked_icon_asset". The icon that shows when the activity is unlocked */ + unlockedIconAsset?: string; + /* TEMPLATE PARAMETER: "unlocked_image_asset". The illustration that shows when the activity is locked */ + unlockedImageAsset?: string; + /* TEMPLATE PARAMETER: "days_to_count_down". The illustration that shows when the activity is locked */ + countDownDays?: number; +} + +@Component({ + selector: "plh-activity-check-in", + templateUrl: "./activity-check-in.component.html", + styleUrls: ["./activity-check-in.component.scss"], +}) +export class PlhActivityCheckInComponent extends TemplateBaseComponent implements OnInit { + params: Partial = {}; + + daysLeft: number; // Progress of days left + progressPercentage: number = 16; // Initial progress + + private unlockDate: Date; + + constructor(public templateTranslateService: TemplateTranslateService) { + super(); + } + + ngOnInit() { + this.getParams(); + this.daysLeft = this.params.countDownDays; + if (this._row.value) { + this.checkInTimer(); + } + } + + private getParams() { + this.params.id = getStringParamFromTemplateRow(this._row, "activity_id", null); + this.params.title = getStringParamFromTemplateRow(this._row, "title", null); + this.params.lockedIconAsset = getStringParamFromTemplateRow( + this._row, + "locked_icon_asset", + null + ); + this.params.lockedImageAsset = getStringParamFromTemplateRow( + this._row, + "locked_image_asset", + null + ); + this.params.unlockedIconAsset = getStringParamFromTemplateRow( + this._row, + "unlocked_icon_asset", + null + ); + this.params.unlockedImageAsset = getStringParamFromTemplateRow( + this._row, + "unlocked_image_asset", + null + ); + this.params.countDownDays = getNumberParamFromTemplateRow(this._row, "days_to_count_down", 6); + } + + private getLocalStorageKey(): string { + return `activity_${this.params.id}`; + } + + // Calculates the days until check in + private checkInTimer() { + const localStorageKey = this.getLocalStorageKey(); + const storedDate = localStorage.getItem(localStorageKey); + if (storedDate) { + this.unlockDate = new Date(storedDate); + this.updateProgress(); + } else { + this.unlockDate = this.getMidnightOfDate(new Date()); + this.unlockDate.setDate(this.unlockDate.getDate() + this.params.countDownDays); + localStorage.setItem(localStorageKey, this.unlockDate.toISOString()); + } + const dailyInterval = this.getMillisecondsUntilMidnight(); // Count until midnight + setTimeout(() => { + setInterval(() => this.updateProgress(), 24 * 60 * 60 * 1000); + }, dailyInterval); + } + + // Update the progress bar and unlock state + private updateProgress(): void { + const now = this.getMidnightOfDate(new Date()); + this.daysLeft = this.unlockDate.getTime() - now.getTime(); + + if (now < this.unlockDate) { + this.daysLeft = Math.ceil( + (this.unlockDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000) + ); + this.progressPercentage = + ((this.params.countDownDays - this.daysLeft) / this.params.countDownDays) * 100; + } else { + this.progressPercentage = 16; + localStorage.removeItem(this.getLocalStorageKey()); + } + } + + // Get the number of milliseconds until midnight. + private getMillisecondsUntilMidnight(): number { + const now = new Date(); + const midnight = this.getMidnightOfDate(now); + midnight.setDate(midnight.getDate() + 1); + return midnight.getTime() - now.getTime(); + } + + // Get the midnight of a given date. + private getMidnightOfDate(date: Date): Date { + const midnight = new Date(date); + midnight.setHours(0, 0, 0, 0); + return midnight; + } +} diff --git a/src/theme/themes/plh_kids_kw/_overrides.scss b/src/theme/themes/plh_kids_kw/_overrides.scss index 55f1cb1a6..3596f3fb4 100644 --- a/src/theme/themes/plh_kids_kw/_overrides.scss +++ b/src/theme/themes/plh_kids_kw/_overrides.scss @@ -604,6 +604,84 @@ body[data-theme="plh_kids_kw"] { } } + // activity-check-in + plh-activity-check-in { + .activity-container { + width: 45%; + .wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + } + .progress-tracker { + width: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + .wrapper { + width: 46%; + } + .progress-bar-container { + background-color: var(--ion-color-gray-200); + height: 14px; + border-radius: var(--ion-border-radius-rounded); + margin-right: 8px; + overflow: hidden; + width: 100%; + } + .progress-bar { + border-radius: var(--ion-border-radius-rounded); + height: 100%; + background-color: #e0b160; + transition: + width 0.5s ease, + background-color 0.5s ease; + } + .countdown-text { + color: var(--ion-color-gray-600); + font-weight: var(--font-weight-medium); + } + } + .image { + img { + width: 140px; + } + } + .unlocked .image { + img { + width: 170px; + } + } + .details { + display: flex; + flex-direction: row; + align-items: center; + margin-top: -5px; + .title h4 { + color: var(--ion-color-gray-600); + font-weight: var(--font-weight-bold); + } + .icon { + display: flex; + align-items: center; + img { + width: 28px; + } + } + } + } + .activity-container[data-language-direction~="rtl"] { + .progress-tracker { + .progress-bar-container { + margin-right: 0; + margin-left: 6px; + } + } + } + } + // Radio Button Group plh-radio-group { .btn_triangle {