visual cleanup

This commit is contained in:
2026-04-08 16:13:01 -07:00
parent e2834c1d5b
commit e77c867beb
5 changed files with 61 additions and 9 deletions
@@ -29,13 +29,13 @@
<span class="student-iep">IEP {{ formatDate(student()!.nextIepDate) }}</span> <span class="student-iep">IEP {{ formatDate(student()!.nextIepDate) }}</span>
</div> </div>
<div class="goal-tabs"> <div class="goal-tabs">
<button class="goal-tab add-goal" (click)="onAddGoal()">+ Goal</button>
@for (g of goals(); track g.goalId) { @for (g of goals(); track g.goalId) {
<button class="goal-tab" [class.active]="selectedGoalId() === g.goalId || (selectedGoal()?.goalId === g.goalId)" <button class="goal-tab" [class.active]="selectedGoalId() === g.goalId || (selectedGoal()?.goalId === g.goalId)"
(click)="onSelectGoal(g.goalId)"> (click)="onSelectGoal(g.goalId)">
{{ g.category }} {{ g.category }}
</button> </button>
} }
<button class="goal-tab add-goal" (click)="onAddGoal()">+ Goal</button>
</div> </div>
</div> </div>
@@ -46,15 +46,15 @@
<div class="goal-card"> <div class="goal-card">
<div class="goal-card-header"> <div class="goal-card-header">
<span class="goal-badge">{{ selectedGoal()!.category }} Goal</span> <span class="goal-badge">{{ selectedGoal()!.category }} Goal</span>
@if (selectedGoal()!.targetCompletionDate) {
<span class="goal-due">Due {{ formatDate(selectedGoal()!.targetCompletionDate) }}</span>
}
<button class="edit-icon" (click)="onEditGoal()" aria-label="Edit goal"> <button class="edit-icon" (click)="onEditGoal()" aria-label="Edit goal">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#999" stroke-width="1.5" <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="#999" stroke-width="1.5"
stroke-linecap="round" stroke-linejoin="round"> stroke-linecap="round" stroke-linejoin="round">
<path d="M11.5 1.5l3 3L5 14H2v-3L11.5 1.5z" /> <path d="M11.5 1.5l3 3L5 14H2v-3L11.5 1.5z" />
</svg> </svg>
</button> </button>
@if (selectedGoal()!.targetCompletionDate) {
<span class="goal-due">Due {{ formatDate(selectedGoal()!.targetCompletionDate) }}</span>
}
</div> </div>
<p class="goal-description">{{ selectedGoal()!.description }}</p> <p class="goal-description">{{ selectedGoal()!.description }}</p>
</div> </div>
@@ -28,6 +28,7 @@ export class Workspace {
const goalId = params.get('goalId'); const goalId = params.get('goalId');
if (studentId && studentId !== this.studentId()) { if (studentId && studentId !== this.studentId()) {
this.selectedGoalId.set(null);
this.studentId.set(studentId); this.studentId.set(studentId);
this.loadStudentData(studentId); this.loadStudentData(studentId);
} else if (!studentId) { } else if (!studentId) {
@@ -28,7 +28,11 @@
</div> </div>
<div class="student-list"> <div class="student-list">
@for (s of students(); track s.studentId) { @for (group of groupedStudents(); track group.label) {
@if (group.label) {
<div class="group-header">{{ group.label }}</div>
}
@for (s of group.students; track s.studentId) {
<div class="student-item" [class.active]="selectedStudentId() === s.studentId" <div class="student-item" [class.active]="selectedStudentId() === s.studentId"
(click)="onSelectStudent(s)"> (click)="onSelectStudent(s)">
<div class="student-item-info"> <div class="student-item-info">
@@ -37,9 +41,6 @@
</div> </div>
<div class="student-item-meta"> <div class="student-item-meta">
IEP: {{ formatDate(s.nextIepDate) }} IEP: {{ formatDate(s.nextIepDate) }}
@if (showAll() && s.ownerName) {
<span class="owner-tag">· {{ s.ownerName }}</span>
}
</div> </div>
</div> </div>
<button class="edit-pencil" (click)="onEditStudent(s, $event)" aria-label="Edit student"> <button class="edit-pencil" (click)="onEditStudent(s, $event)" aria-label="Edit student">
@@ -50,6 +51,7 @@
</button> </button>
</div> </div>
} }
}
</div> </div>
<div class="sidebar-footer"> <div class="sidebar-footer">
@@ -97,6 +97,22 @@
padding: 0 8px 8px; padding: 0 8px 8px;
} }
.group-header {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.04em;
color: var(--text-faint);
padding: 12px 12px 4px;
margin-top: 4px;
border-top: 1px solid var(--border-color);
&:first-child {
border-top: none;
margin-top: 0;
}
}
.student-item { .student-item {
padding: 10px 12px; padding: 10px 12px;
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
@@ -1,4 +1,4 @@
import { Component, effect, inject, signal } from '@angular/core'; import { Component, computed, effect, inject, signal } from '@angular/core';
import { RouterLink, RouterOutlet, Router } from '@angular/router'; import { RouterLink, RouterOutlet, Router } from '@angular/router';
import { Auth } from '../../../shared/services/auth'; import { Auth } from '../../../shared/services/auth';
import { StudentService } from '../../../shared/services/student.service'; import { StudentService } from '../../../shared/services/student.service';
@@ -42,6 +42,39 @@ export class Home {
protected readonly showAddStudentModal = signal(false); protected readonly showAddStudentModal = signal(false);
protected readonly editingStudent = signal<StudentCardDto | null>(null); protected readonly editingStudent = signal<StudentCardDto | null>(null);
// Groups students by owner when "All" is active.
protected readonly groupedStudents = computed(() => {
const all = this.students();
if (!this.showAll()) {
return [{ label: null, students: all }];
}
const mine = all.filter(s => s.isMine);
const others = all.filter(s => !s.isMine);
// Group others by ownerName.
const byOwner = new Map<string, StudentCardDto[]>();
for (const s of others) {
const key = s.ownerName || 'Unknown';
const list = byOwner.get(key) || [];
list.push(s);
byOwner.set(key, list);
}
const groups: { label: string | null; students: StudentCardDto[] }[] = [];
if (mine.length) {
groups.push({ label: 'My Students', students: mine });
}
// Sort other teachers alphabetically.
const sortedKeys = [...byOwner.keys()].sort((a, b) =>
a.localeCompare(b, undefined, { sensitivity: 'base' })
);
for (const key of sortedKeys) {
groups.push({ label: key, students: byOwner.get(key)! });
}
return groups;
});
// ************************ Event Handlers ************************* // ************************ Event Handlers *************************
onSelectStudent(student: StudentCardDto) { onSelectStudent(student: StudentCardDto) {