Renamed some of the goal fields to align with business logic

This commit is contained in:
ivan-pelly
2026-03-14 16:30:17 -07:00
parent 7f91e2e557
commit 4d9b83c327
50 changed files with 279 additions and 149 deletions
@@ -8,18 +8,6 @@
<form class="modal-body" (ngSubmit)="onSubmit()" #goalForm="ngForm">
<div class="field">
<label for="title">Title<span class="required">*</span></label>
<input
id="title"
type="text"
[(ngModel)]="form.title"
name="title"
required
placeholder="e.g. Improve reading comprehension"
/>
</div>
<div class="field">
<label for="category">Category<span class="required">*</span></label>
<input
@@ -32,18 +20,6 @@
/>
</div>
@if (parentGoalOptions().length > 0) {
<div class="field">
<label for="goalParentId">Parent Goal <span class="optional">(optional)</span></label>
<select id="goalParentId" [(ngModel)]="form.goalParentId" name="goalParentId">
<option [ngValue]="null">None</option>
@for (goal of parentGoalOptions(); track goal.goalId) {
<option [ngValue]="goal.goalId">{{ goal.title }}</option>
}
</select>
</div>
}
<div class="field">
<label for="description">Description</label>
<textarea
@@ -55,6 +31,29 @@
></textarea>
</div>
<div class="field">
<label for="baseline">Baseline</label>
<textarea
id="baseline"
[(ngModel)]="form.baseline"
name="baseline"
rows="3"
placeholder="Enter baseline..."
></textarea>
</div>
@if (parentGoalOptions().length > 0) {
<div class="field">
<label for="goalParentId">Parent Goal <span class="optional">(optional)</span></label>
<select id="goalParentId" [(ngModel)]="form.goalParentId" name="goalParentId">
<option [ngValue]="null">None</option>
@for (goal of parentGoalOptions(); track goal.goalId) {
<option [ngValue]="goal.goalId">{{ goal.category }}</option>
}
</select>
</div>
}
@if (errorMessage()) {
<p class="error">{{ errorMessage() }}</p>
}
@@ -31,9 +31,9 @@ export class AddGoalModal {
);
protected form: CreateGoalDto = {
title: '',
description: '',
category: '',
baseline: '',
goalParentId: null,
};
@@ -9,14 +9,14 @@
<form class="modal-body" (ngSubmit)="onSubmit()" #studentForm="ngForm">
<div class="field">
<label for="identifier">Identifier</label>
<label for="identifier">Student</label>
<input
id="identifier"
type="text"
[(ngModel)]="form.identifier"
name="identifier"
required
placeholder="e.g. Student123"
placeholder="Initials or other non-personally identifiable label"
/>
</div>
@@ -16,7 +16,7 @@
<div class="detail-card">
<div class="field">
<span class="field-label">Goal</span>
<span class="field-value">{{ goalTitle }}</span>
<span class="field-value">{{ goalCategory }}</span>
</div>
<div class="field">
<label class="field-label" for="benchmarkText">Benchmark</label>
@@ -47,7 +47,7 @@ export class BenchmarkCardFull implements OnDestroy {
private savedBenchmarkText = '';
// Read-only metadata
protected goalTitle = '';
protected goalCategory = '';
protected createdByName = '';
protected createdAt: Date | null = null;
protected updatedAt: Date | null = null;
@@ -127,7 +127,7 @@ export class BenchmarkCardFull implements OnDestroy {
this.isNew.set(true);
this.benchmarkText = '';
this.savedBenchmarkText = '';
this.loadGoalTitle();
this.loadGoalCategory();
this.loaded.set(true);
return;
}
@@ -147,7 +147,7 @@ export class BenchmarkCardFull implements OnDestroy {
this.benchmarkText = bm.benchmark;
this.savedBenchmarkText = bm.benchmark;
this.goalTitle = bm.goalTitle;
this.goalCategory = bm.goalCategory;
this.createdByName = bm.createdByName;
this.createdAt = bm.createdAt;
this.updatedAt = bm.updatedAt;
@@ -156,13 +156,13 @@ export class BenchmarkCardFull implements OnDestroy {
}
// *****************************************************************
// Loads the goal title for a new benchmark.
// Loads the goal category for a new benchmark.
// *****************************************************************
private loadGoalTitle() {
private loadGoalCategory() {
this.studentService.getGoalsForStudent(this.studentId).then(result => {
if (result.success && result.payload) {
const goal = result.payload.goals.find(g => g.goalId === this.goalId);
this.goalTitle = goal?.title ?? '';
this.goalCategory = goal?.category ?? '';
}
});
}
@@ -1,6 +1,6 @@
<div class="card">
<div class="card-header">
<span class="goal-badge">{{ benchmark().goalTitle }}</span>
<span class="goal-badge">{{ benchmark().goalCategory }}</span>
@if (benchmark().updatedAt) {
<span class="date">Updated: {{ benchmark().updatedAt | date:'M/d/yy' }}</span>
} @else {
@@ -4,8 +4,10 @@
<button class="toolbar-btn" (click)="onAddBenchmark()">+ Add a Benchmark</button>
</div>
@if (studentIdentifier()) {
<h2 class="section-header">Benchmarks for {{ studentIdentifier() }}</h2>
@if (goalCategory()) {
<h2 class="section-header">Benchmarks for {{ goalCategory() }}</h2>
} @else {
<h2 class="section-header">Benchmarks</h2>
}
@if (errorMessage()) {
@@ -13,8 +15,11 @@
}
@if (benchmarks().length === 0 && !errorMessage()) {
<p class="empty-state">No benchmarks yet. Click <a class="empty-link" (click)="onAddBenchmark()">Add a Benchmark</a> to
get started.</p>
<div class="empty-state">
<p>Benchmarks are milestones on the way to achieving a goal. They are optional in this system.</p>
<p>No benchmarks yet.</p>
<p>Click <strong>+ Add a Benchmark</strong> in the upper right to get started.</p>
</div>
} @else {
<div class="card-grid">
@for (bm of benchmarks(); track bm.benchmarkId) {
@@ -54,16 +54,13 @@
}
.empty-state {
color: #888;
font-size: 0.9375rem;
margin: 2rem auto;
text-align: center;
}
.empty-link {
color: #4f46e5;
text-decoration: underline;
cursor: pointer;
padding: 3rem 1.5rem;
color: #555;
font-size: 0.9375rem;
background: #fff;
border-radius: 8px;
border: 1px solid #ddd;
}
.card-grid {
@@ -18,6 +18,7 @@ export class BenchmarkList {
this.studentId = this.route.snapshot.paramMap.get('studentId')!;
this.goalId = this.route.snapshot.paramMap.get('goalId') || '';
this.loadBenchmarks();
this.loadGoalCategory();
}
// ************************** Declarations *************************
@@ -28,7 +29,7 @@ export class BenchmarkList {
protected readonly studentId: string;
protected readonly goalId: string;
protected readonly studentIdentifier = signal<string | null>(null);
protected readonly goalCategory = signal<string | null>(null);
protected readonly benchmarks = signal<BenchmarkDto[]>([]);
protected readonly errorMessage = signal<string | null>(null);
@@ -43,22 +44,51 @@ export class BenchmarkList {
}
onBack() {
this.router.navigate(['/students', this.studentId, 'goals', this.goalId]);
if (this.goalId) {
this.router.navigate(['/students', this.studentId, 'goals', this.goalId]);
} else {
this.router.navigate(['/students', this.studentId, 'goals']);
}
}
// ********************** Support Procedures ***********************
// *****************************************************************
// Loads benchmarks for the student from the service.
// Loads benchmarks for the student from the service. Also sets
// goalCategory from the first benchmark's data.
// *****************************************************************
private loadBenchmarks() {
this.studentService.getBenchmarksForStudent(this.studentId).then(data => {
if (!data.success) {
this.errorMessage.set(data.message);
} else {
this.studentIdentifier.set(data.payload?.studentIdentifier ?? null);
this.benchmarks.set(data.payload?.benchmarks ?? []);
const benchmarks = data.payload?.benchmarks ?? [];
this.benchmarks.set(benchmarks);
// Set the goal category from benchmark data if available
if (this.goalId && benchmarks.length > 0) {
const match = benchmarks.find(b => b.goalId === this.goalId);
if (match) this.goalCategory.set(match.goalCategory);
}
// If we still don't have a category, load it from the goals API
if (!this.goalCategory()) {
this.loadGoalCategory();
}
}
});
}
// *****************************************************************
// Loads the goal category from the goals API as a fallback when
// no benchmarks exist yet to extract it from.
// *****************************************************************
private loadGoalCategory() {
if (!this.goalId) return;
this.studentService.getGoalsForStudent(this.studentId).then(result => {
if (!result.success || !result.payload) return;
const goal = result.payload.goals.find(g => g.goalId === this.goalId);
this.goalCategory.set(goal?.category ?? null);
});
}
}
@@ -1,5 +1,5 @@
<div class="toolbar">
<button class="toolbar-btn back-btn" (click)="onBack()">&#8593; Student</button>
<button class="toolbar-btn back-btn" (click)="onBack()">&#8593; Goals</button>
<span class="toolbar-title">Goal Detail</span>
<span class="spacer"></span>
</div>
@@ -15,8 +15,8 @@
@if (loaded()) {
<div class="detail-card">
<div class="field">
<label class="field-label" for="title">Title</label>
<input id="title" class="field-input" type="text" [(ngModel)]="title" />
<label class="field-label" for="baseline">Baseline</label>
<input id="baseline" class="field-input" type="text" [(ngModel)]="baseline" />
</div>
<div class="field">
<label class="field-label" for="description">Description</label>
@@ -39,18 +39,18 @@ export class GoalCardFull implements OnDestroy {
protected readonly saving = signal(false);
// Form fields
protected title = '';
protected description = '';
protected category = '';
protected baseline = '';
// Read-only metadata
protected progressEventCount = 0;
protected benchmarkCount = 0;
// Snapshot
private savedTitle = '';
private savedDescription = '';
private savedCategory = '';
private savedBaseline = '';
// ************************** Properties ***************************
@@ -58,9 +58,9 @@ export class GoalCardFull implements OnDestroy {
// Returns true if form values differ from the saved snapshot.
// *****************************************************************
hasChanges(): boolean {
return this.title !== this.savedTitle
|| this.description !== this.savedDescription
|| this.category !== this.savedCategory;
return this.description !== this.savedDescription
|| this.category !== this.savedCategory
|| this.baseline !== this.savedBaseline;
}
// ************************ Public Methods *************************
@@ -76,17 +76,17 @@ export class GoalCardFull implements OnDestroy {
this.successMessage.set(null);
const result = await this.studentService.updateGoal(this.studentId, this.goalId, {
title: this.title,
description: this.description,
category: this.category,
baseline: this.baseline,
});
this.saving.set(false);
if (result.success) {
this.savedTitle = this.title;
this.savedDescription = this.description;
this.savedCategory = this.category;
this.savedBaseline = this.baseline;
this.successMessage.set('Changes saved.');
} else {
this.errorMessage.set(result.message);
@@ -97,15 +97,15 @@ export class GoalCardFull implements OnDestroy {
// Reverts form fields to the last-saved snapshot.
// *****************************************************************
onCancel() {
this.title = this.savedTitle;
this.description = this.savedDescription;
this.category = this.savedCategory;
this.baseline = this.savedBaseline;
this.errorMessage.set(null);
this.successMessage.set(null);
}
onBack() {
this.router.navigate(['/students', this.studentId]);
this.router.navigate(['/students', this.studentId, 'goals']);
}
onProgressEvents() {
@@ -139,15 +139,15 @@ export class GoalCardFull implements OnDestroy {
return;
}
this.title = goal.title;
this.description = goal.description;
this.category = goal.category;
this.baseline = goal.baseline;
this.progressEventCount = goal.progressEventCount;
this.benchmarkCount = goal.benchmarkCount;
this.savedTitle = goal.title;
this.savedDescription = goal.description;
this.savedCategory = goal.category;
this.savedBaseline = goal.baseline;
this.loaded.set(true);
});
}
@@ -4,7 +4,7 @@
<span class="event-count">{{ goal().progressEventCount }} events</span>
</div>
<h3 class="title">{{ goal().title }}</h3>
<h3 class="title">{{ goal().category }}</h3>
<p class="description">{{ goal().description }}</p>
<div class="card-footer">
@@ -1,6 +1,6 @@
:host {
display: block;
width: 300px;
width: 450px;
}
.card {
@@ -22,7 +22,10 @@
}
@if (goals().length === 0 && !errorMessage()) {
<p class="empty-state">No goals yet. Click <strong>+ Add a Goal</strong> to get started.</p>
<div class="empty-state">
<p>No goals yet.</p>
<p>Click <strong>+ Add a Goal</strong> in the upper right to get started.</p>
</div>
} @else {
<div class="card-grid">
@for (goal of goals(); track goal.goalId) {
@@ -66,10 +66,13 @@
}
.empty-state {
color: #888;
font-size: 0.9375rem;
margin: 2rem auto;
text-align: center;
padding: 3rem 1.5rem;
color: #555;
font-size: 0.9375rem;
background: #fff;
border-radius: 8px;
border: 1px solid #ddd;
}
.card-grid {
@@ -88,4 +91,5 @@
background: #fff;
border-top: 1px solid #ddd;
flex-shrink: 0;
margin-top: auto;
}
@@ -49,6 +49,7 @@ export class GoalList implements OnDestroy {
onGoalCreated(goal: StudentGoalItem) {
this.goals.update(list => [...list, goal]);
this.showAddModal.set(false);
this.studentService.notifyDataChanged();
}
onModalCancelled() {
@@ -7,10 +7,10 @@
<!-- <img class="hero-image" src="/slalomcropped.png" alt="Slalom" /> -->
@if (studentIdentifier() && goalTitle()) {
@if (studentIdentifier() && goalCategory()) {
<div class="header-row">
<h2 class="section-header">
Student: {{ studentIdentifier() }} &nbsp;&nbsp; Goal: {{ goalTitle() }}
Student: {{ studentIdentifier() }} &nbsp;&nbsp; Goal: {{ goalCategory() }}
@if (isFiltered()) {
<span class="filter-count">(showing {{ filteredEvents().length }} of {{ events().length }})</span>
}
@@ -20,7 +20,7 @@ export class ProgressList implements OnDestroy {
this.studentId = this.route.snapshot.paramMap.get('studentId')!;
this.goalId = this.route.snapshot.paramMap.get('goalId')!;
this.loadEvents();
this.loadGoalTitle();
this.loadGoalCategory();
this.searchInput$.pipe(debounceTime(300)).subscribe(term => {
this.searchTerm.set(term);
@@ -38,7 +38,7 @@ export class ProgressList implements OnDestroy {
private readonly searchInput$ = new Subject<string>();
protected readonly studentIdentifier = signal<string | null>(null);
protected readonly goalTitle = signal<string | null>(null);
protected readonly goalCategory = signal<string | null>(null);
protected readonly events = signal<ProgressEventDto[]>([]);
protected readonly errorMessage = signal<string | null>(null);
protected readonly rawSearchText = signal('');
@@ -118,16 +118,15 @@ export class ProgressList implements OnDestroy {
}
// *****************************************************************
// Loads the goal title from the student's goal list so the heading
// can display "Progress for <goal title>".
// Loads the goal category from the student's goal list so the heading
// can display "Progress for <goal category>".
// *****************************************************************
private loadGoalTitle() {
private loadGoalCategory() {
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);
}
if (!result.success || !result.payload) return;
this.studentIdentifier.set(result.payload.studentIdentifier);
const goal = result.payload.goals.find(g => g.goalId === this.goalId);
this.goalCategory.set(goal?.category ?? null);
});
}
}
@@ -44,6 +44,7 @@ export class StudentCardList {
onStudentCreated(student: StudentCardDto) {
this.students.update(list => this.sortByIdentifier([...list, student]));
this.showAddModal.set(false);
this.studentService.notifyDataChanged();
}
onModalCancelled() {
@@ -106,6 +106,8 @@
overflow-y: auto;
padding: 1.5rem;
background: #f5f5f5;
display: flex;
flex-direction: column;
}
/* Footer */
@@ -120,7 +120,7 @@ export class Home implements OnDestroy {
if (!result.success || !result.payload) return [];
return result.payload.goals.map(goal => ({
label: goal.title,
label: goal.category,
routerLink: ['/students', studentId, 'goals', goal.goalId],
childCount: 2,
children: [
@@ -4,7 +4,7 @@
<button class="back-btn" (click)="onBack()">← Back</button>
<span class="student-name">{{ studentIdentifier() }}</span>
</div>
<h2 class="goal-title">{{ goalTitle() }}</h2>
<h2 class="goal-title">{{ goalCategory() }}</h2>
<!-- Error message -->
@if (error()) {
@@ -17,7 +17,7 @@ export class AddProgressEvent {
// ************************** Constructor **************************
constructor() {
this.goalTitle.set(this.route.snapshot.queryParamMap.get('goalTitle') ?? '');
this.goalCategory.set(this.route.snapshot.queryParamMap.get('goalCategory') ?? '');
this.studentIdentifier.set(this.route.snapshot.queryParamMap.get('studentIdentifier') ?? '');
this.studentId = this.route.snapshot.paramMap.get('studentId') ?? '';
this.goalId = this.route.snapshot.paramMap.get('goalId') ?? '';
@@ -32,7 +32,7 @@ export class AddProgressEvent {
private readonly studentId: string;
private readonly goalId: string;
protected readonly goalTitle = signal('');
protected readonly goalCategory = signal('');
protected readonly studentIdentifier = signal('');
protected readonly notes = signal('');
protected readonly error = signal<string | null>(null);
@@ -9,8 +9,8 @@
<h2 class="section-heading">Goals</h2>
<div class="goal-cards">
@for (goal of data()?.goals; track goal.goalId) {
<div class="goal-card" (click)="onGoalClick(goal.goalId, goal.title)">
<span class="goal-title">{{ goal.title }}</span>
<div class="goal-card" (click)="onGoalClick(goal.goalId, goal.category)">
<span class="goal-title">{{ goal.category }}</span>
<span class="event-count">{{ goal.progressEventCount }}</span>
</div>
}
@@ -46,10 +46,10 @@ export class StudentGoals {
// *****************************************************************
// Navigates to the add-progress-event page for the selected goal.
// *****************************************************************
onGoalClick(goalId: string, goalTitle: string) {
onGoalClick(goalId: string, goalCategory: string) {
this.router.navigate(
['students', this.studentId, 'goals', goalId, 'add-event'],
{ queryParams: { goalTitle, studentIdentifier: this.data()?.studentIdentifier } },
{ queryParams: { goalCategory, studentIdentifier: this.data()?.studentIdentifier } },
);
}
@@ -6,7 +6,7 @@ export interface StudentBenchmarkSummary {
export interface BenchmarkDto {
benchmarkId: string;
goalId: string;
goalTitle: string;
goalCategory: string;
benchmark: string;
createdByName: string;
createdAt: Date;
@@ -1,6 +1,6 @@
export interface CreateGoalDto {
title: string;
description: string;
category: string;
baseline: string;
goalParentId: string | null;
}
@@ -6,9 +6,9 @@ export interface StudentGoalSummary {
export interface StudentGoalItem {
goalId: string; // goal.id_goal — char(36)
goalParentId: string | null;
title: string; // goal.title — varchar(255)
description: string; // goal.description — text
category: string; // goal.category — varchar(100)
baseline: string; // goal.baseline — text
progressEventCount: number; // count of progress_event rows for this goal
benchmarkCount: number; // count of benchmark rows for this goal
}
@@ -19,41 +19,41 @@ export class DummyStudentService {
'1': {
studentIdentifier: 'J.B',
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, benchmarkCount: 2 },
{ 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, benchmarkCount: 0 },
{ goalId: 'g3', goalParentId: null, title: 'Weekly journal entries', description: 'Write a reflective journal entry each week to build writing fluency.', category: 'Communication', progressEventCount: 8, benchmarkCount: 1 },
{ goalId: 'g1', goalParentId: null, description: 'Work on main-idea identification and inference skills across fiction and nonfiction texts.', category: 'Academics', baseline: '', progressEventCount: 5, benchmarkCount: 2 },
{ goalId: 'g2', goalParentId: null, description: 'Finish all units in the algebra course including linear equations and graphing.', category: 'Academics', baseline: '', progressEventCount: 2, benchmarkCount: 0 },
{ goalId: 'g3', goalParentId: null, description: 'Write a reflective journal entry each week to build writing fluency.', category: 'Communication', baseline: '', progressEventCount: 8, benchmarkCount: 1 },
],
},
'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, benchmarkCount: 0 },
{ goalId: 'g5', goalParentId: null, title: 'Attendance above 90%', description: 'Maintain consistent attendance throughout the term.', category: 'Behavior', progressEventCount: 0, benchmarkCount: 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, benchmarkCount: 0 },
{ goalId: 'g7', goalParentId: null, title: 'Portfolio project', description: 'Build a personal portfolio showcasing completed coursework and projects.', category: 'Career Readiness', progressEventCount: 1, benchmarkCount: 0 },
{ goalId: 'g4', goalParentId: null, description: 'Prepare for and pass the industry certification exam by end of quarter.', category: 'Career Readiness', baseline: '', progressEventCount: 3, benchmarkCount: 0 },
{ goalId: 'g5', goalParentId: null, description: 'Maintain consistent attendance throughout the term.', category: 'Behavior', baseline: '', progressEventCount: 0, benchmarkCount: 0 },
{ goalId: 'g6', goalParentId: null, description: 'Log the required 40 hours at the assigned internship site.', category: 'Career Readiness', baseline: '', progressEventCount: 12, benchmarkCount: 0 },
{ goalId: 'g7', goalParentId: null, description: 'Build a personal portfolio showcasing completed coursework and projects.', category: 'Career Readiness', baseline: '', progressEventCount: 1, benchmarkCount: 0 },
],
},
'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, benchmarkCount: 0 },
{ goalId: 'g9', goalParentId: null, title: 'Resume workshop', description: 'Attend the resume writing workshop and produce a final draft.', category: 'Career Readiness', progressEventCount: 0, benchmarkCount: 0 },
{ goalId: 'g8', goalParentId: null, description: 'Complete practice tests and study modules for GED math and reading sections.', category: 'Academics', baseline: '', progressEventCount: 6, benchmarkCount: 0 },
{ goalId: 'g9', goalParentId: null, description: 'Attend the resume writing workshop and produce a final draft.', category: 'Career Readiness', baseline: '', progressEventCount: 0, benchmarkCount: 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, benchmarkCount: 0 },
{ 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, benchmarkCount: 0 },
{ goalId: 'g12', goalParentId: null, title: 'Conflict resolution strategies', description: 'Learn and apply at least three de-escalation techniques.', category: 'Behavior', progressEventCount: 2, benchmarkCount: 0 },
{ goalId: 'g13', goalParentId: null, title: 'Daily attendance streak', description: 'Achieve a 30-day unbroken attendance streak.', category: 'Behavior', progressEventCount: 0, benchmarkCount: 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, benchmarkCount: 0 },
{ goalId: 'g10', goalParentId: null, description: 'Present in front of the class at least once per month.', category: 'Communication', baseline: '', progressEventCount: 4, benchmarkCount: 0 },
{ goalId: 'g11', goalParentId: null, description: 'Move up one placement level in math by the end of the semester.', category: 'Academics', baseline: '', progressEventCount: 7, benchmarkCount: 0 },
{ goalId: 'g12', goalParentId: null, description: 'Learn and apply at least three de-escalation techniques.', category: 'Behavior', baseline: '', progressEventCount: 2, benchmarkCount: 0 },
{ goalId: 'g13', goalParentId: null, description: 'Achieve a 30-day unbroken attendance streak.', category: 'Behavior', baseline: '', progressEventCount: 0, benchmarkCount: 0 },
{ goalId: 'g14', goalParentId: null, description: 'Complete a job shadow day in a field of interest.', category: 'Career Readiness', baseline: '', progressEventCount: 1, benchmarkCount: 0 },
],
},
'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, benchmarkCount: 0 },
{ goalId: 'g15', goalParentId: null, description: 'Reach 40 WPM with 95% accuracy on typing assessments.', category: 'Career Readiness', baseline: '', progressEventCount: 3, benchmarkCount: 0 },
],
},
};
@@ -116,9 +116,9 @@ export class DummyStudentService {
const newGoal: StudentGoalItem = {
goalId: `g${Date.now()}`,
goalParentId: null,
title: data.title,
description: data.description,
category: data.category,
baseline: data.baseline,
progressEventCount: 0,
benchmarkCount: 0,
};
@@ -174,9 +174,9 @@ export class DummyStudentService {
}
const benchmarks: BenchmarkDto[] = [
{ benchmarkId: 'bm1', goalId: 'g1', goalTitle: 'Improve reading comprehension', benchmark: 'Student will identify the main idea of a grade-level nonfiction passage with 80% accuracy.', createdByName: 'Jane Smith', createdAt: new Date('2026-02-15'), updatedAt: null },
{ benchmarkId: 'bm2', goalId: 'g1', goalTitle: 'Improve reading comprehension', benchmark: 'Student will make at least two supported inferences per reading session.', createdByName: 'Jane Smith', createdAt: new Date('2026-02-16'), updatedAt: new Date('2026-02-20') },
{ benchmarkId: 'bm3', goalId: 'g3', goalTitle: 'Weekly journal entries', benchmark: 'Student will complete a minimum of one paragraph (5 sentences) per journal entry.', createdByName: 'John Doe', createdAt: new Date('2026-02-18'), updatedAt: null },
{ benchmarkId: 'bm1', goalId: 'g1', goalCategory: 'Academics', benchmark: 'Student will identify the main idea of a grade-level nonfiction passage with 80% accuracy.', createdByName: 'Jane Smith', createdAt: new Date('2026-02-15'), updatedAt: null },
{ benchmarkId: 'bm2', goalId: 'g1', goalCategory: 'Academics', benchmark: 'Student will make at least two supported inferences per reading session.', createdByName: 'Jane Smith', createdAt: new Date('2026-02-16'), updatedAt: new Date('2026-02-20') },
{ benchmarkId: 'bm3', goalId: 'g3', goalCategory: 'Communication', benchmark: 'Student will complete a minimum of one paragraph (5 sentences) per journal entry.', createdByName: 'John Doe', createdAt: new Date('2026-02-18'), updatedAt: null },
];
return ApiResult.ok({
@@ -216,9 +216,9 @@ export class StudentService {
}
// *****************************************************************
// Updates a goal's title, description, and category.
// Updates a goal's description, category, and baseline.
// *****************************************************************
async updateGoal(studentId: string, goalId: string, data: { title?: string; description?: string; category?: string }): Promise<ApiResult<any>> {
async updateGoal(studentId: string, goalId: string, data: { description?: string; category?: string; baseline?: string }): Promise<ApiResult<any>> {
try {
const result = await firstValueFrom(
this.http.put<ResponseResult<any>>(`${this.base}/api/Student/${studentId}/goals/${goalId}`, data)