diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.html b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.html deleted file mode 100644 index 26221dc..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.html +++ /dev/null @@ -1,48 +0,0 @@ -
- - {{ isNew() ? 'New Benchmark' : 'Benchmark Detail' }} - -
- -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (loaded()) { -
-
- Goal: {{ goalCategory }} -
-
- - -
-
- - -
- @if (!isNew()) { -
- @if (createdByName) { - Created by: {{ createdByName }} - } - Created: {{ createdAt | date:'medium' }} - @if (updatedAt) { - Updated: {{ updatedAt | date:'medium' }} - } -
- } -
- - -
-
-} - -@if (successMessage()) { -

{{ successMessage() }}

-} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.scss b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.scss deleted file mode 100644 index f1c488f..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.scss +++ /dev/null @@ -1,120 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; - padding: 24px 28px; -} - -.toolbar { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 20px; - flex-shrink: 0; -} - -.toolbar-btn { - padding: 6px 14px; - background: transparent; - color: var(--accent-indigo); - border: 1px solid var(--accent-indigo); - border-radius: var(--radius-md); - font-size: 13px; - font-weight: 500; - cursor: pointer; - font-family: inherit; - - &:hover { - background: #EEF2FF; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.toolbar-title { - font-weight: 600; - font-size: 18px; - color: var(--text-primary); -} - -.spacer { - flex: 1; -} - -.error { - font-size: 13px; - color: #dc2626; - margin: 0 0 12px; -} - -.success { - font-size: 13px; - color: #16a34a; - margin: 12px 0 0; -} - -.detail-card { - background: var(--bg-surface); - border: 1px solid var(--border-color); - border-radius: var(--radius-xl); - padding: 22px; - max-width: 600px; -} - -.field { - display: flex; - flex-direction: column; - margin-bottom: 14px; -} - -.field-label { - font-size: 12px; - font-weight: 600; - color: #666; - margin-bottom: 4px; -} - -.field-input { - padding: 8px 10px; - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - font-size: 13px; - font-family: inherit; - outline: none; -} - -.field-textarea { - resize: vertical; - min-height: 70px; -} - -.metadata { - display: flex; - gap: 1.5rem; - margin-bottom: 14px; -} - -.meta-item { - font-size: 12px; - color: var(--text-muted); -} - -.actions { - display: flex; - justify-content: flex-end; - gap: 8px; - margin-top: 8px; -} - -.save-btn { - background: var(--accent-indigo) !important; - color: #fff !important; - border-color: var(--accent-indigo) !important; - - &:hover { - background: #3730A3 !important; - } -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.spec.ts deleted file mode 100644 index 30710b6..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BenchmarkCardFull } from './benchmark-card-full'; - -describe('BenchmarkCardFull', () => { - let component: BenchmarkCardFull; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BenchmarkCardFull] - }) - .compileComponents(); - - fixture = TestBed.createComponent(BenchmarkCardFull); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.ts deleted file mode 100644 index 51f96dd..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card-full/benchmark-card-full.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { Component, inject, signal, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { FormsModule } from '@angular/forms'; -import { DatePipe } from '@angular/common'; -import { Subscription } from 'rxjs'; -import { StudentService } from '../../../shared/services/student.service'; -import { BenchmarkDto } from '../../../shared/classes/benchmark.dto'; - -@Component({ - selector: 'app-benchmark-card-full', - imports: [FormsModule, DatePipe], - templateUrl: './benchmark-card-full.html', - styleUrl: './benchmark-card-full.scss', -}) -export class BenchmarkCardFull implements OnDestroy { - - // ************************** Constructor ************************** - - constructor() { - this.paramSub = this.route.paramMap.subscribe(params => { - this.studentId = params.get('studentId')!; - this.goalId = params.get('goalId')!; - this.benchmarkId = params.get('benchmarkId') ?? null; - this.loadBenchmark(); - }); - } - - // ************************** Declarations ************************* - - private readonly studentService = inject(StudentService); - private readonly route = inject(ActivatedRoute); - private readonly router = inject(Router); - private readonly paramSub: Subscription; - - private studentId!: string; - private goalId!: string; - private benchmarkId: string | null = null; - - protected readonly loaded = signal(false); - protected readonly isNew = signal(false); - protected readonly errorMessage = signal(null); - protected readonly successMessage = signal(null); - protected readonly saving = signal(false); - - // Form fields - protected benchmarkText = ''; - protected shortName = ''; - private savedBenchmarkText = ''; - private savedShortName = ''; - - // Read-only metadata - protected goalCategory = ''; - protected createdByName = ''; - protected createdAt: Date | null = null; - protected updatedAt: Date | null = null; - - // ************************** Properties *************************** - - // ***************************************************************** - // Returns true if the benchmark text has unsaved changes. - // ***************************************************************** - hasChanges(): boolean { - return this.benchmarkText !== this.savedBenchmarkText - || this.shortName !== this.savedShortName; - } - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Saves changes or creates a new benchmark. - // ***************************************************************** - async onSave() { - this.saving.set(true); - this.errorMessage.set(null); - this.successMessage.set(null); - - if (this.isNew()) { - const result = await this.studentService.createBenchmark(this.studentId, { - goalId: this.goalId, - benchmark: this.benchmarkText, - shortName: this.shortName || undefined, - }); - this.saving.set(false); - if (result.success) { - this.successMessage.set('Benchmark created.'); - this.savedBenchmarkText = this.benchmarkText; - this.savedShortName = this.shortName; - this.studentService.notifyDataChanged(); - if (result.payload?.benchmarkId) { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'benchmarks', result.payload.benchmarkId]); - } - } else { - this.errorMessage.set(result.message); - } - } else { - const result = await this.studentService.updateBenchmark(this.studentId, this.benchmarkId!, this.benchmarkText, this.shortName || undefined); - this.saving.set(false); - if (result.success) { - this.savedBenchmarkText = this.benchmarkText; - this.savedShortName = this.shortName; - this.successMessage.set('Changes saved.'); - } else { - this.errorMessage.set(result.message); - } - } - } - - // ***************************************************************** - // Reverts the benchmark text to the last-saved value. - // ***************************************************************** - onCancel() { - this.benchmarkText = this.savedBenchmarkText; - this.shortName = this.savedShortName; - this.errorMessage.set(null); - this.successMessage.set(null); - } - - onBack() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId]); - } - - ngOnDestroy() { - this.paramSub.unsubscribe(); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Loads existing benchmark data or sets up new-benchmark state. - // ***************************************************************** - private loadBenchmark() { - if (!this.benchmarkId) { - this.isNew.set(true); - this.benchmarkText = ''; - this.shortName = ''; - this.savedBenchmarkText = ''; - this.savedShortName = ''; - this.loadGoalCategory(); - this.loaded.set(true); - return; - } - - this.isNew.set(false); - this.studentService.getBenchmarksForStudent(this.studentId).then(result => { - if (!result.success || !result.payload) { - this.errorMessage.set(result.message); - return; - } - - const bm = result.payload.benchmarks.find(b => b.benchmarkId === this.benchmarkId); - if (!bm) { - this.errorMessage.set('Benchmark not found.'); - return; - } - - this.benchmarkText = bm.benchmark; - this.shortName = bm.shortName ?? ''; - this.savedBenchmarkText = bm.benchmark; - this.savedShortName = bm.shortName ?? ''; - this.goalCategory = bm.goalCategory; - this.createdByName = bm.createdByName; - this.createdAt = bm.createdAt; - this.updatedAt = bm.updatedAt; - this.loaded.set(true); - }); - } - - // ***************************************************************** - // Loads the goal category for a new benchmark. - // ***************************************************************** - 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.goalCategory = goal?.category ?? ''; - } - }); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.html b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.html index 3ed6bc8..8838b30 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.html @@ -1,11 +1,13 @@ - +
- - + +
- - + +
@if (errorMessage()) { @@ -14,8 +16,8 @@
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.ts b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.ts index cb924d6..83416e3 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.ts @@ -14,7 +14,10 @@ export class EditBenchmarkModal { private readonly studentService = inject(StudentService); readonly studentId = input.required(); - readonly benchmark = input.required(); + readonly goalId = input.required(); + + /** null for new benchmark, populated for edit */ + readonly benchmark = input(null); readonly saved = output(); readonly closed = output(); @@ -24,31 +27,60 @@ export class EditBenchmarkModal { protected shortName = ''; protected benchmarkText = ''; + protected get isEditMode(): boolean { + return !!this.benchmark(); + } + + protected get modalTitle(): string { + return this.isEditMode ? 'Edit Benchmark' : 'Add Benchmark'; + } + + protected get submitLabel(): string { + return this.isEditMode ? 'Save' : 'Add Benchmark'; + } + ngOnInit() { const b = this.benchmark(); - this.shortName = b.shortName ?? ''; - this.benchmarkText = b.benchmark; + if (b) { + this.shortName = b.shortName ?? ''; + this.benchmarkText = b.benchmark; + } } async onSave() { - if (!this.shortName.trim()) return; + if (!this.benchmarkText.trim()) return; this.saving.set(true); this.errorMessage.set(null); - const result = await this.studentService.updateBenchmark( - this.studentId(), - this.benchmark().benchmarkId, - this.benchmarkText, - this.shortName, - ); + if (this.isEditMode) { + const result = await this.studentService.updateBenchmark( + this.studentId(), + this.benchmark()!.benchmarkId, + this.benchmarkText, + this.shortName || undefined, + ); + this.saving.set(false); - this.saving.set(false); - - if (result.success) { - this.studentService.notifyDataChanged(); - this.saved.emit(); + if (result.success) { + this.studentService.notifyDataChanged(); + this.saved.emit(); + } else { + this.errorMessage.set(result.message); + } } else { - this.errorMessage.set(result.message); + const result = await this.studentService.createBenchmark(this.studentId(), { + goalId: this.goalId(), + benchmark: this.benchmarkText, + shortName: this.shortName || undefined, + }); + this.saving.set(false); + + if (result.success) { + this.studentService.notifyDataChanged(); + this.saved.emit(); + } else { + this.errorMessage.set(result.message); + } } } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html index 0915d49..4eeb9d1 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html @@ -12,7 +12,8 @@ (closed)="showGoalModal.set(null)" /> } @if (showEditBenchmarkModal()) { - } @if (showEditEventModal()) { diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts index fa886e8..d3037ce 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts @@ -73,7 +73,7 @@ export class Workspace { // Modal states protected readonly showGoalModal = signal(null); - protected readonly showEditBenchmarkModal = signal(null); + protected readonly showEditBenchmarkModal = signal(null); protected readonly showEditEventModal = signal(null); // ************************** Properties *************************** @@ -148,8 +148,7 @@ export class Workspace { } onAddBenchmark() { - // Navigate to the new benchmark route (still uses the old page for creation) - this.router.navigate(['/students', this.studentId(), 'goals', this.selectedGoal()!.goalId, 'benchmarks', 'new']); + this.showEditBenchmarkModal.set('new'); } onNewEvent() { diff --git a/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts b/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts index 8f6197e..0e97869 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/desktop.routes.ts @@ -3,8 +3,6 @@ import { Home } from './pages/home/home'; import { Workspace } from './components/workspace/workspace'; import { Reports } from './components/reports/reports'; import { StudentProgressReport } from './components/student-progress-report/student-progress-report'; -import { BenchmarkCardFull } from './components/benchmark-card-full/benchmark-card-full'; - export default [ { path: '', @@ -14,8 +12,6 @@ export default [ { path: 'students', component: Workspace }, { path: 'students/:studentId', component: Workspace }, { path: 'students/:studentId/goals/:goalId', component: Workspace }, - // Benchmark creation still uses the dedicated page (no create-benchmark modal yet) - { path: 'students/:studentId/goals/:goalId/benchmarks/new', component: BenchmarkCardFull }, { path: 'reports', component: Reports }, { path: 'reports/student-progress', component: StudentProgressReport }, ],