diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/confirm-modal/confirm-modal.scss b/ui/winstudentgoaltracker/src/app/desktop/components/confirm-modal/confirm-modal.scss index a25ac09..d09ab0c 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/confirm-modal/confirm-modal.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/confirm-modal/confirm-modal.scss @@ -6,7 +6,7 @@ } .second-confirm-message { - color: #DC2626; + color: var(--danger); font-weight: 600; } @@ -14,29 +14,29 @@ padding: 8px 20px; border-radius: var(--radius-md); border: none; - background: #DC2626; + background: var(--danger); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; &:hover { - background: #B91C1C; + background: var(--danger-dark); } } :host ::ng-deep .btn-danger-confirm { padding: 8px 20px; border-radius: var(--radius-md); - border: 2px solid #DC2626; - background: #FEF2F2; - color: #DC2626; + border: 2px solid var(--danger); + background: var(--danger-bg-light); + color: var(--danger); font-size: 13px; font-weight: 600; cursor: pointer; &:hover { - background: #DC2626; + background: var(--danger); color: #fff; } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.scss b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.scss index c8e1b1c..8ab4c66 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/edit-benchmark-modal/edit-benchmark-modal.scss @@ -6,16 +6,16 @@ .btn-ai { padding: 7px 14px; border-radius: var(--radius-md); - border: 1px solid #c4b5fd; - background: #f5f3ff; - color: #6d28d9; + border: 1px solid var(--accent-purple-border); + background: var(--accent-purple-bg); + color: var(--accent-purple); font-size: 13px; font-weight: 600; cursor: pointer; transition: background 0.15s; &:hover:not(:disabled) { - background: #ede9fe; + background: var(--accent-purple-hover); } &:disabled { @@ -26,7 +26,7 @@ .recommend-error { font-size: 12px; - color: #dc2626; + color: var(--danger); margin: 6px 0 0; } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/edit-event-modal/edit-event-modal.scss b/ui/winstudentgoaltracker/src/app/desktop/components/edit-event-modal/edit-event-modal.scss index 593aa21..758282d 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/edit-event-modal/edit-event-modal.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/edit-event-modal/edit-event-modal.scss @@ -8,9 +8,9 @@ padding: 4px 10px; border-radius: 5px; font-size: 12px; - border: 1.5px solid #D5D5D0; - background: #FFF; - color: #666; + border: 1.5px solid var(--border-muted); + background: var(--bg-surface); + color: var(--text-secondary); cursor: pointer; font-family: inherit; transition: all 0.15s ease; diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/modal-shell/modal-shell.scss b/ui/winstudentgoaltracker/src/app/desktop/components/modal-shell/modal-shell.scss index 00d310e..3e63bb8 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/modal-shell/modal-shell.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/modal-shell/modal-shell.scss @@ -64,7 +64,7 @@ display: block; font-size: 12px; font-weight: 600; - color: #666; + color: var(--text-secondary); margin-bottom: 4px; } @@ -102,7 +102,7 @@ cursor: pointer; &:hover:not(:disabled) { - background: #3730A3; + background: var(--accent-indigo-dark); } &:disabled { @@ -122,13 +122,13 @@ cursor: pointer; &:hover { - background: #e5e5e0; + background: var(--border-color); } } .error { font-size: 13px; - color: #dc2626; + color: var(--danger); margin: 0 0 8px; } } 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 3f238d6..0dc94c3 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 @@ -93,6 +93,6 @@ min-width: 6rem; &:hover { - background: #3730A3 !important; + background: var(--accent-indigo-dark) !important; } } diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.html b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.html new file mode 100644 index 0000000..c24df0a --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.html @@ -0,0 +1,13 @@ +
+ @for (t of toast.toasts(); track t.id) { +
+ {{ t.message }} + +
+ } +
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.scss b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.scss new file mode 100644 index 0000000..cda8495 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.scss @@ -0,0 +1,75 @@ +.toast-stack { + position: fixed; + top: 16px; + right: 16px; + z-index: 9999; + display: flex; + flex-direction: column; + gap: 8px; + pointer-events: none; +} + +.toast { + display: flex; + align-items: center; + gap: 10px; + padding: 9px 12px; + border-radius: var(--radius-lg); + font-size: 13px; + font-weight: 500; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); + pointer-events: all; + animation: toast-in 0.15s ease; + min-width: 180px; + max-width: 300px; + + &--success { + background: var(--bg-surface); + border: 1px solid var(--border-color); + color: var(--text-primary); + } + + &--error { + background: var(--danger-bg-light); + border: 1px solid var(--danger); + color: var(--danger); + } + + &--info { + background: var(--accent-indigo-bg); + border: 1px solid var(--accent-indigo-border-light); + color: var(--accent-indigo); + } +} + +.toast-msg { + flex: 1; +} + +.toast-close { + background: none; + border: none; + padding: 2px; + cursor: pointer; + color: inherit; + opacity: 0.45; + display: flex; + align-items: center; + flex-shrink: 0; + border-radius: var(--radius-sm); + + &:hover { + opacity: 1; + } +} + +@keyframes toast-in { + from { + opacity: 0; + transform: translateX(8px); + } + to { + opacity: 1; + transform: translateX(0); + } +} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.ts b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.ts new file mode 100644 index 0000000..49e5208 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/desktop/components/toast-host/toast-host.ts @@ -0,0 +1,11 @@ +import { Component, inject } from '@angular/core'; +import { ToastService } from '../../../shared/services/toast.service'; + +@Component({ + selector: 'app-toast-host', + templateUrl: './toast-host.html', + styleUrl: './toast-host.scss', +}) +export class ToastHost { + protected readonly toast = inject(ToastService); +} diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html index 4e5f6b0..eeb8f2d 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.html @@ -67,13 +67,14 @@

{{ student()!.identifier }}

IEP {{ formatDate(student()!.nextIepDate) }} -
diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss index f34f998..a530446 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.scss @@ -46,20 +46,24 @@ .delete-student-btn { display: flex; align-items: center; - justify-content: center; + gap: 6px; margin-left: auto; align-self: center; - padding: 4px; - border: none; + padding: 6px 12px; + border: 1px solid var(--border-muted); background: none; - color: var(--text-faint); + color: var(--text-muted); + font-size: 12px; + font-weight: 500; + font-family: inherit; cursor: pointer; - border-radius: var(--radius-sm); - transition: color var(--transition-fast), background var(--transition-fast); + border-radius: var(--radius-md); + transition: color var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast); &:hover { - color: #DC2626; - background: #FEE2E2; + color: var(--danger); + background: var(--danger-bg); + border-color: var(--danger); } } @@ -76,7 +80,7 @@ border-radius: var(--radius-md); border: 1.5px solid var(--border-color); background: var(--bg-surface); - color: #666; + color: var(--text-secondary); font-weight: 400; font-size: 13px; cursor: pointer; @@ -85,9 +89,9 @@ &.active { font-weight: 600; - border-color: #818CF8; - background: #EEF2FF; - color: #4338CA; + border-color: var(--accent-indigo-border); + background: var(--accent-indigo-bg); + color: var(--accent-indigo); } &.add-goal { @@ -127,8 +131,8 @@ letter-spacing: 0.05em; padding: 3px 8px; border-radius: var(--radius-sm); - color: #4338CA; - background: #EEF2FF; + color: var(--accent-indigo); + background: var(--accent-indigo-bg); } .goal-due { @@ -150,8 +154,8 @@ transition: color var(--transition-fast), background var(--transition-fast); &:hover { - color: #DC2626; - background: #FEE2E2; + color: var(--danger); + background: var(--danger-bg); } } @@ -159,7 +163,7 @@ font-size: 15px; line-height: 1.55; margin: 0; - color: #333; + color: var(--text-primary); } /* ─── Sub Tabs ─── */ @@ -185,8 +189,8 @@ &.active { font-weight: 600; - color: #4338CA; - border-bottom-color: #6366F1; + color: var(--accent-indigo); + border-bottom-color: var(--accent-indigo-light); } } @@ -215,7 +219,7 @@ .benchmark-name { font-weight: 600; font-size: 14px; - color: #4338CA; + color: var(--accent-indigo); } .benchmark-events { @@ -245,8 +249,8 @@ transition: color var(--transition-fast), background var(--transition-fast); &:hover { - color: #DC2626; - background: #FEE2E2; + color: var(--danger); + background: var(--danger-bg); } } @@ -278,8 +282,8 @@ width: 12px; height: 12px; border-radius: 50%; - border: 2px solid #818CF8; - background: #EEF2FF; + border: 2px solid var(--accent-indigo-border); + background: var(--accent-indigo-bg); } .event-card { @@ -305,7 +309,7 @@ font-size: 13px; line-height: 1.55; margin: 0; - color: #333; + color: var(--text-primary); } .delete-event-btn { @@ -322,8 +326,8 @@ transition: color var(--transition-fast), background var(--transition-fast); &:hover { - color: #DC2626; - background: #FEE2E2; + color: var(--danger); + background: var(--danger-bg); } } @@ -337,11 +341,11 @@ .benchmark-tag { font-size: 11px; font-weight: 500; - color: #4338CA; - background: #EEF2FF; + color: var(--accent-indigo); + background: var(--accent-indigo-bg); padding: 3px 8px; border-radius: var(--radius-sm); - border: 1px solid #C7D2FE; + border: 1px solid var(--accent-indigo-border-light); } /* ─── Add Buttons ─── */ diff --git a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts index ce28a65..dacde44 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/components/workspace/workspace.ts @@ -10,6 +10,7 @@ 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 { ConfirmModal } from '../confirm-modal/confirm-modal'; +import { ToastService } from '../../../shared/services/toast.service'; import { formatDate } from '../../../shared/utils/format-date'; type TabView = 'benchmarks' | 'progress'; @@ -66,6 +67,7 @@ export class Workspace { private readonly studentService = inject(StudentService); private readonly route = inject(ActivatedRoute); private readonly router = inject(Router); + private readonly toast = inject(ToastService); protected readonly studentId = signal(null); protected readonly student = signal(null); @@ -136,6 +138,7 @@ export class Workspace { onGoalSaved() { this.showGoalModal.set(null); this.refetchProfile(); + this.toast.show('Goal updated'); } onAddGoal() { @@ -162,6 +165,7 @@ export class Workspace { this.selectedGoalId.set(null); this.studentService.notifyDataChanged(); await this.refetchProfile(); + this.toast.show('Goal deleted'); } onGoalCreated(goal: StudentGoalItem) { @@ -170,6 +174,7 @@ export class Workspace { this.refetchProfile().then(() => { this.selectedGoalId.set(goal.goalId); }); + this.toast.show('Goal added'); } onEditBenchmark(b: BenchmarkDto) { @@ -179,6 +184,7 @@ export class Workspace { onEditBenchmarkSaved() { this.showEditBenchmarkModal.set(null); this.refetchProfile(); + this.toast.show('Benchmark saved'); } onAddBenchmark() { @@ -204,6 +210,7 @@ export class Workspace { if (!result.success) return; await this.refetchProfile(); + this.toast.show('Benchmark deleted'); } onNewEvent() { @@ -217,6 +224,7 @@ export class Workspace { onEventSaved() { this.showEditEventModal.set(null); this.refetchProfile(); + this.toast.show('Progress event saved'); } onDeleteEvent(ev: ProgressEventWithGoalDto) { @@ -238,6 +246,7 @@ export class Workspace { if (!result.success) return; await this.refetchProfile(); + this.toast.show('Progress event deleted'); } // ***************************************************************** @@ -274,6 +283,7 @@ export class Workspace { if (!result.success) return; this.studentService.notifyDataChanged(); + this.toast.show('Student deleted'); this.router.navigate(['/']); } diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html index 3264b52..d8f6bd0 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.html @@ -1,3 +1,4 @@ +
@if (showStudentModal()) { diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss index efd0f84..7a2c9ce 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.scss @@ -68,7 +68,7 @@ align-items: center; width: 30px; height: 16px; - background: #d1d5db; + background: var(--toggle-inactive); border-radius: 8px; padding: 2px; transition: background var(--transition-normal); @@ -102,11 +102,11 @@ font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; - color: #4338CA; - background: #EEF2FF; + color: var(--accent-indigo); + background: var(--accent-indigo-bg); padding: 6px 12px; margin-top: 6px; - border-left: 3px solid #818CF8; + border-left: 3px solid var(--accent-indigo-border); } .student-item { diff --git a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts index cdd8fef..c3de319 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts +++ b/ui/winstudentgoaltracker/src/app/desktop/pages/home/home.ts @@ -5,11 +5,13 @@ import { StudentService } from '../../../shared/services/student.service'; import { StudentCardDto } from '../../../shared/classes/student-card.dto'; import { StudentModal } from '../../components/student-modal/student-modal'; import { EditIcon } from '../../components/edit-icon/edit-icon'; +import { ToastHost } from '../../components/toast-host/toast-host'; +import { ToastService } from '../../../shared/services/toast.service'; import { formatDate } from '../../../shared/utils/format-date'; @Component({ selector: 'app-home', - imports: [RouterOutlet, RouterLink, StudentModal, EditIcon], + imports: [RouterOutlet, RouterLink, StudentModal, EditIcon, ToastHost], templateUrl: './home.html', styleUrl: './home.scss', }) @@ -36,6 +38,7 @@ export class Home { protected readonly auth = inject(Auth); private readonly router = inject(Router); private readonly studentService = inject(StudentService); + private readonly toast = inject(ToastService); protected readonly students = signal([]); protected readonly selectedStudentId = signal(null); @@ -96,6 +99,7 @@ export class Home { this.studentService.notifyDataChanged(); this.selectedStudentId.set(student.studentId); this.router.navigate(['/students', student.studentId]); + this.toast.show('Student added'); } onEditStudent(student: StudentCardDto, event: Event) { @@ -106,6 +110,7 @@ export class Home { onStudentSaved() { this.showStudentModal.set(null); this.loadStudents(); + this.toast.show('Student updated'); } onLogout() { diff --git a/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss b/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss index a03d54a..74bbce6 100644 --- a/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss +++ b/ui/winstudentgoaltracker/src/app/desktop/styles/_detail-page.scss @@ -25,7 +25,7 @@ font-family: inherit; &:hover { - background: #EEF2FF; + background: var(--accent-indigo-bg); } &:disabled { @@ -60,7 +60,7 @@ .field-label { font-size: 12px; font-weight: 600; - color: #666; + color: var(--text-secondary); margin-bottom: 4px; } @@ -80,7 +80,7 @@ .error { font-size: 13px; - color: #dc2626; + color: var(--danger); margin: 0 0 12px; } diff --git a/ui/winstudentgoaltracker/src/app/shared/services/toast.service.ts b/ui/winstudentgoaltracker/src/app/shared/services/toast.service.ts new file mode 100644 index 0000000..6092ed6 --- /dev/null +++ b/ui/winstudentgoaltracker/src/app/shared/services/toast.service.ts @@ -0,0 +1,25 @@ +import { Injectable, signal } from '@angular/core'; + +export type ToastType = 'success' | 'error' | 'info'; + +export interface Toast { + id: number; + message: string; + type: ToastType; +} + +@Injectable({ providedIn: 'root' }) +export class ToastService { + private nextId = 0; + readonly toasts = signal([]); + + show(message: string, type: ToastType = 'success') { + const id = ++this.nextId; + this.toasts.update(list => [...list, { id, message, type }]); + setTimeout(() => this.dismiss(id), 3000); + } + + dismiss(id: number) { + this.toasts.update(list => list.filter(t => t.id !== id)); + } +} diff --git a/ui/winstudentgoaltracker/src/styles.scss b/ui/winstudentgoaltracker/src/styles.scss index 967d081..bb39a90 100644 --- a/ui/winstudentgoaltracker/src/styles.scss +++ b/ui/winstudentgoaltracker/src/styles.scss @@ -15,6 +15,19 @@ --text-dim: #bbb; --accent-indigo: #4338CA; --accent-indigo-light: #6366F1; + --accent-indigo-dark: #3730A3; + --accent-indigo-bg: #EEF2FF; + --accent-indigo-border: #818CF8; + --accent-indigo-border-light: #C7D2FE; + --accent-purple: #6d28d9; + --accent-purple-bg: #f5f3ff; + --accent-purple-border: #c4b5fd; + --accent-purple-hover: #ede9fe; + --danger: #DC2626; + --danger-dark: #B91C1C; + --danger-bg: #FEE2E2; + --danger-bg-light: #FEF2F2; + --toggle-inactive: #d1d5db; --radius-sm: 4px; --radius-md: 6px; --radius-lg: 8px;