diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html
index 0e89ac2..329c66c 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html
@@ -1,4 +1,4 @@
-
+
+
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss
index 21c6766..c3a0d8d 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss
@@ -11,6 +11,16 @@
display: flex;
flex-direction: column;
gap: 0.625rem;
+ height: 130px;
+ min-width: 0;
+}
+
+.card.clickable {
+ cursor: pointer;
+}
+
+.card.clickable:hover {
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}
.card-header {
@@ -38,6 +48,9 @@
font-size: 1rem;
font-weight: 600;
color: #111;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.description {
@@ -45,4 +58,8 @@
font-size: 0.875rem;
color: #555;
line-height: 1.5;
-}
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts
index 9070017..abd6797 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts
@@ -1,4 +1,5 @@
-import { Component, input } from '@angular/core';
+import { Component, inject, input } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
import { StudentGoalItem } from '../../../shared/classes/student-goal';
@Component({
@@ -13,6 +14,9 @@ export class GoalCard {
// ************************** Declarations *************************
+ private readonly router = inject(Router);
+ private readonly route = inject(ActivatedRoute);
+
readonly goal = input.required
();
// ************************** Properties ***************************
@@ -21,5 +25,13 @@ export class GoalCard {
// ************************ Event Handlers *************************
+ // *****************************************************************
+ // Navigates to the progress events page for this goal.
+ // *****************************************************************
+ onCardClick() {
+ const studentId = this.route.snapshot.paramMap.get('studentId')!;
+ this.router.navigate(['/students', studentId, 'goals', this.goal().goalId, 'progress']);
+ }
+
// ********************** Support Procedures ***********************
}
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html b/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html
index f38b1a8..a72299d 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html
@@ -1,31 +1,28 @@
- @if (studentIdentifier()) {
- {{ studentIdentifier() }}
- }
+@if (studentIdentifier()) {
+
+}
+
@if (showAddModal()) {
-
+
}
@if (errorMessage()) {
- {{ errorMessage() }}
+{{ errorMessage() }}
}
@if (goals().length === 0 && !errorMessage()) {
- No goals yet. Click + Add a Goal to get started.
+No goals yet. Click + Add a Goal to get started.
} @else {
-
- @for (goal of goals(); track goal.goalId) {
-
- }
-
-}
+
+ @for (goal of goals(); track goal.goalId) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss b/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss
index 592f50b..56ef332 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss
@@ -36,10 +36,11 @@
margin-left: 0.5rem;
}
-.student-label {
- font-size: 0.9375rem;
+.section-header {
+ font-size: 1.125rem;
font-weight: 600;
color: #333;
+ margin: 0 0 0.5rem;
}
.spacer {
@@ -65,4 +66,4 @@
gap: 1rem;
overflow-y: auto;
flex: 1;
-}
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.html b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.html
new file mode 100644
index 0000000..4cb8d0d
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.html
@@ -0,0 +1,9 @@
+
+
{{ event().content }}
+
+
+
+
+
{{ event().createdByName }}
+
{{ event().createdAt | date:'MMM d, y' }}
+
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.scss b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.scss
new file mode 100644
index 0000000..b1d74cb
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.scss
@@ -0,0 +1,61 @@
+:host {
+ display: block;
+}
+
+.card {
+ background: #fff;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
+ padding: 1.25rem 1.5rem;
+ display: grid;
+ grid-template-columns: 1fr auto;
+ grid-template-rows: auto auto;
+ gap: 0.75rem 1rem;
+}
+
+.content {
+ grid-column: 1;
+ grid-row: 1;
+ margin: 0;
+ font-size: 0.9375rem;
+ color: #333;
+ line-height: 1.6;
+}
+
+.action-icons {
+ grid-column: 2;
+ grid-row: 1;
+ display: flex;
+ gap: 0.5rem;
+ align-self: start;
+ justify-content: flex-end;
+}
+
+.author {
+ grid-column: 1;
+ grid-row: 2;
+ font-size: 0.8125rem;
+ font-weight: 600;
+ color: #888;
+}
+
+.date {
+ grid-column: 2;
+ grid-row: 2;
+ font-size: 0.8125rem;
+ color: #888;
+ text-align: right;
+}
+
+.icon-btn {
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 1rem;
+ color: #888;
+ padding: 0.125rem;
+}
+
+.icon-btn:hover {
+ color: #4f46e5;
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.spec.ts
new file mode 100644
index 0000000..6fe4032
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProgressItem } from './progress-item';
+
+describe('ProgressItem', () => {
+ let component: ProgressItem;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ProgressItem]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ProgressItem);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.ts
new file mode 100644
index 0000000..401e6f4
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.ts
@@ -0,0 +1,26 @@
+import { Component, input } from '@angular/core';
+import { DatePipe } from '@angular/common';
+import { ProgressEventDto } from '../../../shared/classes/progress-event.dto';
+
+@Component({
+ selector: 'app-progress-item',
+ imports: [DatePipe],
+ templateUrl: './progress-item.html',
+ styleUrl: './progress-item.scss',
+})
+export class ProgressItem {
+
+ // ************************** Constructor **************************
+
+ // ************************** Declarations *************************
+
+ readonly event = input.required();
+
+ // ************************** Properties ***************************
+
+ // ************************ Public Methods *************************
+
+ // ************************ Event Handlers *************************
+
+ // ********************** Support Procedures ***********************
+}
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.html b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.html
new file mode 100644
index 0000000..ffb1510
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.html
@@ -0,0 +1,35 @@
+
+
+
+
+@if (studentIdentifier() && goalTitle()) {
+
+}
+
+@if (errorMessage()) {
+{{ errorMessage() }}
+}
+
+@if (filteredEvents().length === 0 && !errorMessage()) {
+No progress events recorded yet.
+} @else {
+
+ @for (evt of filteredEvents(); track evt.progressEventId) {
+
+ }
+
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.scss b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.scss
new file mode 100644
index 0000000..28b1883
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.scss
@@ -0,0 +1,116 @@
+:host {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.toolbar {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ height: 40px;
+ padding-right: 0.5rem;
+ border-radius: 8px;
+ background: #fff;
+ border-bottom: 1px solid #ddd;
+ margin-bottom: 1rem;
+ flex-shrink: 0;
+}
+
+.toolbar-btn {
+ padding: 0.375rem 0.75rem;
+ background: transparent;
+ color: #4f46e5;
+ border: 1px solid #4f46e5;
+ border-radius: 6px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ cursor: pointer;
+}
+
+.toolbar-btn:hover {
+ background: #eef2ff;
+}
+
+.back-btn {
+ margin-left: 0.5rem;
+}
+
+.header-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+ padding-right: calc(0.75rem + 17px);
+ flex-shrink: 0;
+}
+
+.section-header {
+ font-size: 1.125rem;
+ font-weight: 600;
+ color: #333;
+ margin: 0;
+}
+
+.filter-count {
+ font-weight: 400;
+ color: #888;
+}
+
+.search-box {
+ position: relative;
+}
+
+.search-input {
+ padding: 0.375rem 2rem 0.375rem 0.75rem;
+ border: 1px solid #ccc;
+ border-radius: 6px;
+ font-size: 0.875rem;
+ width: 200px;
+ outline: none;
+}
+
+.search-input:focus {
+ border-color: #4f46e5;
+}
+
+.clear-btn {
+ position: absolute;
+ right: 0.375rem;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ cursor: pointer;
+ font-size: 1.125rem;
+ color: #888;
+ line-height: 1;
+ padding: 0 0.125rem;
+}
+
+.clear-btn:hover {
+ color: #333;
+}
+
+.error {
+ font-size: 0.875rem;
+ color: #dc2626;
+ margin: 0 0 1rem;
+}
+
+.empty-state {
+ color: #888;
+ font-size: 0.9375rem;
+ margin: 2rem auto;
+ text-align: center;
+}
+
+.event-list {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ overflow-y: auto;
+ scrollbar-gutter: stable;
+ flex: 1;
+ padding-right: 0.75rem;
+}
\ No newline at end of file
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.spec.ts
new file mode 100644
index 0000000..f6d6a62
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { ProgressList } from './progress-list';
+
+describe('ProgressList', () => {
+ let component: ProgressList;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [ProgressList]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(ProgressList);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.ts
new file mode 100644
index 0000000..da48b3b
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.ts
@@ -0,0 +1,130 @@
+import { Component, computed, inject, signal, OnDestroy } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { Subject } from 'rxjs';
+import { debounceTime } from 'rxjs/operators';
+import { ProgressItem } from '../progress-item/progress-item';
+import { ProgressEventDto } from '../../../shared/classes/progress-event.dto';
+import { DummyStudentService } from '../../../shared/services/dummy-student.service';
+import { StudentService } from '../../../shared/services/student.service';
+
+@Component({
+ selector: 'app-progress-list',
+ imports: [ProgressItem],
+ templateUrl: './progress-list.html',
+ styleUrl: './progress-list.scss',
+})
+export class ProgressList implements OnDestroy {
+
+ // ************************** Constructor **************************
+
+ constructor() {
+ this.studentId = this.route.snapshot.paramMap.get('studentId')!;
+ this.goalId = this.route.snapshot.paramMap.get('goalId')!;
+ this.loadEvents();
+ this.loadGoalTitle();
+
+ this.searchInput$.pipe(debounceTime(300)).subscribe(term => {
+ this.searchTerm.set(term);
+ });
+ }
+
+ // ************************** Declarations *************************
+
+ private readonly dummyService = inject(DummyStudentService);
+ private readonly studentService = inject(StudentService);
+ private readonly route = inject(ActivatedRoute);
+ private readonly router = inject(Router);
+
+ private readonly studentId: string;
+ private readonly goalId: string;
+ private readonly searchInput$ = new Subject();
+
+ protected readonly studentIdentifier = signal(null);
+ protected readonly goalTitle = signal(null);
+ protected readonly events = signal([]);
+ protected readonly errorMessage = signal(null);
+ protected readonly rawSearchText = signal('');
+ protected readonly searchTerm = signal('');
+
+ // ************************** Properties ***************************
+
+ // *****************************************************************
+ // Returns events filtered by the debounced search term. Matches
+ // against the event content (case-insensitive). Only filters when
+ // the term is at least 2 characters.
+ // *****************************************************************
+ protected readonly filteredEvents = computed(() => {
+ const term = this.searchTerm().trim().toLowerCase();
+ if (term.length < 2) return this.events();
+ return this.events().filter(e => e.content.toLowerCase().includes(term));
+ });
+
+ protected readonly isFiltered = computed(() => {
+ return this.searchTerm().trim().length >= 2 && this.filteredEvents().length !== this.events().length;
+ });
+
+ // ************************ Public Methods *************************
+
+ // ************************ Event Handlers *************************
+
+ // *****************************************************************
+ // Navigates back to the goals list for this student.
+ // *****************************************************************
+ onBack() {
+ this.router.navigate(['/students', this.studentId, 'goals']);
+ }
+
+ // *****************************************************************
+ // Pushes the raw input value into the debounce stream.
+ // *****************************************************************
+ onSearchInput(value: string) {
+ this.rawSearchText.set(value);
+ this.searchInput$.next(value);
+ }
+
+ // *****************************************************************
+ // Clears the search box and resets the filter.
+ // *****************************************************************
+ onClearSearch() {
+ this.rawSearchText.set('');
+ this.searchTerm.set('');
+ }
+
+ ngOnDestroy() {
+ this.searchInput$.complete();
+ }
+
+ // ********************** Support Procedures ***********************
+
+ // *****************************************************************
+ // Loads progress events for the given goal from the dummy service,
+ // sorted newest-first by createdAt.
+ // TODO: Replace DummyStudentService with StudentService
+ // *****************************************************************
+ private loadEvents() {
+ this.dummyService.getProgressEventsForGoal(this.goalId).then(result => {
+ if (!result.success) {
+ this.errorMessage.set(result.message);
+ } else {
+ const sorted = (result.payload ?? [])
+ .slice()
+ .sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
+ this.events.set(sorted);
+ }
+ });
+ }
+
+ // *****************************************************************
+ // Loads the goal title from the student's goal list so the heading
+ // can display "Progress for ".
+ // *****************************************************************
+ private loadGoalTitle() {
+ this.studentService.getGoalsForStudent(this.studentId).then(result => {
+ if (result.success && result.payload) {
+ this.studentIdentifier.set(result.payload.studentIdentifier);
+ const goal = result.payload.goals.find(g => g.goalId === this.goalId);
+ this.goalTitle.set(goal?.title ?? null);
+ }
+ });
+ }
+}
diff --git a/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts b/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts
index 5ba5a31..2ca5e85 100644
--- a/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts
+++ b/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts
@@ -2,6 +2,7 @@ import { Routes } from '@angular/router';
import { Home } from './pages/home/home';
import { StudentCardList } from './components/student-card-list/student-card-list';
import { GoalList } from './components/goal-list/goal-list';
+import { ProgressList } from './components/progress-list/progress-list';
export default [
{
@@ -11,6 +12,7 @@ export default [
{ path: '', redirectTo: 'students', pathMatch: 'full' },
{ path: 'students', component: StudentCardList },
{ path: 'students/:studentId/goals', component: GoalList },
+ { path: 'students/:studentId/goals/:goalId/progress', component: ProgressList },
],
},
] satisfies Routes;
diff --git a/ui/winstudentgoaltracker/src/app/mobile/pages/add-progress-event/add-progress-event.scss b/ui/winstudentgoaltracker/src/app/mobile/pages/add-progress-event/add-progress-event.scss
index 6c0d7cf..a47b048 100644
--- a/ui/winstudentgoaltracker/src/app/mobile/pages/add-progress-event/add-progress-event.scss
+++ b/ui/winstudentgoaltracker/src/app/mobile/pages/add-progress-event/add-progress-event.scss
@@ -69,7 +69,7 @@
border: 1px solid #ddd;
border-radius: 8px;
padding: 0.75rem;
- font-size: 0.875rem;
+ font-size: 1rem;
font-family: inherit;
resize: vertical;
margin-bottom: 1rem;
diff --git a/ui/winstudentgoaltracker/src/app/shared/classes/progress-event.dto.ts b/ui/winstudentgoaltracker/src/app/shared/classes/progress-event.dto.ts
new file mode 100644
index 0000000..7c965a8
--- /dev/null
+++ b/ui/winstudentgoaltracker/src/app/shared/classes/progress-event.dto.ts
@@ -0,0 +1,6 @@
+export interface ProgressEventDto {
+ progressEventId: string;
+ content: string;
+ createdAt: Date;
+ createdByName: string;
+}
diff --git a/ui/winstudentgoaltracker/src/app/shared/services/dummy-student.service.ts b/ui/winstudentgoaltracker/src/app/shared/services/dummy-student.service.ts
index cf8c8c8..c997ef0 100644
--- a/ui/winstudentgoaltracker/src/app/shared/services/dummy-student.service.ts
+++ b/ui/winstudentgoaltracker/src/app/shared/services/dummy-student.service.ts
@@ -3,6 +3,7 @@ import { StudentCardDto } from '../classes/student-card.dto';
import { ApiResult } from '../classes/api-result';
import { StudentGoalSummary, StudentGoalItem } from '../classes/student-goal';
import { CreateGoalDto } from '../classes/create-goal.dto';
+import { ProgressEventDto } from '../classes/progress-event.dto';
@Injectable({
providedIn: 'root',
@@ -19,39 +20,39 @@ export class DummyStudentService {
goals: [
{ goalId: 'g1', goalParentId: null, title: 'Improve reading comprehension', description: 'Work on main-idea identification and inference skills across fiction and nonfiction texts.', category: 'Academics', progressEventCount: 5 },
{ goalId: 'g2', goalParentId: null, title: 'Complete algebra module', description: 'Finish all units in the algebra course including linear equations and graphing.', category: 'Academics', progressEventCount: 2 },
- { goalId: 'g3', goalParentId: null, title: 'Weekly journal entries', description: 'Write a reflective journal entry each week to build writing fluency.', category: 'Communication', progressEventCount: 8 },
+ { goalId: 'g3', goalParentId: null, title: 'Weekly journal entries', description: 'Write a reflective journal entry each week to build writing fluency.', category: 'Communication', progressEventCount: 8 },
],
},
'2': {
studentIdentifier: 'M.K',
goals: [
- { goalId: 'g4', goalParentId: null, title: 'Pass certification exam', description: 'Prepare for and pass the industry certification exam by end of quarter.', category: 'Career Readiness', progressEventCount: 3 },
- { goalId: 'g5', goalParentId: null, title: 'Attendance above 90%', description: 'Maintain consistent attendance throughout the term.', category: 'Behavior', progressEventCount: 0 },
- { goalId: 'g6', goalParentId: null, title: 'Complete internship hours', description: 'Log the required 40 hours at the assigned internship site.', category: 'Career Readiness', progressEventCount: 12 },
- { goalId: 'g7', goalParentId: null, title: 'Portfolio project', description: 'Build a personal portfolio showcasing completed coursework and projects.', category: 'Career Readiness', progressEventCount: 1 },
+ { goalId: 'g4', goalParentId: null, title: 'Pass certification exam', description: 'Prepare for and pass the industry certification exam by end of quarter.', category: 'Career Readiness', progressEventCount: 3 },
+ { goalId: 'g5', goalParentId: null, title: 'Attendance above 90%', description: 'Maintain consistent attendance throughout the term.', category: 'Behavior', progressEventCount: 0 },
+ { goalId: 'g6', goalParentId: null, title: 'Complete internship hours', description: 'Log the required 40 hours at the assigned internship site.', category: 'Career Readiness', progressEventCount: 12 },
+ { goalId: 'g7', goalParentId: null, title: 'Portfolio project', description: 'Build a personal portfolio showcasing completed coursework and projects.', category: 'Career Readiness', progressEventCount: 1 },
],
},
'3': {
studentIdentifier: 'A.R',
goals: [
- { goalId: 'g8', goalParentId: null, title: 'GED preparation', description: 'Complete practice tests and study modules for GED math and reading sections.', category: 'Academics', progressEventCount: 6 },
- { goalId: 'g9', goalParentId: null, title: 'Resume workshop', description: 'Attend the resume writing workshop and produce a final draft.', category: 'Career Readiness', progressEventCount: 0 },
+ { goalId: 'g8', goalParentId: null, title: 'GED preparation', description: 'Complete practice tests and study modules for GED math and reading sections.', category: 'Academics', progressEventCount: 6 },
+ { goalId: 'g9', goalParentId: null, title: 'Resume workshop', description: 'Attend the resume writing workshop and produce a final draft.', category: 'Career Readiness', progressEventCount: 0 },
],
},
'4': {
studentIdentifier: 'T.W',
goals: [
- { goalId: 'g10', goalParentId: null, title: 'Public speaking practice', description: 'Present in front of the class at least once per month.', category: 'Communication', progressEventCount: 4 },
- { goalId: 'g11', goalParentId: null, title: 'Math placement improvement', description: 'Move up one placement level in math by the end of the semester.', category: 'Academics', progressEventCount: 7 },
- { goalId: 'g12', goalParentId: null, title: 'Conflict resolution strategies', description: 'Learn and apply at least three de-escalation techniques.', category: 'Behavior', progressEventCount: 2 },
- { goalId: 'g13', goalParentId: null, title: 'Daily attendance streak', description: 'Achieve a 30-day unbroken attendance streak.', category: 'Behavior', progressEventCount: 0 },
- { goalId: 'g14', goalParentId: null, title: 'Job shadow experience', description: 'Complete a job shadow day in a field of interest.', category: 'Career Readiness', progressEventCount: 1 },
+ { goalId: 'g10', goalParentId: null, title: 'Public speaking practice', description: 'Present in front of the class at least once per month.', category: 'Communication', progressEventCount: 4 },
+ { goalId: 'g11', goalParentId: null, title: 'Math placement improvement', description: 'Move up one placement level in math by the end of the semester.', category: 'Academics', progressEventCount: 7 },
+ { goalId: 'g12', goalParentId: null, title: 'Conflict resolution strategies', description: 'Learn and apply at least three de-escalation techniques.', category: 'Behavior', progressEventCount: 2 },
+ { goalId: 'g13', goalParentId: null, title: 'Daily attendance streak', description: 'Achieve a 30-day unbroken attendance streak.', category: 'Behavior', progressEventCount: 0 },
+ { goalId: 'g14', goalParentId: null, title: 'Job shadow experience', description: 'Complete a job shadow day in a field of interest.', category: 'Career Readiness', progressEventCount: 1 },
],
},
'5': {
studentIdentifier: 'L.C',
goals: [
- { goalId: 'g15', goalParentId: null, title: 'Improve typing speed', description: 'Reach 40 WPM with 95% accuracy on typing assessments.', category: 'Career Readiness', progressEventCount: 3 },
+ { goalId: 'g15', goalParentId: null, title: 'Improve typing speed', description: 'Reach 40 WPM with 95% accuracy on typing assessments.', category: 'Career Readiness', progressEventCount: 3 },
],
},
};
@@ -65,7 +66,7 @@ export class DummyStudentService {
// until the API endpoint is available.
// *****************************************************************
async getMyStudents(): Promise> {
- var payload = [
+ var payload = [
{
studentId: '1',
identifier: 'J.B',
@@ -97,8 +98,7 @@ export class DummyStudentService {
async getGoalsForStudent(studentId: string): Promise> {
var goals = this.data[studentId] ?? null;
- if (goals === null)
- {
+ if (goals === null) {
return ApiResult.fail('Student not found');
}
@@ -129,6 +129,38 @@ export class DummyStudentService {
return ApiResult.empty();
}
+ // *****************************************************************
+ // Returns hardcoded progress events for a given goal. The real
+ // service will call the API with the goal ID.
+ // TODO: Replace with actual API call
+ // *****************************************************************
+ async getProgressEventsForGoal(goalId: string): Promise> {
+ const events: ProgressEventDto[] = [
+ { progressEventId: 'pe1', content: 'Student demonstrated strong understanding of the topic during today\'s session. Completed all assigned exercises independently.', createdAt: new Date('2026-02-28T10:30:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe2', content: 'Reviewed previous week\'s material. Student needed some additional guidance but showed improvement by end of session.', createdAt: new Date('2026-02-27T14:15:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe3', content: 'Initial assessment completed. Identified key areas for focused practice going forward.', createdAt: new Date('2026-02-26T09:00:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe4', content: 'Practiced problem-solving strategies with real-world scenarios. Student engaged well and asked thoughtful questions.', createdAt: new Date('2026-02-25T11:00:00'), createdByName: 'Lisa Martinez' },
+ { progressEventId: 'pe5', content: 'Worked on time management skills. Created a weekly planner and discussed prioritization techniques.', createdAt: new Date('2026-02-24T13:45:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe6', content: 'Student completed a timed practice exercise. Performance improved compared to last week.', createdAt: new Date('2026-02-21T10:00:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe7', content: 'Discussed long-term objectives and broke them into smaller milestones. Student is motivated and on track.', createdAt: new Date('2026-02-20T15:30:00'), createdByName: 'Lisa Martinez' },
+ { progressEventId: 'pe8', content: 'Reviewed feedback from previous assignment. Student made corrections independently with minimal prompting.', createdAt: new Date('2026-02-19T09:30:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe9', content: 'Collaborative session with peer group. Student contributed actively and helped explain concepts to others.', createdAt: new Date('2026-02-18T14:00:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe10', content: 'Introduced new topic area. Student showed curiosity and took detailed notes for independent review.', createdAt: new Date('2026-02-14T11:15:00'), createdByName: 'Lisa Martinez' },
+ { progressEventId: 'pe11', content: 'Student struggled with today\'s material but remained focused. Will revisit key concepts next session.', createdAt: new Date('2026-02-13T10:45:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe12', content: 'Follow-up on yesterday\'s challenging session. Student showed marked improvement after overnight reflection.', createdAt: new Date('2026-02-12T09:00:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe13', content: 'Mid-term progress check. Student is meeting expectations in most areas with room for growth in written expression.', createdAt: new Date('2026-02-11T13:00:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe14', content: 'Worked through a series of practice problems. Accuracy rate was 85%, up from 70% two weeks ago.', createdAt: new Date('2026-02-10T10:30:00'), createdByName: 'Lisa Martinez' },
+ { progressEventId: 'pe15', content: 'Student presented a short summary of recent learning to the group. Showed confidence and clarity.', createdAt: new Date('2026-02-07T14:30:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe16', content: 'Explored supplementary resources together. Student identified two additional practice tools to use independently.', createdAt: new Date('2026-02-06T11:00:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe17', content: 'Reviewed study habits and discussed strategies for staying consistent. Student committed to a daily review routine.', createdAt: new Date('2026-02-05T09:15:00'), createdByName: 'Lisa Martinez' },
+ { progressEventId: 'pe18', content: 'Hands-on activity session. Student completed the project ahead of schedule with strong attention to detail.', createdAt: new Date('2026-02-04T13:30:00'), createdByName: 'Sarah Johnson' },
+ { progressEventId: 'pe19', content: 'Addressed gaps identified in the initial assessment. Student showed solid understanding of foundational concepts.', createdAt: new Date('2026-02-03T10:00:00'), createdByName: 'Mike Thompson' },
+ { progressEventId: 'pe20', content: 'First session of the term. Established rapport and set expectations for the upcoming weeks.', createdAt: new Date('2026-01-31T09:00:00'), createdByName: 'Sarah Johnson' },
+ ];
+
+ return ApiResult.ok(events);
+ }
+
// ************************ Event Handlers *************************