diff --git a/packages/components/plh/index.ts b/packages/components/plh/index.ts
index be5a377341..e5649f9044 100644
--- a/packages/components/plh/index.ts
+++ b/packages/components/plh/index.ts
@@ -1,6 +1,7 @@
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";
import { PlhCompletionModalComponent } from "./plh-kids-kw/components/completion-modal/completion-modal.component";
import { PlhModuleDetailsHeaderComponent } from "./plh-kids-kw/components/module-details-header/module-details-header.component";
import { PlhBottomNavigationBarComponent } from "./plh-kids-kw/components/bottom-navigation-bar/bottom-navigation-bar.component";
@@ -9,6 +10,7 @@ export {
PlhParentPointCounterComponent,
PlhParentPointBoxComponent,
PlhModuleListItemComponent,
+ PlhActivityCheckInComponent,
PlhCompletionModalComponent,
PlhModuleDetailsHeaderComponent,
PlhBottomNavigationBarComponent,
@@ -18,6 +20,7 @@ export const PLH_COMPONENTS = [
PlhParentPointCounterComponent,
PlhParentPointBoxComponent,
PlhModuleListItemComponent,
+ PlhActivityCheckInComponent,
PlhCompletionModalComponent,
PlhModuleDetailsHeaderComponent,
PlhBottomNavigationBarComponent,
@@ -28,6 +31,7 @@ export const PLH_COMPONENT_MAPPING = {
parent_point_box: PlhParentPointBoxComponent,
plh_module_details_header: PlhModuleDetailsHeaderComponent,
plh_module_list_item: PlhModuleListItemComponent,
+ plh_activity_check_in: PlhActivityCheckInComponent,
plh_completion_modal: PlhCompletionModalComponent,
plh_bottom_nav: PlhBottomNavigationBarComponent,
};
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 0000000000..f402b2c93f
--- /dev/null
+++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.html
@@ -0,0 +1,52 @@
+@if (locked()) {
+
+ @if (params.countdownStartDate) {
+
+
+
{{ countdownText() }}
+
+ }
+
+
+
![]()
+
+
+
+
{{ 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 0000000000..613bd87fb5
--- /dev/null
+++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.scss
@@ -0,0 +1,85 @@
+.activity-container {
+ max-width: 168px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ .progress-tracker {
+ width: 100%;
+ padding: 0 12px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ .progress-bar-container {
+ width: 40%;
+ background-color: var(--ion-color-gray-200);
+ height: 14px;
+ border-radius: 50px;
+ overflow: hidden;
+ .progress-bar {
+ border-radius: 50px;
+ height: 100%;
+ background-color: #e0b160;
+ transition:
+ width 0.5s ease,
+ background-color 0.5s ease;
+ }
+ }
+ .countdown-text {
+ margin-left: 8px;
+ color: var(--ion-color-gray-600);
+ font-weight: var(--font-weight-medium);
+ }
+ }
+ .image {
+ height: 150px;
+ img {
+ padding: 4px;
+ height: 100%;
+ width: 100%;
+ object-fit: contain;
+ }
+ }
+ .details {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ width: fit-content;
+ margin: 0 auto;
+ .title h4 {
+ text-align: center;
+ color: var(--ion-color-gray-600);
+ font-weight: var(--font-weight-bold);
+ }
+ .icon {
+ display: flex;
+ align-items: center;
+ margin: 0 4px;
+ img {
+ width: 28px;
+ }
+ }
+ }
+}
+.activity-container[data-language-direction~="rtl"] {
+ .progress-tracker {
+ .progress-bar-container {
+ margin-right: 0;
+ margin-left: 6px;
+ }
+ }
+ .image {
+ -webkit-transform: scaleX(-1);
+ transform: scaleX(-1);
+ }
+ .details {
+ .icon {
+ img {
+ -webkit-transform: scaleX(-1);
+ transform: scaleX(-1);
+ }
+ }
+ }
+}
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 0000000000..36f89a12df
--- /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 0000000000..42bdc7f55b
--- /dev/null
+++ b/packages/components/plh/plh-kids-kw/components/activity-check-in/activity-check-in.component.ts
@@ -0,0 +1,124 @@
+import { Component, computed, OnInit, signal } from "@angular/core";
+import { addDays, differenceInCalendarDays, isValid, parseISO, startOfDay } from "date-fns";
+import { TemplateBaseComponent } from "src/app/shared/components/template/components/base";
+import { TemplateTranslateService } from "src/app/shared/components/template/services/template-translate.service";
+import {
+ getNumberParamFromTemplateRow,
+ getParamFromTemplateRow,
+ getStringParamFromTemplateRow,
+} from "src/app/shared/utils";
+
+interface IActivityCheckInParams {
+ /* 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: "countdown_start_date". The date when the countdown is initially started */
+ countdownStartDate?: Date;
+ /* TEMPLATE PARAMETER: "countdown_days". The number of days after the start date that the activity is unlocked */
+ countDownDays?: number;
+ /* TEMPLATE PARAMETER: "countdown_text_list". A list of text strings relating to the number of days left to countdown, in order from fewest to most */
+ countdownTextList?: string[];
+}
+
+@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 = {};
+
+ daysUntilUnlock = signal(Infinity);
+ locked = computed(() => {
+ // If no start date is provided, activity should be locked
+ if (!this.params.countdownStartDate) return true;
+ return this.daysUntilUnlock() && this.daysUntilUnlock() > 0;
+ });
+ countdownText = computed(() => {
+ const targetIndex = Math.min(
+ this.daysUntilUnlock() - 1,
+ this.params.countdownTextList.length - 1
+ );
+ return this.params.countdownTextList[targetIndex];
+ });
+ progressPercentage = computed(() => {
+ if (this.daysUntilUnlock() === Infinity) return 0;
+ return (
+ ((this.params.countDownDays - (this.daysUntilUnlock() || 0)) / this.params.countDownDays) *
+ 100
+ );
+ });
+
+ constructor(public templateTranslateService: TemplateTranslateService) {
+ super();
+ }
+
+ ngOnInit() {
+ this.getParams();
+ this.calculateDaysUntilUnlock();
+ }
+
+ private getParams() {
+ 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, "countdown_days", 6);
+ const countdownStartDate = getParamFromTemplateRow(this._row, "countdown_start_date", null);
+ let parsedDate = null;
+ if (countdownStartDate) {
+ const attemptDate = new Date(countdownStartDate);
+ if (isValid(attemptDate)) {
+ parsedDate = attemptDate;
+ } else {
+ const attemptISO = parseISO(countdownStartDate);
+ if (isValid(attemptISO)) {
+ parsedDate = attemptISO;
+ }
+ }
+ }
+ this.params.countdownStartDate = parsedDate;
+
+ let countdownTextList = getParamFromTemplateRow(this._row, "countdown_text_list", []);
+ if (typeof countdownTextList === "string") {
+ countdownTextList = countdownTextList.split(",").map((text) => text.trim());
+ }
+ this.params.countdownTextList = countdownTextList;
+ }
+
+ private calculateDaysUntilUnlock() {
+ if (this.params.countDownDays === 0) this.daysUntilUnlock.set(0);
+ if (this.params.countdownStartDate && this.params.countDownDays) {
+ const unlockDate = addDays(this.params.countdownStartDate, this.params.countDownDays);
+ const daysRemaining = differenceInCalendarDays(
+ startOfDay(unlockDate),
+ startOfDay(new Date())
+ );
+ this.daysUntilUnlock.set(Math.max(daysRemaining, 0));
+ }
+ }
+}
diff --git a/src/app/shared/components/template/components/number-selector/number-selector.component.ts b/src/app/shared/components/template/components/number-selector/number-selector.component.ts
index 73c875c2cc..cf15d0af9c 100644
--- a/src/app/shared/components/template/components/number-selector/number-selector.component.ts
+++ b/src/app/shared/components/template/components/number-selector/number-selector.component.ts
@@ -56,7 +56,7 @@ export class TmplNumberComponent
this.displayValue = this.min_value;
}
// assign any previously saved value
- if (this._row.value) {
+ if (this._row.value || this._row.value === 0) {
this.displayValue = this._row.value;
}
}
diff --git a/src/app/shared/components/template/components/task-progress-bar/task-progress-bar.component.scss b/src/app/shared/components/template/components/task-progress-bar/task-progress-bar.component.scss
index a51f32e80e..0dd8f284c5 100644
--- a/src/app/shared/components/template/components/task-progress-bar/task-progress-bar.component.scss
+++ b/src/app/shared/components/template/components/task-progress-bar/task-progress-bar.component.scss
@@ -53,7 +53,7 @@ $progress-colour: var(--task-progress-bar-color, var(--ion-color-primary));
.task-progress-wheel {
.progress-container {
position: relative;
- width: 150px;
+ max-width: 150px;
height: 150px;
margin-bottom: 48px;
}