This commit is contained in:
ivan-pelly
2026-03-11 18:51:12 -07:00
parent 015fb78136
commit ec36d8842d
13 changed files with 104 additions and 11 deletions
@@ -11,11 +11,18 @@
}
@if (displayMode() === 'card') {
<div class="card-grid">
@for (student of students(); track student.studentId) {
<app-student-card [student]="student" />
@if (loaded() && students().length === 0) {
<div class="empty-state">
<p>You don't have any students entered yet.</p>
<p>Click <strong>+ Add a Student</strong> in the upper right to get started.</p>
</div>
} @else {
<div class="card-grid">
@for (student of students(); track student.studentId) {
<app-student-card [student]="student" />
}
</div>
}
</div>
} @else {
<!-- List mode — to be implemented -->
<p>List view coming soon.</p>
@@ -45,4 +45,14 @@
gap: 1rem;
overflow-y: auto;
flex: 1;
}
.empty-state {
text-align: center;
padding: 3rem 1.5rem;
color: #555;
font-size: 0.9375rem;
background: #fff;
border-radius: 8px;
border: 1px solid #ddd;
}
@@ -27,6 +27,7 @@ export class StudentCardList {
protected readonly students = signal<StudentCardDto[]>([]);
protected readonly displayMode = signal<DisplayMode>('card');
protected readonly showAddModal = signal(false);
protected readonly loaded = signal(false);
public errorMessage = signal<String | null>(null);
@@ -73,6 +74,7 @@ export class StudentCardList {
this.students.set(this.sortByIdentifier(data.payload || []))
}
this.loaded.set(true);
});
}
}
@@ -2,8 +2,7 @@
<!-- Header -->
<header class="header">
<button class="menu-toggle" (click)="onToggleSidebar()"></button>
<span class="header-title">WinStudentGoalTracker</span>
<span class="spacer"></span>
<span class="header-title">{{ auth.schoolDistrictName() }} — {{ auth.programName() }}</span>
<button class="logout-btn" (click)="onLogout()">Log Out</button>
</header>
@@ -37,15 +37,13 @@
}
.header-title {
flex: 1;
text-align: center;
font-weight: 600;
font-size: 1rem;
color: #333;
}
.spacer {
flex: 1;
}
.logout-btn {
background: none;
border: 1px solid #ddd;
@@ -41,7 +41,7 @@ export class Home implements OnDestroy {
// ************************** Declarations *************************
private readonly auth = inject(Auth);
protected readonly auth = inject(Auth);
private readonly router = inject(Router);
private readonly studentService = inject(StudentService);
private readonly routeSub: Subscription;
@@ -33,6 +33,7 @@ export interface SelectProgramResponse {
userId: string;
email: string;
programName: string;
schoolDistrictName: string;
jwt: string;
refreshToken: string;
role: string;
@@ -34,6 +34,8 @@ export class Auth {
private readonly _sessionToken = signal<string | null>(this.loadSessionToken());
private readonly _programs = signal<UserProgramSummary[]>([]);
private readonly _isRefreshing = signal(false);
private readonly _programName = signal<string>('');
private readonly _schoolDistrictName = signal<string>('');
/** The currently authenticated user, parsed from the JWT. Null when logged out. */
readonly user = computed<AuthUser | null>(() => {
@@ -50,6 +52,12 @@ export class Auth {
/** Programs returned by phase 1 for the user to choose from. */
readonly programs = this._programs.asReadonly();
/** The name of the currently selected program. */
readonly programName = this._programName.asReadonly();
/** The name of the school district for the currently selected program. */
readonly schoolDistrictName = this._schoolDistrictName.asReadonly();
/** Emits when a token refresh fails and the user is forced to re-login. */
readonly sessionExpired$ = new Subject<void>();
@@ -187,6 +195,10 @@ export class Auth {
private handleFullAuth(data: SelectProgramResponse): void {
this.storeTokens(data.jwt, data.refreshToken);
// Store program context for header display
this._programName.set(data.programName);
this._schoolDistrictName.set(data.schoolDistrictName);
// Clear phase-1 artefacts
localStorage.removeItem(STORAGE_KEYS.SESSION_TOKEN);
this._sessionToken.set(null);
@@ -229,6 +241,8 @@ export class Auth {
this._sessionToken.set(null);
this._programs.set([]);
this._isRefreshing.set(false);
this._programName.set('');
this._schoolDistrictName.set('');
}
private loadSessionToken(): string | null {