From af4ec3f7515cb961104dae7a3cc107d35f18cf35 Mon Sep 17 00:00:00 2001 From: Oliver Pelly Date: Wed, 8 Apr 2026 17:39:03 -0700 Subject: [PATCH] dead code cleanup and component consolidation --- .../src/app/components/example/example.html | 1 - .../src/app/components/example/example.scss | 0 .../app/components/example/example.spec.ts | 23 ----- .../src/app/components/example/example.ts | 11 --- .../add-student-modal/add-student-modal.html | 22 ----- .../add-student-modal/add-student-modal.ts | 45 --------- .../desktop/components/edit-icon/edit-icon.ts | 40 ++++++++ .../edit-student-modal.scss | 1 - .../edit-student-modal/edit-student-modal.ts | 51 ---------- .../student-modal.html} | 11 ++- .../student-modal.scss} | 0 .../components/student-modal/student-modal.ts | 90 ++++++++++++++++++ .../student-progress-report.scss | 82 +---------------- .../student-progress-report.ts | 16 +--- .../components/workspace/workspace.html | 28 ++---- .../components/workspace/workspace.scss | 31 ++++--- .../desktop/components/workspace/workspace.ts | 15 +-- .../src/app/desktop/pages/home/home.html | 20 ++-- .../src/app/desktop/pages/home/home.scss | 15 --- .../src/app/desktop/pages/home/home.ts | 27 +++--- .../src/app/desktop/styles/_detail-page.scss | 92 +++++++++++++++++++ .../app/shared/services/student.service.ts | 14 +-- .../src/app/shared/utils/format-date.ts | 13 +++ 23 files changed, 299 insertions(+), 349 deletions(-) delete mode 100644 ui/winstudentgoaltracker/src/app/components/example/example.html delete mode 100644 ui/winstudentgoaltracker/src/app/components/example/example.scss delete mode 100644 ui/winstudentgoaltracker/src/app/components/example/example.spec.ts delete mode 100644 ui/winstudentgoaltracker/src/app/components/example/example.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.html delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.ts create mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/edit-icon/edit-icon.ts delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.scss delete mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.ts rename ui/winstudentgoaltracker/src/app/desktop/components/{edit-student-modal/edit-student-modal.html => student-modal/student-modal.html} (56%) rename ui/winstudentgoaltracker/src/app/desktop/components/{add-student-modal/add-student-modal.scss => student-modal/student-modal.scss} (100%) create mode 100644 ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.ts create mode 100644 ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss create mode 100644 ui/winstudentgoaltracker/src/app/shared/utils/format-date.ts diff --git a/ui/winstudentgoaltracker/src/app/components/example/example.html b/ui/winstudentgoaltracker/src/app/components/example/example.html deleted file mode 100644 index b9c1b38..0000000 --- a/ui/winstudentgoaltracker/src/app/components/example/example.html +++ /dev/null @@ -1 +0,0 @@ -

example works!

diff --git a/ui/winstudentgoaltracker/src/app/components/example/example.scss b/ui/winstudentgoaltracker/src/app/components/example/example.scss deleted file mode 100644 index e69de29..0000000 diff --git a/ui/winstudentgoaltracker/src/app/components/example/example.spec.ts b/ui/winstudentgoaltracker/src/app/components/example/example.spec.ts deleted file mode 100644 index 7062995..0000000 --- a/ui/winstudentgoaltracker/src/app/components/example/example.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; - -import { Example } from './example'; - -describe('Example', () => { - let component: Example; - let fixture: ComponentFixture; - - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [Example] - }) - .compileComponents(); - - fixture = TestBed.createComponent(Example); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/ui/winstudentgoaltracker/src/app/components/example/example.ts b/ui/winstudentgoaltracker/src/app/components/example/example.ts deleted file mode 100644 index 8413ed5..0000000 --- a/ui/winstudentgoaltracker/src/app/components/example/example.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Component } from '@angular/core'; - -@Component({ - selector: 'app-example', - imports: [], - templateUrl: './example.html', - styleUrl: './example.scss', -}) -export class Example { - -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.html b/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.html deleted file mode 100644 index bc4a110..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.html +++ /dev/null @@ -1,22 +0,0 @@ - -
- - -
-
- - -
- - @if (errorMessage()) { -

{{ errorMessage() }}

- } - - -
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.ts b/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.ts deleted file mode 100644 index b2f4714..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Component, inject, output, signal } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { ModalShell } from '../modal-shell/modal-shell'; -import { CreateStudentDto } from '../../../shared/classes/create-student.dto'; -import { StudentCardDto } from '../../../shared/classes/student-card.dto'; -import { StudentService } from '../../../shared/services/student.service'; - -@Component({ - selector: 'app-add-student-modal', - imports: [FormsModule, ModalShell], - templateUrl: './add-student-modal.html', - styleUrl: './add-student-modal.scss', -}) -export class AddStudentModal { - private readonly studentService = inject(StudentService); - - readonly studentCreated = output(); - readonly cancelled = output(); - - protected readonly isSubmitting = signal(false); - protected readonly errorMessage = signal(null); - - protected form: CreateStudentDto = { - identifier: '', - programYear: null, - enrollmentDate: null, - nextIepDate: null, - }; - - async onSubmit() { - if (!this.form.identifier.trim()) return; - this.errorMessage.set(null); - this.isSubmitting.set(true); - - const result = await this.studentService.createStudent(this.form); - this.isSubmitting.set(false); - - if (!result.success) { - this.errorMessage.set(result.message); - return; - } - - this.studentCreated.emit(result.payload!); - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-icon/edit-icon.ts b/ui/winstudentgoaltracker/src/app/desktop/components/edit-icon/edit-icon.ts new file mode 100644 index 0000000..9770b06 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/components/edit-icon/edit-icon.ts @@ -0,0 +1,40 @@ +import { Component, input } from '@angular/core'; + +@Component({ + selector: 'app-edit-icon', + imports: [], + template: ` + + `, + styles: [` + .edit-icon { + background: none; + border: none; + cursor: pointer; + padding: 2px; + flex-shrink: 0; + display: flex; + align-items: center; + } + .edit-icon:hover svg { + stroke: #555 !important; /* Force hover color since original styles did the same */ + } + :host-context(.student-item) .edit-icon { + opacity: 0; + transition: opacity 0.15s ease; + } + :host-context(.student-item:hover) .edit-icon { + opacity: 1; + } + `] +}) +export class EditIcon { + readonly size = input(14); + readonly color = input('#999'); + readonly ariaLabel = input('Edit'); +} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.scss b/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.scss deleted file mode 100644 index 2918c96..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.scss +++ /dev/null @@ -1 +0,0 @@ -/* Inherits all styles from modal-shell via ::ng-deep */ diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.ts b/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.ts deleted file mode 100644 index 931823e..0000000 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Component, inject, input, output, signal } from '@angular/core'; -import { FormsModule } from '@angular/forms'; -import { ModalShell } from '../modal-shell/modal-shell'; -import { StudentService } from '../../../shared/services/student.service'; -import { StudentCardDto } from '../../../shared/classes/student-card.dto'; - -@Component({ - selector: 'app-edit-student-modal', - imports: [FormsModule, ModalShell], - templateUrl: './edit-student-modal.html', - styleUrl: './edit-student-modal.scss', -}) -export class EditStudentModal { - private readonly studentService = inject(StudentService); - - readonly student = input.required(); - readonly saved = output(); - readonly closed = output(); - - protected readonly saving = signal(false); - protected readonly errorMessage = signal(null); - - protected identifier = ''; - protected nextIepDate = ''; - - ngOnInit() { - const s = this.student(); - this.identifier = s.identifier; - this.nextIepDate = s.nextIepDate ? new Date(s.nextIepDate).toISOString().split('T')[0] : ''; - } - - async onSave() { - if (!this.identifier.trim()) return; - this.saving.set(true); - this.errorMessage.set(null); - - const result = await this.studentService.updateStudent(this.student().studentId, { - identifier: this.identifier, - nextIepDate: this.nextIepDate || null, - }); - - this.saving.set(false); - - if (result.success) { - this.studentService.notifyDataChanged(); - this.saved.emit(); - } else { - this.errorMessage.set(result.message); - } - } -} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.html b/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.html similarity index 56% rename from ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.html rename to ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.html index 3e7fc6b..c26a33e 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-student-modal/edit-student-modal.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.html @@ -1,10 +1,11 @@ - +
- +
- +
@@ -14,8 +15,8 @@
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.scss b/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.scss similarity index 100% rename from ui/winstudentgoaltracker/src/app/desktop/components/add-student-modal/add-student-modal.scss rename to ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.scss diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.ts new file mode 100644 index 0000000..698d435 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/components/student-modal/student-modal.ts @@ -0,0 +1,90 @@ +import { Component, inject, input, output, signal } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { ModalShell } from '../modal-shell/modal-shell'; +import { StudentService } from '../../../shared/services/student.service'; +import { StudentCardDto } from '../../../shared/classes/student-card.dto'; +import { toIsoDateString } from '../../../shared/utils/format-date'; + +@Component({ + selector: 'app-student-modal', + imports: [FormsModule, ModalShell], + templateUrl: './student-modal.html', + styleUrl: './student-modal.scss', +}) +export class StudentModal { + private readonly studentService = inject(StudentService); + + /** Optional: when provided the modal operates in edit mode. */ + readonly student = input(null); + + /** Emits the newly created student (add mode). */ + readonly studentCreated = output(); + + /** Emits when an existing student has been saved (edit mode). */ + readonly saved = output(); + + /** Emits when the modal is dismissed without saving. */ + readonly closed = output(); + + protected readonly isSubmitting = signal(false); + protected readonly errorMessage = signal(null); + + protected identifier = ''; + protected nextIepDate = ''; + + protected get isEditMode(): boolean { + return !!this.student(); + } + + protected get modalTitle(): string { + return this.isEditMode ? 'Edit Student' : 'Add Student'; + } + + protected get submitLabel(): string { + return this.isEditMode ? 'Save' : 'Add Student'; + } + + ngOnInit() { + const s = this.student(); + if (s) { + // Edit mode — populate form from the existing student + this.identifier = s.identifier; + this.nextIepDate = s.nextIepDate ? toIsoDateString(s.nextIepDate) : ''; + } + } + + async onSubmit() { + if (!this.identifier.trim()) return; + this.errorMessage.set(null); + this.isSubmitting.set(true); + + if (this.isEditMode) { + const result = await this.studentService.updateStudent(this.student()!.studentId, { + identifier: this.identifier, + nextIepDate: this.nextIepDate || null, + }); + this.isSubmitting.set(false); + + if (result.success) { + this.studentService.notifyDataChanged(); + this.saved.emit(); + } else { + this.errorMessage.set(result.message); + } + } else { + const result = await this.studentService.createStudent({ + identifier: this.identifier, + programYear: null, + enrollmentDate: null, + nextIepDate: this.nextIepDate ? new Date(this.nextIepDate) : null, + }); + this.isSubmitting.set(false); + + if (result.success) { + this.studentCreated.emit(result.payload!); + } else { + this.errorMessage.set(result.message); + } + } + } +} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.scss b/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.scss index b91047c..ecc2da1 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.scss @@ -1,56 +1,4 @@ -:host { - display: flex; - flex-direction: column; - height: 100%; - padding: 24px 28px; -} - -.toolbar { - display: flex; - align-items: center; - gap: 0.75rem; - margin-bottom: 20px; - flex-shrink: 0; -} - -.toolbar-btn { - padding: 6px 14px; - background: transparent; - color: var(--accent-indigo); - border: 1px solid var(--accent-indigo); - border-radius: var(--radius-md); - font-size: 13px; - font-weight: 500; - cursor: pointer; - font-family: inherit; - - &:hover { - background: #EEF2FF; - } - - &:disabled { - opacity: 0.5; - cursor: not-allowed; - } -} - -.toolbar-title { - font-weight: 600; - font-size: 18px; - color: var(--text-primary); -} - -.spacer { - flex: 1; -} - -.detail-card { - background: var(--bg-surface); - border: 1px solid var(--border-color); - border-radius: var(--radius-xl); - max-width: 600px; - overflow: hidden; -} +@use '../../styles/detail-page'; .card-header { display: flex; @@ -70,28 +18,6 @@ padding: 22px; } -.field { - display: flex; - flex-direction: column; - margin-bottom: 14px; -} - -.field-label { - font-size: 12px; - font-weight: 600; - color: #666; - margin-bottom: 4px; -} - -.field-input { - padding: 8px 10px; - border: 1px solid var(--border-muted); - border-radius: var(--radius-md); - font-family: inherit; - font-size: 13px; - outline: none; -} - .date-row { display: flex; gap: 1.5rem; @@ -129,12 +55,6 @@ } } -.actions { - display: flex; - justify-content: flex-end; - margin-top: 8px; -} - .run-btn { background: var(--accent-indigo) !important; color: #fff !important; diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.ts b/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.ts index 6b09360..ce5fc33 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/components/student-progress-report/student-progress-report.ts @@ -4,6 +4,7 @@ import { Router } from '@angular/router'; import { StudentService } from '../../../shared/services/student.service'; import { StudentCardDto } from '../../../shared/classes/student-card.dto'; import { StudentGoalItem } from '../../../shared/classes/student-goal'; +import { toIsoDateString } from '../../../shared/utils/format-date'; interface GoalCheckItem { goalId: string; @@ -62,10 +63,10 @@ export class StudentProgressReport { if (!student) return; if (student.firstEntryDate) { - this.fromDate = this.toIsoDate(new Date(student.firstEntryDate)); + this.fromDate = toIsoDateString(new Date(student.firstEntryDate)); } if (student.lastEntryDate) { - this.toDate = this.toIsoDate(new Date(student.lastEntryDate)); + this.toDate = toIsoDateString(new Date(student.lastEntryDate)); } const goalsResult = await this.studentService.getGoalsForStudent(this.selectedStudentId); @@ -145,15 +146,4 @@ export class StudentProgressReport { document.body.removeChild(a); URL.revokeObjectURL(url); } - - // ***************************************************************** - // Formats a Date as an ISO date string (yyyy-MM-dd) for use with - // the native HTML date input. - // ***************************************************************** - private toIsoDate(date: Date): string { - const y = date.getFullYear(); - const m = String(date.getMonth() + 1).padStart(2, '0'); - const d = String(date.getDate()).padStart(2, '0'); - return `${y}-${m}-${d}`; - } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html index 9276a5b..19f307f 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html @@ -48,12 +48,7 @@
{{ selectedGoal()!.category }} Goal - + @if (selectedGoal()!.targetCompletionDate) { Due {{ formatDate(selectedGoal()!.targetCompletionDate) }} } @@ -81,12 +76,7 @@
{{ b.shortName || b.benchmark }} - +

{{ b.benchmark }}

@@ -105,14 +95,16 @@
{{ formatDate(ev.createdAt) }} - +

{{ ev.content }}

+ @if (getBenchmarksForEvent(ev.progressEventId).length > 0) { +
+ @for (b of getBenchmarksForEvent(ev.progressEventId); track b.benchmarkId) { + {{ b.shortName || b.benchmark }} + } +
+ }
} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss index 3ff03c5..c075c93 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss @@ -124,20 +124,6 @@ color: #333; } -.edit-icon { - background: none; - border: none; - cursor: pointer; - padding: 2px; - flex-shrink: 0; - display: flex; - align-items: center; - - &:hover svg { - stroke: #555; - } -} - /* ─── Sub Tabs ─── */ .sub-tabs { display: flex; @@ -265,6 +251,23 @@ color: #333; } +.event-benchmarks { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 10px; +} + +.benchmark-tag { + font-size: 11px; + font-weight: 500; + color: #4338CA; + background: #EEF2FF; + padding: 3px 8px; + border-radius: var(--radius-sm); + border: 1px solid #C7D2FE; +} + /* ─── Add Buttons ─── */ .add-btn { padding: 12px; diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts index e4e96b6..be6daad 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts @@ -8,12 +8,14 @@ import { StudentFullProfileDto, ProgressEventWithGoalDto, ProgressEventBenchmark import { GoalModal } from '../goal-modal/goal-modal'; import { EditBenchmarkModal } from '../edit-benchmark-modal/edit-benchmark-modal'; import { EditEventModal } from '../edit-event-modal/edit-event-modal'; +import { EditIcon } from '../edit-icon/edit-icon'; +import { formatDate } from '../../../shared/utils/format-date'; type TabView = 'benchmarks' | 'progress'; @Component({ selector: 'app-workspace', - imports: [GoalModal, EditBenchmarkModal, EditEventModal], + imports: [GoalModal, EditBenchmarkModal, EditEventModal, EditIcon], templateUrl: './workspace.html', styleUrl: './workspace.scss', }) @@ -177,14 +179,13 @@ export class Workspace { .map(link => link.benchmarkId); } - // ************************ Formatting Helpers ********************** - - formatDate(d: string | Date | null): string { - if (!d) return ''; - const date = new Date(d); - return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + getBenchmarksForEvent(progressEventId: string): BenchmarkDto[] { + const ids = this.getBenchmarkIdsForEvent(progressEventId); + return this.benchmarks().filter(b => ids.includes(b.benchmarkId)); } + formatDate = formatDate; + // ********************** Support Procedures *********************** private async loadStudentData(studentId: string) { diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html index 644ca6c..3264b52 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html @@ -1,12 +1,11 @@
- @if (showAddStudentModal()) { - - } - @if (editingStudent()) { - + @if (showStudentModal()) { + } @@ -43,12 +42,7 @@ IEP: {{ formatDate(s.nextIepDate) }}
- + } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss index 526c704..eb666d7 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss @@ -157,21 +157,6 @@ color: var(--text-dim); } -.edit-pencil { - background: none; - border: none; - cursor: pointer; - padding: 2px; - flex-shrink: 0; - display: flex; - opacity: 0; - transition: opacity var(--transition-fast); - - .student-item:hover & { - opacity: 1; - } -} - /* ─── Sidebar Footer ─── */ .sidebar-footer { padding: 10px 12px; diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts index 3257f9a..cdd8fef 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts @@ -3,12 +3,13 @@ import { RouterLink, RouterOutlet, Router } from '@angular/router'; import { Auth } from '../../../shared/services/auth'; import { StudentService } from '../../../shared/services/student.service'; import { StudentCardDto } from '../../../shared/classes/student-card.dto'; -import { AddStudentModal } from '../../components/add-student-modal/add-student-modal'; -import { EditStudentModal } from '../../components/edit-student-modal/edit-student-modal'; +import { StudentModal } from '../../components/student-modal/student-modal'; +import { EditIcon } from '../../components/edit-icon/edit-icon'; +import { formatDate } from '../../../shared/utils/format-date'; @Component({ selector: 'app-home', - imports: [RouterOutlet, RouterLink, AddStudentModal, EditStudentModal], + imports: [RouterOutlet, RouterLink, StudentModal, EditIcon], templateUrl: './home.html', styleUrl: './home.scss', }) @@ -39,8 +40,7 @@ export class Home { protected readonly students = signal([]); protected readonly selectedStudentId = signal(null); protected readonly showAll = signal(false); - protected readonly showAddStudentModal = signal(false); - protected readonly editingStudent = signal(null); + protected readonly showStudentModal = signal(null); // Groups students by owner when "All" is active. protected readonly groupedStudents = computed(() => { @@ -88,11 +88,11 @@ export class Home { } onAddStudent() { - this.showAddStudentModal.set(true); + this.showStudentModal.set('add'); } onStudentCreated(student: StudentCardDto) { - this.showAddStudentModal.set(false); + this.showStudentModal.set(null); this.studentService.notifyDataChanged(); this.selectedStudentId.set(student.studentId); this.router.navigate(['/students', student.studentId]); @@ -100,11 +100,11 @@ export class Home { onEditStudent(student: StudentCardDto, event: Event) { event.stopPropagation(); - this.editingStudent.set(student); + this.showStudentModal.set(student); } - onEditStudentSaved() { - this.editingStudent.set(null); + onStudentSaved() { + this.showStudentModal.set(null); this.loadStudents(); } @@ -113,12 +113,7 @@ export class Home { this.auth.forceLogout(); } - // ************************ Formatting Helpers ********************** - - formatDate(d: Date | null): string { - if (!d) return ''; - return new Date(d).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); - } + formatDate = formatDate; // ********************** Support Procedures *********************** diff --git a/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss b/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss new file mode 100644 index 0000000..a03d54a --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss @@ -0,0 +1,92 @@ +:host { + display: flex; + flex-direction: column; + height: 100%; + padding: 24px 28px; +} + +.toolbar { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 20px; + flex-shrink: 0; +} + +.toolbar-btn { + padding: 6px 14px; + background: transparent; + color: var(--accent-indigo); + border: 1px solid var(--accent-indigo); + border-radius: var(--radius-md); + font-size: 13px; + font-weight: 500; + cursor: pointer; + font-family: inherit; + + &:hover { + background: #EEF2FF; + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +.toolbar-title { + font-weight: 600; + font-size: 18px; + color: var(--text-primary); +} + +.spacer { + flex: 1; +} + +.detail-card { + background: var(--bg-surface); + border: 1px solid var(--border-color); + border-radius: var(--radius-xl); + max-width: 600px; +} + +.field { + display: flex; + flex-direction: column; + margin-bottom: 14px; +} + +.field-label { + font-size: 12px; + font-weight: 600; + color: #666; + margin-bottom: 4px; +} + +.field-input { + padding: 8px 10px; + border: 1px solid var(--border-muted); + border-radius: var(--radius-md); + font-size: 13px; + font-family: inherit; + outline: none; +} + +.field-textarea { + resize: vertical; + min-height: 70px; +} + +.error { + font-size: 13px; + color: #dc2626; + margin: 0 0 12px; +} + +.actions { + display: flex; + justify-content: flex-end; + gap: 8px; + margin-top: 8px; +} diff --git a/ui/winstudentgoaltracker/src/app/shared/services/student.service.ts b/ui/winstudentgoaltracker/src/app/shared/services/student.service.ts index a785fa5..6687bdd 100644 --- a/ui/winstudentgoaltracker/src/app/shared/services/student.service.ts +++ b/ui/winstudentgoaltracker/src/app/shared/services/student.service.ts @@ -1,6 +1,6 @@ import { HttpClient, HttpErrorResponse } from '@angular/common/http'; import { inject, Injectable, signal } from '@angular/core'; -import { firstValueFrom, Subject } from 'rxjs'; +import { firstValueFrom } from 'rxjs'; import { environment } from '../../../environments/environment'; import { ApiResult } from '../classes/api-result'; import { ResponseResult } from '../classes/auth.models'; @@ -31,10 +31,6 @@ export class StudentService { // Per-student full profile cache. private readonly profileCache = new Map(); - // Emits targeted label updates for sidebar nodes without a full rebuild. - private readonly _sidebarLabelUpdate = new Subject<{ routerLink: string[]; label: string }>(); - readonly sidebarLabelUpdate$ = this._sidebarLabelUpdate.asObservable(); - // ************************** Properties *************************** // ************************ Public Methods ************************* @@ -81,14 +77,6 @@ export class StudentService { } } - // ***************************************************************** - // Emits a targeted sidebar label update for a specific node, - // avoiding the full tree rebuild that notifyDataChanged triggers. - // ***************************************************************** - updateSidebarLabel(routerLink: string[], label: string) { - this._sidebarLabelUpdate.next({ routerLink, label }); - } - // ***************************************************************** // Returns student card summaries for the authenticated user. // When scope is 'all', returns all students in the program. diff --git a/ui/winstudentgoaltracker/src/app/shared/utils/format-date.ts b/ui/winstudentgoaltracker/src/app/shared/utils/format-date.ts new file mode 100644 index 0000000..4f7f0a5 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/shared/utils/format-date.ts @@ -0,0 +1,13 @@ +export function formatDate(d: string | Date | null): string { + if (!d) return ''; + const date = new Date(d); + return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); +} + +export function toIsoDateString(d: string | Date): string { + const date = new Date(d); + const y = date.getFullYear(); + const m = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${y}-${m}-${day}`; +}