mirror of
https://github.com/opelly27/WinStudentGoalTracker.git
synced 2026-05-20 14:37:34 +00:00
Added Goals fields
This commit is contained in:
+4
@@ -0,0 +1,4 @@
|
||||
<div class="tile" [class.checked]="checked()" (click)="onTap()">
|
||||
<span class="check-indicator">{{ checked() ? '✓' : '' }}</span>
|
||||
<span class="tile-label">{{ label() }}</span>
|
||||
</div>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
.tile {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
padding: 0.625rem 0.75rem;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
|
||||
.tile:active {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.tile.checked {
|
||||
border-color: #4f46e5;
|
||||
background: #eef2ff;
|
||||
}
|
||||
|
||||
.check-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 1.375rem;
|
||||
height: 1.375rem;
|
||||
border: 2px solid #ccc;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.tile.checked .check-indicator {
|
||||
background: #4f46e5;
|
||||
border-color: #4f46e5;
|
||||
}
|
||||
|
||||
.tile-label {
|
||||
font-size: 0.9375rem;
|
||||
color: #333;
|
||||
line-height: 1.3;
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ToggleBenchmark } from './toggle-benchmark';
|
||||
|
||||
describe('ToggleBenchmark', () => {
|
||||
let component: ToggleBenchmark;
|
||||
let fixture: ComponentFixture<ToggleBenchmark>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [ToggleBenchmark]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ToggleBenchmark);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { Component, input, output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-toggle-benchmark',
|
||||
imports: [],
|
||||
templateUrl: './toggle-benchmark.html',
|
||||
styleUrl: './toggle-benchmark.scss',
|
||||
})
|
||||
export class ToggleBenchmark {
|
||||
|
||||
// ************************** Declarations *************************
|
||||
|
||||
readonly label = input.required<string>();
|
||||
readonly checked = input.required<boolean>();
|
||||
readonly toggled = output<void>();
|
||||
|
||||
// ************************ Event Handlers *************************
|
||||
|
||||
onTap() {
|
||||
this.toggled.emit();
|
||||
}
|
||||
}
|
||||
+12
-3
@@ -15,11 +15,20 @@
|
||||
<div class="form-card">
|
||||
<label class="field-label">Progress event notes</label>
|
||||
<textarea class="notes-input" placeholder="Type your message here." rows="5" [(ngModel)]="notes"></textarea>
|
||||
|
||||
<label class="field-label">Voice note</label>
|
||||
<button class="voice-btn">🎙 Record voice note</button>
|
||||
</div>
|
||||
|
||||
@if (benchmarkItems().length > 0) {
|
||||
<label class="field-label">Associated Benchmarks</label>
|
||||
<div class="benchmark-scroll">
|
||||
@for (bm of benchmarkItems(); track bm.benchmarkId) {
|
||||
<app-toggle-benchmark
|
||||
[label]="bm.label"
|
||||
[checked]="bm.checked"
|
||||
(toggled)="onToggleBenchmark(bm.benchmarkId)" />
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Save button -->
|
||||
<button class="save-btn" [disabled]="!canSave()" (click)="onSave()">
|
||||
{{ saving() ? 'Saving...' : 'Save' }}
|
||||
|
||||
+11
@@ -129,4 +129,15 @@
|
||||
color: #dc2626;
|
||||
border-radius: 8px;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
/* Scrollable benchmark region */
|
||||
.benchmark-scroll {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
+45
-5
@@ -3,12 +3,19 @@ import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { describeHttpError } from '../../../shared/classes/http-errors';
|
||||
import { DummyStudentService } from '../../../shared/services/dummy-student.service';
|
||||
import { StudentService } from '../../../shared/services/student.service';
|
||||
|
||||
interface BenchmarkCheckItem {
|
||||
benchmarkId: string;
|
||||
label: string;
|
||||
checked: boolean;
|
||||
}
|
||||
|
||||
import { ToggleBenchmark } from '../../components/toggle-benchmark/toggle-benchmark';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-progress-event',
|
||||
imports: [FormsModule],
|
||||
imports: [FormsModule, ToggleBenchmark],
|
||||
templateUrl: './add-progress-event.html',
|
||||
styleUrl: './add-progress-event.scss',
|
||||
})
|
||||
@@ -21,6 +28,7 @@ export class AddProgressEvent {
|
||||
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') ?? '';
|
||||
this.loadBenchmarks();
|
||||
}
|
||||
|
||||
// ************************** Declarations *************************
|
||||
@@ -37,6 +45,7 @@ export class AddProgressEvent {
|
||||
protected readonly notes = signal('');
|
||||
protected readonly error = signal<string | null>(null);
|
||||
protected readonly saving = signal(false);
|
||||
protected readonly benchmarkItems = signal<BenchmarkCheckItem[]>([]);
|
||||
|
||||
// ************************** Properties ***************************
|
||||
|
||||
@@ -59,15 +68,31 @@ export class AddProgressEvent {
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Saves the progress event. On success, returns to the goal list.
|
||||
// On failure, displays the error message from the API.
|
||||
// Toggles a benchmark checkbox.
|
||||
// *****************************************************************
|
||||
onToggleBenchmark(benchmarkId: string) {
|
||||
this.benchmarkItems.update(items =>
|
||||
items.map(b => b.benchmarkId === benchmarkId ? { ...b, checked: !b.checked } : b)
|
||||
);
|
||||
}
|
||||
|
||||
// *****************************************************************
|
||||
// Saves the progress event with optional benchmark associations.
|
||||
// On success, returns to the goal list.
|
||||
// *****************************************************************
|
||||
async onSave() {
|
||||
this.error.set(null);
|
||||
this.saving.set(true);
|
||||
|
||||
const checkedIds = this.benchmarkItems()
|
||||
.filter(b => b.checked)
|
||||
.map(b => b.benchmarkId);
|
||||
|
||||
try {
|
||||
const result = await this.studentService.addProgressEvent(this.studentId, this.goalId, this.notes().trim());
|
||||
const result = await this.studentService.addProgressEvent(
|
||||
this.studentId, this.goalId, this.notes().trim(),
|
||||
checkedIds.length > 0 ? checkedIds : undefined
|
||||
);
|
||||
this.saving.set(false);
|
||||
if (result.success) {
|
||||
this.router.navigate(['students', this.studentId, 'goals']);
|
||||
@@ -81,4 +106,19 @@ export class AddProgressEvent {
|
||||
}
|
||||
|
||||
// ********************** Support Procedures ***********************
|
||||
|
||||
// *****************************************************************
|
||||
// Loads benchmarks for the current goal to populate checkboxes.
|
||||
// *****************************************************************
|
||||
private async loadBenchmarks() {
|
||||
const result = await this.studentService.getBenchmarksForStudent(this.studentId);
|
||||
if (result.success && result.payload) {
|
||||
const goalBenchmarks = result.payload.benchmarks.filter(b => b.goalId === this.goalId);
|
||||
this.benchmarkItems.set(goalBenchmarks.map(b => ({
|
||||
benchmarkId: b.benchmarkId,
|
||||
label: b.shortName || b.benchmark,
|
||||
checked: false,
|
||||
})));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user