From 4b435ccdb4d4a5ea8e4bd756784efbb352fc1b1c Mon Sep 17 00:00:00 2001 From: Oliver Pelly Date: Wed, 8 Apr 2026 14:02:33 -0700 Subject: [PATCH] dead code removal --- .../benchmark-card-full.ts | 7 - .../benchmark-card/benchmark-card.html | 12 - .../benchmark-card/benchmark-card.scss | 51 ---- .../benchmark-card/benchmark-card.spec.ts | 23 -- .../benchmark-card/benchmark-card.ts | 26 -- .../benchmark-list/benchmark-list.html | 29 --- .../benchmark-list/benchmark-list.scss | 72 ------ .../benchmark-list/benchmark-list.spec.ts | 23 -- .../benchmark-list/benchmark-list.ts | 94 -------- .../goal-card-full/goal-card-full.html | 81 ------- .../goal-card-full/goal-card-full.scss | 208 ---------------- .../goal-card-full/goal-card-full.spec.ts | 23 -- .../goal-card-full/goal-card-full.ts | 196 --------------- .../components/goal-card/goal-card.html | 28 --- .../components/goal-card/goal-card.scss | 105 -------- .../desktop/components/goal-card/goal-card.ts | 55 ----- .../components/goal-list/goal-list.html | 40 --- .../components/goal-list/goal-list.scss | 96 -------- .../desktop/components/goal-list/goal-list.ts | 91 ------- .../progress-edit/progress-edit.html | 57 ----- .../progress-edit/progress-edit.scss | 163 ------------- .../progress-edit/progress-edit.spec.ts | 23 -- .../components/progress-edit/progress-edit.ts | 228 ------------------ .../progress-item/progress-item.html | 9 - .../progress-item/progress-item.scss | 63 ----- .../progress-item/progress-item.spec.ts | 23 -- .../components/progress-item/progress-item.ts | 33 --- .../progress-list/progress-list.html | 40 --- .../progress-list/progress-list.scss | 139 ----------- .../progress-list/progress-list.spec.ts | 23 -- .../components/progress-list/progress-list.ts | 131 ---------- .../sidebar-tree-node/sidebar-tree-node.html | 20 -- .../sidebar-tree-node/sidebar-tree-node.scss | 60 ----- .../sidebar-tree-node/sidebar-tree-node.ts | 67 ----- .../student-card-full/student-card-full.html | 45 ---- .../student-card-full/student-card-full.scss | 180 -------------- .../student-card-full.spec.ts | 23 -- .../student-card-full/student-card-full.ts | 160 ------------ .../student-card-list/student-card-list.html | 100 -------- .../student-card-list/student-card-list.scss | 210 ---------------- .../student-card-list/student-card-list.ts | 103 -------- .../components/student-card/student-card.html | 20 -- .../components/student-card/student-card.scss | 49 ---- .../components/student-card/student-card.ts | 27 --- .../src/app/shared/classes/sidebar-node.ts | 8 - 45 files changed, 3264 deletions(-) delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.ts delete mode 100644 ui/winstudentgoaltracker/src/app/shared/classes/sidebar-node.ts 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 index e2c6ce5..51f96dd 100644 --- 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 @@ -95,19 +95,12 @@ export class BenchmarkCardFull implements OnDestroy { this.errorMessage.set(result.message); } } else { - const shortNameChanged = this.shortName !== this.savedShortName; 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.'); - if (shortNameChanged) { - this.studentService.updateSidebarLabel( - ['/students', this.studentId, 'goals', this.goalId, 'benchmarks', this.benchmarkId!], - this.shortName || this.benchmarkText - ); - } } else { this.errorMessage.set(result.message); } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.html b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.html deleted file mode 100644 index 4b9f0aa..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.html +++ /dev/null @@ -1,12 +0,0 @@ -
-
- {{ benchmark().goalCategory }} - @if (benchmark().updatedAt) { - Updated: {{ benchmark().updatedAt | date:'M/d/yy' }} - } @else { - {{ benchmark().createdAt | date:'M/d/yy' }} - } -
- -

{{ benchmark().benchmark }}

-
\ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.scss b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.scss deleted file mode 100644 index e7744af..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.scss +++ /dev/null @@ -1,51 +0,0 @@ -:host { - display: block; - width: 300px; -} - -.card { - background: #fff; - border-radius: 8px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); - padding: 1.25rem 1.5rem; - display: flex; - flex-direction: column; - gap: 0.625rem; - min-width: 0; -} - -.card-header { - display: flex; - align-items: center; - justify-content: space-between; -} - -.goal-badge { - padding: 0.2rem 0.6rem; - background: #f0fdf4; - color: #16a34a; - border-radius: 999px; - font-size: 0.75rem; - font-weight: 600; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 60%; -} - -.date { - font-size: 0.8125rem; - color: #888; -} - -.benchmark-text { - margin: 0; - font-size: 0.875rem; - color: #333; - line-height: 1.5; - display: -webkit-box; - -webkit-line-clamp: 3; - line-clamp: 3; - -webkit-box-orient: vertical; - overflow: hidden; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.spec.ts deleted file mode 100644 index 1881594..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BenchmarkCard } from './benchmark-card'; - -describe('BenchmarkCard', () => { - let component: BenchmarkCard; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BenchmarkCard] - }) - .compileComponents(); - - fixture = TestBed.createComponent(BenchmarkCard); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.ts deleted file mode 100644 index 4f924a3..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-card/benchmark-card.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Component, input } from '@angular/core'; -import { DatePipe } from '@angular/common'; -import { BenchmarkDto } from '../../../shared/classes/benchmark.dto'; - -@Component({ - selector: 'app-benchmark-card', - imports: [DatePipe], - templateUrl: './benchmark-card.html', - styleUrl: './benchmark-card.scss', -}) -export class BenchmarkCard { - - // ************************** Constructor ************************** - - // ************************** Declarations ************************* - - readonly benchmark = input.required(); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ********************** Support Procedures *********************** -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.html b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.html deleted file mode 100644 index f82402b..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.html +++ /dev/null @@ -1,29 +0,0 @@ -
- - - -
- -@if (goalCategory()) { -

Benchmarks for {{ goalCategory() }}

-} @else { -

Benchmarks

-} - -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (benchmarks().length === 0 && !errorMessage()) { -
-

Benchmarks are milestones on the way to achieving a goal. They are optional in this system.

-

No benchmarks yet.

-

Click + Add a Benchmark in the upper right to get started.

-
-} @else { -
- @for (bm of benchmarks(); track bm.benchmarkId) { - - } -
-} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.scss b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.scss deleted file mode 100644 index 1a518dd..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.scss +++ /dev/null @@ -1,72 +0,0 @@ -: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; -} - -.section-header { - font-size: 1.125rem; - font-weight: 600; - color: #333; - margin: 0 0 0.5rem; -} - -.spacer { - flex: 1; -} - -.error { - font-size: 0.875rem; - color: #dc2626; - margin: 0 0 1rem; -} - -.empty-state { - text-align: center; - padding: 3rem 1.5rem; - color: #555; - font-size: 0.9375rem; - background: #fff; - border-radius: 8px; - border: 1px solid #ddd; -} - -.card-grid { - display: flex; - flex-wrap: wrap; - gap: 1rem; - overflow-y: auto; - flex: 1; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.spec.ts deleted file mode 100644 index 964283c..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { BenchmarkList } from './benchmark-list'; - -describe('BenchmarkList', () => { - let component: BenchmarkList; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [BenchmarkList] - }) - .compileComponents(); - - fixture = TestBed.createComponent(BenchmarkList); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.ts b/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.ts deleted file mode 100644 index e8cccf6..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/benchmark-list/benchmark-list.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Component, inject, signal } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { BenchmarkDto } from '../../../shared/classes/benchmark.dto'; -import { StudentService } from '../../../shared/services/student.service'; -import { BenchmarkCard } from '../benchmark-card/benchmark-card'; - -@Component({ - selector: 'app-benchmark-list', - imports: [BenchmarkCard], - templateUrl: './benchmark-list.html', - styleUrl: './benchmark-list.scss', -}) -export class BenchmarkList { - - // ************************** Constructor ************************** - - constructor() { - this.studentId = this.route.snapshot.paramMap.get('studentId')!; - this.goalId = this.route.snapshot.paramMap.get('goalId') || ''; - this.loadBenchmarks(); - this.loadGoalCategory(); - } - - // ************************** Declarations ************************* - - private readonly studentService = inject(StudentService); - private readonly route = inject(ActivatedRoute); - private readonly router = inject(Router); - - protected readonly studentId: string; - protected readonly goalId: string; - protected readonly goalCategory = signal(null); - protected readonly benchmarks = signal([]); - protected readonly errorMessage = signal(null); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - onAddBenchmark() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'benchmarks', 'new']); - } - - onBack() { - 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. 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 { - 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); - }); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.html b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.html deleted file mode 100644 index 7f450c1..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.html +++ /dev/null @@ -1,81 +0,0 @@ -
- - Goal Detail - -
- -@if (loaded()) { -
-
- Goal: {{ category }} - @if (targetCompletionDate) { - Target: {{ targetCompletionDate | date:'mediumDate' }} - } @else { - No target date - } -
- -
-
- - -
- -
- - -
-
- - -
- -
- - -
- - @if (closeDate !== null) { -
-
- - -
-
- - -
- @if (achieved === false) { -
- - -
- } -
- } - -
- - -
-
- - -
-} - -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (successMessage()) { -

{{ successMessage() }}

-} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.scss b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.scss deleted file mode 100644 index 5bb3225..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.scss +++ /dev/null @@ -1,208 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - -.toolbar { - display: flex; - align-items: center; - position: relative; - 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; -} - -.toolbar-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.back-btn { - margin-left: 0.5rem; -} - -.toolbar-title { - position: absolute; - left: 50%; - transform: translateX(-50%); - font-weight: 600; - font-size: 1.25rem; - color: #333; -} - -.spacer { - flex: 1; -} - -.error { - font-size: 0.875rem; - color: #dc2626; - margin: 1rem 0 0; -} - -.success { - font-size: 0.875rem; - color: #16a34a; - margin: 1rem 0 0; -} - -.detail-card { - background: #fff; - border: 1px solid #ddd; - border-radius: 8px; - max-width: 600px; - overflow: hidden; -} - -.card-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.625rem 1.25rem; - background: #f8f9fa; - border-bottom: 1px solid #ddd; -} - -.card-title { - font-size: 0.875rem; - font-weight: 600; - color: #333; -} - -.card-body { - padding: 1.5rem; -} - -.card-footer { - display: flex; - align-items: center; - gap: 1.5rem; - padding: 0.5rem 1.5rem; - border-top: 1px solid #eee; -} - -.footer-btn { - padding: 0.375rem 0.75rem; - background: transparent; - border: 1px solid; - border-radius: 6px; - font-size: 0.8125rem; - font-weight: 500; - cursor: pointer; -} - -.btn-green { - color: #16a34a; - border-color: #16a34a; -} - -.btn-green:hover { - background: #f0fdf4; -} - -.btn-blue { - color: #2563eb; - border-color: #2563eb; - margin-left: auto; -} - -.btn-blue:hover { - background: #eff6ff; -} - -.field { - display: flex; - flex-direction: column; - margin-bottom: 1rem; -} - -.field-label { - font-size: 0.75rem; - font-weight: 600; - color: #666; - text-transform: uppercase; - letter-spacing: 0.025em; - margin-bottom: 0.25rem; -} - -.field-input { - padding: 0.375rem 0.5rem; - border: 1px solid #ccc; - border-radius: 6px; - font-family: inherit; - font-size: 0.9375rem; - outline: none; -} - -.field-input:focus { - border-color: #4f46e5; -} - -.field-textarea { - font-family: inherit; - resize: vertical; - min-height: 5rem; -} - -.actions { - display: flex; - justify-content: flex-end; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.actions .toolbar-btn { - min-width: 6rem; -} - -.save-btn { - background: #4f46e5; - color: #fff; - border-color: #4f46e5; -} - -.save-btn:hover { - background: #4338ca; -} - -.close-section { - border-top: 1px solid #eee; - padding-top: 1rem; - margin-top: 0.5rem; -} - -.field-row { - flex-direction: row; - align-items: center; - gap: 0.5rem; -} - -.field-row input[type="checkbox"] { - width: 1.125rem; - height: 1.125rem; -} - -.required { - color: #dc2626; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.spec.ts deleted file mode 100644 index d69bbd6..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { GoalCardFull } from './goal-card-full'; - -describe('GoalCardFull', () => { - let component: GoalCardFull; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [GoalCardFull] - }) - .compileComponents(); - - fixture = TestBed.createComponent(GoalCardFull); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.ts b/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.ts deleted file mode 100644 index 9783624..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card-full/goal-card-full.ts +++ /dev/null @@ -1,196 +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 { StudentGoalItem } from '../../../shared/classes/student-goal'; - -@Component({ - selector: 'app-goal-card-full', - imports: [FormsModule, DatePipe], - templateUrl: './goal-card-full.html', - styleUrl: './goal-card-full.scss', -}) -export class GoalCardFull implements OnDestroy { - - // ************************** Constructor ************************** - - constructor() { - this.paramSub = this.route.paramMap.subscribe(params => { - this.studentId = params.get('studentId')!; - this.goalId = params.get('goalId')!; - this.loadGoal(); - }); - } - - // ************************** 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; - - protected readonly loaded = signal(false); - protected readonly errorMessage = signal(null); - protected readonly successMessage = signal(null); - protected readonly saving = signal(false); - - // Form fields - protected description = ''; - protected category = ''; - protected baseline = ''; - protected targetCompletionDate: string | null = null; - protected closeDate: string | null = null; - protected achieved: boolean | null = null; - protected closeNotes: string | null = null; - - // Read-only metadata - protected progressEventCount = 0; - protected benchmarkCount = 0; - - // Snapshot - private savedDescription = ''; - private savedCategory = ''; - private savedBaseline = ''; - private savedTargetCompletionDate: string | null = null; - private savedCloseDate: string | null = null; - private savedAchieved: boolean | null = null; - private savedCloseNotes: string | null = null; - - // ************************** Properties *************************** - - // ***************************************************************** - // Returns true if form values differ from the saved snapshot. - // ***************************************************************** - hasChanges(): boolean { - return this.description !== this.savedDescription - || this.category !== this.savedCategory - || this.baseline !== this.savedBaseline - || this.targetCompletionDate !== this.savedTargetCompletionDate - || this.closeDate !== this.savedCloseDate - || this.achieved !== this.savedAchieved - || this.closeNotes !== this.savedCloseNotes; - } - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Saves changes to the goal via the API. - // ***************************************************************** - async onSave() { - this.saving.set(true); - this.errorMessage.set(null); - this.successMessage.set(null); - - const result = await this.studentService.updateGoal(this.studentId, this.goalId, { - description: this.description, - category: this.category, - baseline: this.baseline, - targetCompletionDate: this.targetCompletionDate, - closeDate: this.closeDate, - achieved: this.achieved, - closeNotes: this.closeNotes, - }); - - this.saving.set(false); - - if (result.success) { - this.savedDescription = this.description; - this.savedCategory = this.category; - this.savedBaseline = this.baseline; - this.savedTargetCompletionDate = this.targetCompletionDate; - this.savedCloseDate = this.closeDate; - this.savedAchieved = this.achieved; - this.savedCloseNotes = this.closeNotes; - this.successMessage.set('Changes saved.'); - this.studentService.notifyDataChanged(); - } else { - this.errorMessage.set(result.message); - } - } - - // ***************************************************************** - // Reverts form fields to the last-saved snapshot. - // ***************************************************************** - onCancel() { - this.description = this.savedDescription; - this.category = this.savedCategory; - this.baseline = this.savedBaseline; - this.targetCompletionDate = this.savedTargetCompletionDate; - this.closeDate = this.savedCloseDate; - this.achieved = this.savedAchieved; - this.closeNotes = this.savedCloseNotes; - this.errorMessage.set(null); - this.successMessage.set(null); - } - - onBack() { - this.router.navigate(['/students', this.studentId, 'goals']); - } - - onProgressEvents() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'progress']); - } - - onBenchmarks() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'benchmarks']); - } - - ngOnDestroy() { - this.paramSub.unsubscribe(); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Normalizes an API date string to YYYY-MM-DD for . - // ***************************************************************** - private toDateInput(value: string | null): string | null { - if (!value) return null; - return value.substring(0, 10); - } - - // ***************************************************************** - // Loads the goal by finding it in the student's goal list. - // ***************************************************************** - private loadGoal() { - this.loaded.set(false); - this.studentService.getGoalsForStudent(this.studentId).then(result => { - if (!result.success || !result.payload) { - this.errorMessage.set(result.message); - return; - } - - const goal = result.payload.goals.find(g => g.goalId === this.goalId); - if (!goal) { - this.errorMessage.set('Goal not found.'); - return; - } - - this.description = goal.description; - this.category = goal.category; - this.baseline = goal.baseline; - this.targetCompletionDate = this.toDateInput(goal.targetCompletionDate); - this.closeDate = this.toDateInput(goal.closeDate); - this.achieved = goal.achieved; - this.closeNotes = goal.closeNotes; - this.progressEventCount = goal.progressEventCount; - this.benchmarkCount = goal.benchmarkCount; - - this.savedDescription = goal.description; - this.savedCategory = goal.category; - this.savedBaseline = goal.baseline; - this.savedTargetCompletionDate = this.toDateInput(goal.targetCompletionDate); - this.savedCloseDate = this.toDateInput(goal.closeDate); - this.savedAchieved = goal.achieved; - this.savedCloseNotes = goal.closeNotes; - this.loaded.set(true); - }); - } -} 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 deleted file mode 100644 index a9961a3..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.html +++ /dev/null @@ -1,28 +0,0 @@ -
-
- Goal: {{ goal().category }} - @if (goal().closeDate !== null) { - - {{ goal().achieved === true ? 'Closed ✓' : 'Closed ✗' }} - - } - @if (goal().targetCompletionDate) { - Target: {{ goal().targetCompletionDate | date:'mediumDate' }} - } @else { - No target date - } -
- -
- @if (goalNumber() > 0) { -

Goal {{ goalNumber() }}: {{ goal().description }}

- } @else { -

{{ goal().description }}

- } -
- - -
\ 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 deleted file mode 100644 index 4d8e6bd..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.scss +++ /dev/null @@ -1,105 +0,0 @@ -:host { - display: block; - width: 450px; -} - -.card { - background: #fff; - border-radius: 8px; - border: 1px solid #ddd; - display: flex; - flex-direction: column; - cursor: pointer; - min-width: 0; - overflow: hidden; -} - -.card-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 0.625rem 1.25rem; - background: #f8f9fa; - border-bottom: 1px solid #ddd; -} - -.card-title { - font-size: 0.875rem; - font-weight: 600; - color: #333; -} - -.event-count { - font-size: 0.875rem; - font-weight: 600; - color: #333; -} - -.card-body { - padding: 1.25rem 1.5rem; -} - -.description { - margin: 0; - font-size: 0.875rem; - color: #555; - line-height: 1.5; - display: -webkit-box; - line-clamp: 2; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; -} - -.card-footer { - display: flex; - align-items: center; - gap: 1.5rem; - padding: 0.5rem 1.5rem; - border-top: 1px solid #eee; -} - -.footer-btn { - padding: 0.375rem 0.75rem; - background: transparent; - border: 1px solid; - border-radius: 6px; - font-size: 0.8125rem; - font-weight: 500; - cursor: pointer; -} - -.btn-green { - color: #16a34a; - border-color: #16a34a; -} - -.btn-green:hover { - background: #f0fdf4; -} - -.btn-blue { - color: #2563eb; - border-color: #2563eb; - margin-left: auto; -} - -.btn-blue:hover { - background: #eff6ff; -} - -.closed-badge { - font-size: 0.75rem; - font-weight: 600; - padding: 0.125rem 0.5rem; - border-radius: 4px; - background: #fef2f2; - color: #dc2626; - border: 1px solid #fecaca; -} - -.closed-badge.achieved { - background: #f0fdf4; - color: #16a34a; - border-color: #bbf7d0; -} \ 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 deleted file mode 100644 index eb8d2d3..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-card/goal-card.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { Component, inject, input } from '@angular/core'; -import { DatePipe } from '@angular/common'; -import { ActivatedRoute, Router } from '@angular/router'; -import { StudentGoalItem } from '../../../shared/classes/student-goal'; - -@Component({ - selector: 'app-goal-card', - imports: [DatePipe], - templateUrl: './goal-card.html', - styleUrl: './goal-card.scss', -}) -export class GoalCard { - - // ************************** Constructor ************************** - - // ************************** Declarations ************************* - - private readonly router = inject(Router); - private readonly route = inject(ActivatedRoute); - - readonly goal = input.required(); - readonly goalNumber = input(0); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Navigates to the goal detail page. - // ***************************************************************** - onCardClick() { - const studentId = this.route.snapshot.paramMap.get('studentId')!; - this.router.navigate(['/students', studentId, 'goals', this.goal().goalId]); - } - - // ***************************************************************** - // Navigates to the benchmarks page for this goal. - // ***************************************************************** - onBenchmarksClick() { - const studentId = this.route.snapshot.paramMap.get('studentId')!; - this.router.navigate(['/students', studentId, 'goals', this.goal().goalId, 'benchmarks']); - } - - // ***************************************************************** - // Navigates to the progress events page for this goal. - // ***************************************************************** - onProgressEventsClick() { - 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 deleted file mode 100644 index 0c64896..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.html +++ /dev/null @@ -1,40 +0,0 @@ -
- - Goals - - -
- - - - -@if (studentIdentifier()) { -

Student: {{ studentIdentifier() }}

-} - -@if (showAddModal()) { - -} - -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (goals().length === 0 && !errorMessage()) { -
-

No goals yet.

-

Click + Add a Goal in the upper right to get started.

-
-} @else { -
- @for (goal of goals(); track goal.goalId; let i = $index) { - - } -
-} - -
- - -
\ 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 deleted file mode 100644 index ed009bf..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.scss +++ /dev/null @@ -1,96 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - - -.toolbar { - display: flex; - align-items: center; - position: relative; - 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; - cursor: pointer; -} - -.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; -} - -.toolbar-title { - position: absolute; - left: 50%; - transform: translateX(-50%); - font-weight: 600; - font-size: 1.25rem; - color: #333; -} - -.section-header { - font-size: 1.125rem; - font-weight: 600; - color: #333; - margin: 0 0 1.25rem; -} - -.spacer { - flex: 1; -} - -.error { - font-size: 0.875rem; - color: #dc2626; - margin: 0 0 1rem; -} - -.empty-state { - text-align: center; - padding: 3rem 1.5rem; - color: #555; - font-size: 0.9375rem; - background: #fff; - border-radius: 8px; - border: 1px solid #ddd; -} - -.card-grid { - display: flex; - flex-wrap: wrap; - align-content: start; - gap: 1rem; - overflow-y: auto; - flex: 1; -} - -.goal-footer { - display: flex; - align-items: center; - padding: 0 1rem; - height: 48px; - background: #fff; - border-top: 1px solid #ddd; - flex-shrink: 0; - margin-top: auto; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.ts b/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.ts deleted file mode 100644 index ef7989b..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/goal-list/goal-list.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, inject, signal, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { Subscription } from 'rxjs'; -import { StudentGoalItem } from '../../../shared/classes/student-goal'; -import { StudentService } from '../../../shared/services/student.service'; -import { GoalCard } from '../goal-card/goal-card'; -import { AddGoalModal } from '../add-goal-modal/add-goal-modal'; - -@Component({ - selector: 'app-goal-list', - imports: [GoalCard, AddGoalModal], - templateUrl: './goal-list.html', - styleUrl: './goal-list.scss', -}) -export class GoalList implements OnDestroy { - - // ************************** Constructor ************************** - - constructor() { - this.paramSub = this.route.paramMap.subscribe(params => { - this.studentId = params.get('studentId')!; - this.loadGoals(); - }); - } - - // ************************** Declarations ************************* - - private readonly studentService = inject(StudentService); - private readonly route = inject(ActivatedRoute); - private readonly router = inject(Router); - private readonly paramSub: Subscription; - - protected studentId!: string; - protected readonly studentIdentifier = signal(null); - protected readonly nextIepDate = signal(null); - protected readonly goals = signal([]); - protected readonly showAddModal = signal(false); - protected readonly errorMessage = signal(null); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - onAddGoal() { - this.showAddModal.set(true); - } - - onGoalCreated(goal: StudentGoalItem) { - this.goals.update(list => [...list, goal]); - this.showAddModal.set(false); - this.studentService.notifyDataChanged(); - this.router.navigate(['/students', this.studentId, 'goals', goal.goalId]); - } - - onModalCancelled() { - this.showAddModal.set(false); - } - - onBack() { - this.router.navigate(['/students', this.studentId]); - } - - ngOnDestroy() { - this.paramSub.unsubscribe(); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Loads goals for the student from the service. - // ***************************************************************** - private loadGoals() { - this.studentService.getStudentById(this.studentId).then(studentResult => { - if (studentResult.success && studentResult.payload) { - const iep = studentResult.payload.nextIepDate; - this.nextIepDate.set(iep ? String(iep).substring(0, 10) : null); - } - }); - - this.studentService.getGoalsForStudent(this.studentId).then(data => { - if (!data.success) { - this.errorMessage.set(data.message); - } else { - this.studentIdentifier.set(data.payload?.studentIdentifier ?? null); - this.goals.set(data.payload?.goals ?? []); - } - }); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.html b/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.html deleted file mode 100644 index 3dad196..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.html +++ /dev/null @@ -1,57 +0,0 @@ -
- - {{ isNew() ? 'New Progress Event' : 'Edit Progress Event' }} - -
- -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (loaded()) { -
-
- Goal: {{ goalCategory }} -
-
- - -
- - @if (benchmarkItems().length > 0) { -
- Associated Benchmarks -
- @for (bm of benchmarkItems(); track bm.benchmarkId) { - - } -
-
- } - - @if (!isNew()) { - - } -
- - -
-
-} - -@if (successMessage()) { -

{{ successMessage() }}

-} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.scss b/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.scss deleted file mode 100644 index a8a391d..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.scss +++ /dev/null @@ -1,163 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - -.toolbar { - display: flex; - align-items: center; - position: relative; - 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; -} - -.toolbar-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.back-btn { - margin-left: 0.5rem; -} - -.toolbar-title { - position: absolute; - left: 50%; - transform: translateX(-50%); - font-weight: 600; - font-size: 1.25rem; - color: #333; -} - -.spacer { - flex: 1; -} - -.error { - font-size: 0.875rem; - color: #dc2626; - margin: 0 0 1rem; -} - -.success { - font-size: 0.875rem; - color: #16a34a; - margin: 0 0 1rem; -} - -.detail-card { - background: #fff; - border: 1px solid #ddd; - border-radius: 8px; - padding: 1.5rem; - max-width: 600px; -} - -.field { - display: flex; - flex-direction: column; - margin-bottom: 1rem; -} - -.field-label { - font-size: 0.75rem; - font-weight: 600; - color: #666; - text-transform: uppercase; - letter-spacing: 0.025em; - margin-bottom: 0.25rem; -} - -.field-input { - padding: 0.375rem 0.5rem; - border: 1px solid #ccc; - border-radius: 6px; - font-size: 0.9375rem; - outline: none; -} - -.field-input:focus { - border-color: #4f46e5; -} - -.field-textarea { - font-family: inherit; - resize: vertical; - min-height: 5rem; -} - -.benchmark-checks { - display: flex; - flex-direction: column; - gap: 0.5rem; - margin-top: 0.25rem; -} - -.check-item { - display: flex; - align-items: center; - gap: 0.5rem; - font-size: 0.9375rem; - color: #333; - cursor: pointer; -} - -.check-item input[type="checkbox"] { - width: 1rem; - height: 1rem; - accent-color: #4f46e5; -} - -.metadata { - display: flex; - gap: 1.5rem; - margin-bottom: 1rem; -} - -.meta-item { - font-size: 0.8125rem; - color: #888; -} - -.actions { - display: flex; - justify-content: flex-end; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.actions .toolbar-btn { - min-width: 6rem; -} - -.save-btn { - background: #4f46e5; - color: #fff; - border-color: #4f46e5; -} - -.save-btn:hover { - background: #4338ca; -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.spec.ts deleted file mode 100644 index 57b2089..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ProgressEdit } from './progress-edit'; - -describe('ProgressEdit', () => { - let component: ProgressEdit; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ProgressEdit] - }) - .compileComponents(); - - fixture = TestBed.createComponent(ProgressEdit); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.ts b/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.ts deleted file mode 100644 index 3ad8691..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-edit/progress-edit.ts +++ /dev/null @@ -1,228 +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'; - -interface BenchmarkCheckItem { - benchmarkId: string; - label: string; - checked: boolean; -} - -@Component({ - selector: 'app-progress-edit', - imports: [FormsModule, DatePipe], - templateUrl: './progress-edit.html', - styleUrl: './progress-edit.scss', -}) -export class ProgressEdit implements OnDestroy { - - // ************************** Constructor ************************** - - constructor() { - this.paramSub = this.route.paramMap.subscribe(params => { - this.studentId = params.get('studentId')!; - this.goalId = params.get('goalId')!; - this.progressEventId = params.get('progressEventId') ?? null; - this.loadData(); - }); - } - - // ************************** 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 progressEventId: 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 content = ''; - private savedContent = ''; - - // Benchmark checkboxes - protected benchmarkItems = signal([]); - private savedBenchmarkSelections: Set = new Set(); - - // Read-only metadata - protected goalCategory = ''; - protected createdByName = ''; - protected createdAt: Date | null = null; - - // ************************** Properties *************************** - - // ***************************************************************** - // Returns true if the form has unsaved changes. - // ***************************************************************** - hasChanges(): boolean { - if (this.content !== this.savedContent) return true; - const current = new Set(this.benchmarkItems().filter(b => b.checked).map(b => b.benchmarkId)); - if (current.size !== this.savedBenchmarkSelections.size) return true; - for (const id of current) { - if (!this.savedBenchmarkSelections.has(id)) return true; - } - return false; - } - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Saves the progress event (create or update) and any benchmark - // associations based on the checked checkboxes. - // ***************************************************************** - async onSave() { - this.saving.set(true); - this.errorMessage.set(null); - this.successMessage.set(null); - - const checkedIds = this.benchmarkItems() - .filter(b => b.checked) - .map(b => b.benchmarkId); - - if (this.isNew()) { - const result = await this.studentService.addProgressEvent( - this.studentId, this.goalId, this.content.trim(), - checkedIds.length > 0 ? checkedIds : undefined - ); - this.saving.set(false); - if (result.success) { - this.successMessage.set('Progress event created.'); - this.savedContent = this.content; - this.savedBenchmarkSelections = new Set(checkedIds); - this.studentService.notifyDataChanged(); - if (result.payload?.progressEventId) { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'progress', result.payload.progressEventId]); - } - } else { - this.errorMessage.set(result.message); - } - } else { - const result = await this.studentService.updateProgressEvent( - this.studentId, this.progressEventId!, this.content.trim(), checkedIds - ); - this.saving.set(false); - if (result.success) { - this.savedContent = this.content; - this.savedBenchmarkSelections = new Set(checkedIds); - this.successMessage.set('Changes saved.'); - } else { - this.errorMessage.set(result.message); - } - } - } - - // ***************************************************************** - // Reverts the form to the last-saved state. - // ***************************************************************** - onCancel() { - this.content = this.savedContent; - this.benchmarkItems.update(items => - items.map(b => ({ ...b, checked: this.savedBenchmarkSelections.has(b.benchmarkId) })) - ); - this.errorMessage.set(null); - this.successMessage.set(null); - } - - onBack() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'progress']); - } - - // ***************************************************************** - // Toggles a benchmark checkbox. - // ***************************************************************** - onToggleBenchmark(benchmarkId: string) { - this.benchmarkItems.update(items => - items.map(b => b.benchmarkId === benchmarkId ? { ...b, checked: !b.checked } : b) - ); - } - - ngOnDestroy() { - this.paramSub.unsubscribe(); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Loads all data needed for the form: goal category, benchmarks, - // and (for edit mode) the existing event content + associations. - // ***************************************************************** - private async loadData() { - this.loaded.set(false); - - // Load goal category - const goalsResult = await this.studentService.getGoalsForStudent(this.studentId); - if (goalsResult.success && goalsResult.payload) { - const goal = goalsResult.payload.goals.find(g => g.goalId === this.goalId); - this.goalCategory = goal?.category ?? ''; - } - - // Load benchmarks for this goal - const bmResult = await this.studentService.getBenchmarksForStudent(this.studentId); - const goalBenchmarks = (bmResult.success && bmResult.payload) - ? bmResult.payload.benchmarks.filter(b => b.goalId === this.goalId) - : []; - - if (!this.progressEventId) { - // New event mode - this.isNew.set(true); - this.content = ''; - this.savedContent = ''; - this.savedBenchmarkSelections = new Set(); - this.benchmarkItems.set(goalBenchmarks.map(b => ({ - benchmarkId: b.benchmarkId, - label: b.shortName || b.benchmark, - checked: false, - }))); - this.loaded.set(true); - return; - } - - // Edit mode — load existing event - this.isNew.set(false); - const eventsResult = await this.studentService.getProgressEventsForGoal(this.goalId); - if (!eventsResult.success || !eventsResult.payload) { - this.errorMessage.set(eventsResult.message); - this.loaded.set(true); - return; - } - - const event = eventsResult.payload.find(e => e.progressEventId === this.progressEventId); - if (!event) { - this.errorMessage.set('Progress event not found.'); - this.loaded.set(true); - return; - } - - this.content = event.content; - this.savedContent = event.content; - this.createdByName = event.createdByName; - this.createdAt = event.createdAt; - - // Load existing benchmark associations - const assocResult = await this.studentService.getProgressEventBenchmarks(this.progressEventId!); - const associatedIds = new Set(assocResult.success && assocResult.payload ? assocResult.payload : []); - this.savedBenchmarkSelections = new Set(associatedIds); - - this.benchmarkItems.set(goalBenchmarks.map(b => ({ - benchmarkId: b.benchmarkId, - label: b.shortName || b.benchmark, - checked: associatedIds.has(b.benchmarkId), - }))); - - this.loaded.set(true); - } -} 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 deleted file mode 100644 index 1ae849b..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.html +++ /dev/null @@ -1,9 +0,0 @@ -
-

{{ 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 deleted file mode 100644 index 79e61a6..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.scss +++ /dev/null @@ -1,63 +0,0 @@ -: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; -} - -.edit-btn { - padding: 0.25rem 0.625rem; - background: transparent; - color: #4f46e5; - border: 1px solid #4f46e5; - border-radius: 6px; - font-size: 0.8125rem; - font-weight: 500; - cursor: pointer; -} - -.edit-btn:hover { - background: #eef2ff; -} \ 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 deleted file mode 100644 index 6fe4032..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 5ab52cc..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-item/progress-item.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Component, inject, input } from '@angular/core'; -import { DatePipe } from '@angular/common'; -import { ActivatedRoute, Router } from '@angular/router'; -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 ************************* - - private readonly router = inject(Router); - private readonly route = inject(ActivatedRoute); - readonly event = input.required(); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - onEdit() { - this.router.navigate([this.event().progressEventId], { relativeTo: this.route }); - } - - // ********************** 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 deleted file mode 100644 index d390b0a..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.html +++ /dev/null @@ -1,40 +0,0 @@ -
- - Progress Events - - -
- - - -@if (studentIdentifier() && goalCategory()) { -
-

- Student: {{ studentIdentifier() }}    Goal: {{ goalCategory() }} - @if (isFiltered()) { - (showing {{ filteredEvents().length }} of {{ events().length }}) - } -

- -
-} - -@if (errorMessage()) { -

{{ errorMessage() }}

-} - -@if (filteredEvents().length === 0 && !errorMessage()) { -

No progress events recorded yet. Click + Add Progress Event to get started.

-} @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 deleted file mode 100644 index 7217a4b..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.scss +++ /dev/null @@ -1,139 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - -.hero-image { - max-width: 50%; - max-height: 100px; - object-fit: contain; - margin-bottom: 1rem; - flex-shrink: 0; - align-self: flex-start; -} - -.toolbar { - display: flex; - align-items: center; - position: relative; - 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-title { - position: absolute; - left: 50%; - transform: translateX(-50%); - font-weight: 600; - font-size: 1.25rem; - color: #333; -} - -.spacer { - flex: 1; -} - -.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 deleted file mode 100644 index f6d6a62..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index d2fa4a0..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/progress-list/progress-list.ts +++ /dev/null @@ -1,131 +0,0 @@ -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 { 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.loadGoalCategory(); - - this.searchInput$.pipe(debounceTime(300)).subscribe(term => { - this.searchTerm.set(term); - }); - } - - // ************************** Declarations ************************* - - 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 goalCategory = signal(null); - protected readonly events = signal([]); - protected readonly errorMessage = signal(null); - protected readonly rawSearchText = signal(''); - protected readonly searchTerm = signal(''); - protected readonly showAddModal = signal(false); - - // ************************** 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 ************************* - - onAddProgressEvent() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId, 'progress', 'new']); - } - - // ***************************************************************** - // Navigates back to the parent goal detail. - // ***************************************************************** - onBack() { - this.router.navigate(['/students', this.studentId, 'goals', this.goalId]); - } - - // ***************************************************************** - // 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 API, sorted - // newest-first by createdAt. - // ***************************************************************** - private loadEvents() { - this.studentService.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 category from the student's goal list so the heading - // can display "Progress for ". - // ***************************************************************** - private loadGoalCategory() { - this.studentService.getGoalsForStudent(this.studentId).then(result => { - 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); - }); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.html b/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.html deleted file mode 100644 index b87834e..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.html +++ /dev/null @@ -1,20 +0,0 @@ -@for (node of nodes(); track node.label) { -
- @if (hasToggle(node)) { - {{ node.expanded ? '−' : '+' }} - } @else { - - } - @if (node.routerLink) { - {{ node.label }} - } @else if (hasToggle(node)) { - {{ node.label }} - } @else { - {{ node.label }} - } -
-@if (node.expanded && node.children) { - -} -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.scss b/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.scss deleted file mode 100644 index 4c64f52..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.scss +++ /dev/null @@ -1,60 +0,0 @@ -:host { - display: contents; -} - -.node-row { - display: flex; - align-items: center; - gap: 0.375rem; - padding: 0.5rem 1rem; - min-height: 1.75rem; -} - -.node-row:hover { - background: #f5f5f5; -} - -.node-row:has(.node-label.active) { - background: #e5e5e5; -} - -.toggle-indicator { - display: inline-flex; - align-items: center; - justify-content: center; - width: 1rem; - height: 1rem; - border: 1px solid #aaa; - border-radius: 2px; - font-size: 0.75rem; - line-height: 1; - color: #555; - cursor: pointer; - user-select: none; - flex-shrink: 0; -} - -.toggle-placeholder { - width: calc(1rem + 2px); - flex-shrink: 0; -} - -.node-label { - flex: 1; - font-size: 0.8125rem; - color: #333; - text-decoration: none; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.node-label.clickable { - cursor: pointer; - user-select: none; -} - -.node-label.active { - font-weight: 600; - color: #4f46e5; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.ts b/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.ts deleted file mode 100644 index c198f64..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/sidebar-tree-node/sidebar-tree-node.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Component, input } from '@angular/core'; -import { RouterLink, RouterLinkActive } from '@angular/router'; -import { SidebarNode } from '../../../shared/classes/sidebar-node'; - -@Component({ - selector: 'app-sidebar-tree-node', - imports: [RouterLink, RouterLinkActive, SidebarTreeNode], - templateUrl: './sidebar-tree-node.html', - styleUrl: './sidebar-tree-node.scss', -}) -export class SidebarTreeNode { - - // ************************** Constructor ************************** - - // ************************** Declarations ************************* - - readonly nodes = input.required(); - readonly depth = input(0); - - // ************************** Properties *************************** - - // ***************************************************************** - // Computed indentation in rem based on depth. - // ***************************************************************** - indent(): string { - return (1 + this.depth() * 1.5) + 'rem'; - } - - // ************************ Public Methods ************************* - - // ***************************************************************** - // Returns true if a node should show the +/- toggle. - // A node is expandable if it has static children, or has a - // loadChildren function with a non-zero childCount. - // ***************************************************************** - hasToggle(node: SidebarNode): boolean { - if (node.children && node.children.length > 0) return true; - if (node.loadChildren && node.childCount !== 0) return true; - return false; - } - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Toggles a node's expanded state. On first expand of a lazy node, - // calls loadChildren and caches the result in node.children. - // ***************************************************************** - async onToggle(node: SidebarNode, event: Event) { - if (!this.hasToggle(node)) return; - - event.preventDefault(); - event.stopPropagation(); - - if (node.expanded) { - node.expanded = false; - return; - } - - if (node.loadChildren && !node.children) { - node.children = await node.loadChildren(); - } - - node.expanded = true; - } - - // ********************** Support Procedures *********************** -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.html b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.html deleted file mode 100644 index a782ef2..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.html +++ /dev/null @@ -1,45 +0,0 @@ -
- - Student Detail - -
- -@if (errorMessage()) { -

{{ errorMessage() }}

-} - - - -@if (loaded()) { -
-
- Student -
- -
-
- - -
-
- - -
- -
- - -
-
- - -
-} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.scss b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.scss deleted file mode 100644 index cc2b750..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.scss +++ /dev/null @@ -1,180 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - -.toolbar { - display: flex; - align-items: center; - position: relative; - 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; -} - -.toolbar-btn:disabled { - opacity: 0.5; - cursor: not-allowed; -} - -.back-btn { - margin-left: 0.5rem; -} - -.toolbar-title { - position: absolute; - left: 50%; - transform: translateX(-50%); - font-weight: 600; - font-size: 1.25rem; - color: #333; -} - -.spacer { - flex: 1; -} - -.error { - font-size: 0.875rem; - color: #dc2626; - margin: 0 0 1rem; -} - - - -.detail-card { - background: #fff; - border: 1px solid #ddd; - border-radius: 8px; - max-width: 480px; - overflow: hidden; -} - -.detail-card-header { - display: flex; - align-items: center; - padding: 0.625rem 1.25rem; - background: #f8f9fa; - border-bottom: 1px solid #ddd; -} - -.detail-card-title { - font-size: 0.875rem; - font-weight: 600; - color: #333; -} - -.detail-card-body { - padding: 1.5rem; -} - -.field { - display: flex; - flex-direction: column; - margin-bottom: 1rem; -} - -.field-label { - font-size: 0.75rem; - font-weight: 600; - color: #666; - text-transform: uppercase; - letter-spacing: 0.025em; - margin-bottom: 0.25rem; -} - -.field-value { - font-size: 0.9375rem; - color: #333; -} - -.field-input { - padding: 0.375rem 0.5rem; - border: 1px solid #ccc; - border-radius: 6px; - font-size: 0.9375rem; - outline: none; -} - -.field-input:focus { - border-color: #4f46e5; -} - -.card-footer { - display: flex; - align-items: center; - padding: 0.5rem 1.5rem; - border-top: 1px solid #eee; -} - -.footer-btn { - padding: 0.375rem 0.75rem; - background: transparent; - border: 1px solid; - border-radius: 6px; - font-size: 0.8125rem; - font-weight: 500; - cursor: pointer; -} - -.btn-dark-green { - color: #15803d; - border-color: #15803d; -} - -.btn-dark-green:hover { - background: #f0fdf4; -} - -.success-label { - font-size: 0.8125rem; - color: #16a34a; - opacity: 1; - transition: opacity 1s ease; -} - -.success-label.fade-out { - opacity: 0; -} - -.actions { - display: flex; - justify-content: flex-end; - gap: 0.5rem; - margin-top: 0.5rem; -} - -.actions .toolbar-btn { - min-width: 6rem; -} - -.save-btn { - background: #4f46e5; - color: #fff; - border-color: #4f46e5; -} - -.save-btn:hover { - background: #4338ca; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.spec.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.spec.ts deleted file mode 100644 index 4ed435b..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { StudentCardFull } from './student-card-full'; - -describe('StudentCardFull', () => { - let component: StudentCardFull; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [StudentCardFull] - }) - .compileComponents(); - - fixture = TestBed.createComponent(StudentCardFull); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.ts deleted file mode 100644 index a0f85d3..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-full/student-card-full.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { Component, inject, signal, OnDestroy } from '@angular/core'; -import { ActivatedRoute, Router } from '@angular/router'; -import { FormsModule } from '@angular/forms'; -import { Subscription } from 'rxjs'; -import { StudentService } from '../../../shared/services/student.service'; -import { StudentCardDto } from '../../../shared/classes/student-card.dto'; - -@Component({ - selector: 'app-student-card-full', - imports: [FormsModule], - templateUrl: './student-card-full.html', - styleUrl: './student-card-full.scss', -}) -export class StudentCardFull implements OnDestroy { - - // ************************** Constructor ************************** - - constructor() { - this.paramSub = this.route.paramMap.subscribe(params => { - this.studentId = params.get('studentId')!; - this.loadStudent(); - }); - } - - // ************************** Declarations ************************* - - private readonly studentService = inject(StudentService); - private readonly route = inject(ActivatedRoute); - private readonly router = inject(Router); - private readonly paramSub: Subscription; - - private studentId!: string; - - protected readonly errorMessage = signal(null); - protected readonly successMessage = signal(null); - protected readonly saving = signal(false); - protected readonly loaded = signal(false); - protected readonly fading = signal(false); - private successTimer: any = null; - - // Form fields — always editable - protected identifier = ''; - protected nextIepDate = ''; - - // Snapshot of last-saved values for cancel - private savedIdentifier = ''; - private savedNextIepDate = ''; - - // ************************** Properties *************************** - - // ***************************************************************** - // Returns true if form values differ from the saved snapshot. - // ***************************************************************** - hasChanges(): boolean { - return this.identifier !== this.savedIdentifier - || this.nextIepDate !== this.savedNextIepDate; - } - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ***************************************************************** - // Saves changes to the student via the API. - // ***************************************************************** - async onSave() { - this.saving.set(true); - this.errorMessage.set(null); - this.successMessage.set(null); - - const result = await this.studentService.updateStudent(this.studentId, { - identifier: this.identifier, - nextIepDate: this.nextIepDate || null, - }); - - this.saving.set(false); - - if (result.success) { - this.savedIdentifier = this.identifier; - this.savedNextIepDate = this.nextIepDate; - this.showSuccessTemporarily('Changes saved.'); - this.studentService.notifyDataChanged(); - } else { - this.errorMessage.set(result.message); - } - } - - // ***************************************************************** - // Reverts form fields to the last-saved snapshot. - // ***************************************************************** - onCancel() { - this.identifier = this.savedIdentifier; - this.nextIepDate = this.savedNextIepDate; - this.errorMessage.set(null); - this.successMessage.set(null); - } - - onBack() { - this.router.navigate(['/students']); - } - - onGoals() { - this.router.navigate(['/students', this.studentId, 'goals']); - } - - ngOnDestroy() { - this.paramSub.unsubscribe(); - if (this.successTimer) clearTimeout(this.successTimer); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Shows a success message for 4 seconds, then fades it out over 1s. - // ***************************************************************** - private showSuccessTemporarily(message: string) { - if (this.successTimer) clearTimeout(this.successTimer); - this.fading.set(false); - this.successMessage.set(message); - - this.successTimer = setTimeout(() => { - this.fading.set(true); - this.successTimer = setTimeout(() => { - this.successMessage.set(null); - this.fading.set(false); - }, 1000); - }, 4000); - } - - // ***************************************************************** - // Loads the student by ID and populates form fields. - // ***************************************************************** - private loadStudent() { - if (!this.loaded()) { - this.loaded.set(false); - } - this.studentService.getStudentById(this.studentId).then(result => { - if (result.success && result.payload) { - const s = result.payload; - this.identifier = s.identifier; - this.nextIepDate = this.toDateInput(s.nextIepDate); - - this.savedIdentifier = this.identifier; - this.savedNextIepDate = this.nextIepDate; - this.loaded.set(true); - } else { - this.errorMessage.set(result.message); - } - }); - } - - // ***************************************************************** - // Converts a Date to a YYYY-MM-DD string for date input binding. - // ***************************************************************** - private toDateInput(date: Date | null): string { - if (!date) return ''; - const d = new Date(date); - return d.toISOString().split('T')[0]; - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.html b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.html deleted file mode 100644 index 557eb26..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.html +++ /dev/null @@ -1,100 +0,0 @@ -
-

{{ showAll() ? 'All Students' : 'My Students' }} - {{ students().length }}

-
- -
- - -
- -
-
- -@if (showAddModal()) { - -} - -@if (displayMode() === 'card') { - @if (loaded() && students().length === 0) { -
-

You don't have any students entered yet.

-

Click + Add a Student in the upper right to get started.

-
- } @else { -
- @for (student of students(); track student.studentId) { - - } -
- } -} @else { - @if (loaded() && students().length === 0) { -
-

You don't have any students entered yet.

-

Click + Add a Student in the upper right to get started.

-
- } @else { -
-
- Student - @if (showAll()) { - Owner - } - IEP Date - Last Entry - Goals - Events - Benchmarks -
- @for (student of students(); track student.studentId) { -
- 🎓 {{ student.identifier }} - @if (showAll()) { - {{ student.ownerName }} - } - {{ student.nextIepDate | date:'M/d/yy' }} - - @if (student.lastEntryDate) { - {{ student.lastEntryDate | date:'M/d/yy' }} - } @else { - No entries yet - } - - {{ student.goalCount }} - {{ student.progressEventCount }} - {{ student.benchmarkCount }} -
- } -
- } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.scss b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.scss deleted file mode 100644 index 5c9211d..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.scss +++ /dev/null @@ -1,210 +0,0 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; -} - -.toolbar { - display: flex; - align-items: center; - justify-content: space-between; - height: 40px; - padding-right: 0.5rem; - border-radius: 8px; - background: #fff; - border-bottom: 1px solid #ddd; - margin-bottom: 1rem; - flex-shrink: 0; -} - -.toolbar-right { - display: flex; - align-items: center; - gap: 0.5rem; -} - -.view-toggle { - display: flex; - border: 1px solid #d1d5db; - border-radius: 6px; - overflow: hidden; -} - -.toggle-btn { - display: flex; - align-items: center; - justify-content: center; - width: 32px; - height: 28px; - background: transparent; - border: none; - color: #6b7280; - cursor: pointer; - - &:hover { - background: #f3f4f6; - color: #374151; - } - - &.active { - background: #eef2ff; - color: #4f46e5; - } - - & + & { - border-left: 1px solid #d1d5db; - } -} - -.page-title { - font-size: 24px; - font-weight: 600; - margin-left: 0.75rem; -} - -.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; -} - -.card-grid { - display: flex; - flex-wrap: wrap; - align-content: flex-start; - gap: 1rem; - overflow-y: auto; - flex: 1; -} - -.student-list { - display: flex; - flex-direction: column; - overflow-y: auto; - flex: 1; - background: #fff; - border: 1px solid #ddd; - border-radius: 8px; -} - -.list-header { - display: flex; - align-items: center; - padding: 0.5rem 1rem; - background: #f9fafb; - border-bottom: 1px solid #e5e7eb; - font-size: 0.75rem; - font-weight: 600; - color: #6b7280; - text-transform: uppercase; - letter-spacing: 0.05em; - flex-shrink: 0; -} - -.list-row { - display: flex; - align-items: center; - padding: 0.625rem 1rem; - border-bottom: 1px solid #f3f4f6; - cursor: pointer; - font-size: 0.9rem; - - &:last-child { - border-bottom: none; - } - - &:hover { - background: #f5f3ff; - } -} - -.col-name { - flex: 2; - font-weight: 600; - color: #1f2937; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.col-iep, -.col-entry { - flex: 1; - color: #4b5563; - font-size: 0.875rem; -} - -.col-stat { - flex: 0 0 80px; - text-align: center; - color: #374151; - font-size: 0.875rem; -} - -.no-entry { - color: #9ca3af; - font-style: italic; -} - -.empty-state { - text-align: center; - padding: 3rem 1.5rem; - color: #555; - font-size: 0.9375rem; - background: #fff; - border-radius: 8px; - border: 1px solid #ddd; -} - -.scope-toggle { - display: flex; - align-items: center; - gap: 0.375rem; - cursor: pointer; - font-size: 0.875rem; - color: #4b5563; - user-select: none; -} - -.toggle-track { - display: inline-flex; - align-items: center; - width: 36px; - height: 20px; - background: #d1d5db; - border-radius: 10px; - padding: 2px; - transition: background 0.2s; - - &.active { - background: #4f46e5; - } -} - -.toggle-thumb { - width: 16px; - height: 16px; - background: #fff; - border-radius: 50%; - transition: transform 0.2s; - - .active > & { - transform: translateX(16px); - } -} - -.col-owner { - flex: 1; - color: #4b5563; - font-size: 0.875rem; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.ts deleted file mode 100644 index 6c3b3ee..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card-list/student-card-list.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Component, inject, signal } from '@angular/core'; -import { Router, RouterLink } from '@angular/router'; -import { DatePipe } from '@angular/common'; -import { StudentCard } from '../student-card/student-card'; -import { AddStudentModal } from '../add-student-modal/add-student-modal'; -import { DummyStudentService } from '../../../shared/services/dummy-student.service'; -import { StudentCardDto } from '../../../shared/classes/student-card.dto'; -import { StudentService } from '../../../shared/services/student.service'; - -export type DisplayMode = 'card' | 'list'; - -@Component({ - selector: 'app-student-card-list', - imports: [StudentCard, AddStudentModal, RouterLink, DatePipe], - templateUrl: './student-card-list.html', - styleUrl: './student-card-list.scss', -}) -export class StudentCardList { - - // ************************** Constructor ************************** - - constructor() { - this.loadStudents(); - } - - // ************************** Declarations ************************* - - private readonly studentService = inject(StudentService); - private readonly router = inject(Router); - protected readonly students = signal([]); - protected readonly displayMode = signal('card'); - protected readonly showAddModal = signal(false); - protected readonly loaded = signal(false); - protected readonly showAll = signal(false); - - public errorMessage = signal(null); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - setDisplayMode(mode: DisplayMode) { - this.displayMode.set(mode); - } - - onAddStudent() { - this.showAddModal.set(true); - } - - // ***************************************************************** - // Toggles between "My Students" and "All Students" scope, then - // reloads the student list from the API with the new scope. - // ***************************************************************** - onToggleScope() { - this.showAll.update(v => !v); - this.loaded.set(false); - this.loadStudents(); - this.studentService.notifyDataChanged(); - } - - onStudentCreated(student: StudentCardDto) { - this.students.update(list => this.sortByIdentifier([...list, student])); - this.showAddModal.set(false); - this.studentService.notifyDataChanged(); - this.router.navigate(['/students', student.studentId]); - } - - onModalCancelled() { - this.showAddModal.set(false); - } - - // ********************** Support Procedures *********************** - - // ***************************************************************** - // Sorts an array of students alphabetically by identifier. - // ***************************************************************** - private sortByIdentifier(students: StudentCardDto[]): StudentCardDto[] { - return students.sort((a, b) => - a.identifier.localeCompare(b.identifier, undefined, { sensitivity: 'base' }) - ); - } - - // ***************************************************************** - // Loads students from the service and populates the students signal. - // Uses scope 'all' when the toggle is active. - // ***************************************************************** - private loadStudents() { - const scope = this.showAll() ? 'all' : undefined; - this.studentService.getMyStudents(scope).then(data => { - - if (!data.success) { - this.errorMessage.set(data.message); - } - else { - this.students.set(this.sortByIdentifier(data.payload || [])) - } - - this.loaded.set(true); - }); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.html b/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.html deleted file mode 100644 index a91885d..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.html +++ /dev/null @@ -1,20 +0,0 @@ -
-

🎓 {{ student().identifier }}

- -
- IEP Date: {{ student().nextIepDate | date:'M/d/yy'}} - - @if (student().lastEntryDate) { - Last entry: {{ student().lastEntryDate | date:'M/d/yy' }} - } @else { - No entries yet - } - -
- -
- Goals: {{ student().goalCount }} - Events: {{ student().progressEventCount }} - Benchmarks: {{ student().benchmarkCount }} -
-
\ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.scss b/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.scss deleted file mode 100644 index a5e65bb..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.scss +++ /dev/null @@ -1,49 +0,0 @@ -:host { - display: block; - width: 280px; -} - -.card { - background: #fff; - border-radius: 8px; - box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); - padding: 1.5rem; - cursor: pointer; - transition: box-shadow 0.15s ease, transform 0.15s ease; -} - -.card:hover { - box-shadow: 0 4px 20px rgba(79, 70, 229, 0.15); - transform: translateY(-2px); -} - -.identifier { - margin: 0 0 0.75rem; - font-size: 1.5rem; - text-align: center; -} - -.meta { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; - font-size: 0.875rem; - color: #666; -} - -.badge { - padding: 0.25rem 0.5rem; - border: 1px solid #ddd; - border-radius: 6px; - font-size: 0.8125rem; - color: #333; -} - -.stats { - display: flex; - justify-content: space-around; - font-size: 0.875rem; - font-weight: 500; - color: #333; -} \ No newline at end of file diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.ts deleted file mode 100644 index de6f77d..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-card/student-card.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Component, computed, input } from '@angular/core'; -import { DatePipe } from '@angular/common'; -import { RouterLink } from '@angular/router'; -import { StudentCardDto } from '../../../shared/classes/student-card.dto'; - -@Component({ - selector: 'app-student-card', - imports: [DatePipe, RouterLink], - templateUrl: './student-card.html', - styleUrl: './student-card.scss', -}) -export class StudentCard { - - // ************************** Constructor ************************** - - // ************************** Declarations ************************* - - readonly student = input.required(); - - // ************************** Properties *************************** - - // ************************ Public Methods ************************* - - // ************************ Event Handlers ************************* - - // ********************** Support Procedures *********************** -} diff --git a/ui/winstudentgoaltracker/src/app/shared/classes/sidebar-node.ts b/ui/winstudentgoaltracker/src/app/shared/classes/sidebar-node.ts deleted file mode 100644 index 5512921..0000000 --- a/ui/winstudentgoaltracker/src/app/shared/classes/sidebar-node.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SidebarNode { - label: string; - routerLink?: string[]; - children?: SidebarNode[]; - expanded?: boolean; - loadChildren?: () => Promise; - childCount?: number; -}