import { css, html, LitElement } from "lit"; import { customElement, state } from "lit/decorators.js"; import "./class-registration-modal"; interface Class { id: string; course_code: string; name: string; professor: string; semester: string; year: number; archived: boolean; } interface ClassesGrouped { [semesterYear: string]: Class[]; } @customElement("classes-overview") export class ClassesOverview extends LitElement { @state() classes: ClassesGrouped = {}; @state() isLoading = true; @state() error: string | null = null; @state() showRegistrationModal = false; static override styles = css` :host { display: block; } h1 { color: var(--text); margin-bottom: 2rem; } .semester-section { margin-bottom: 3rem; } .semester-title { font-size: 1.5rem; font-weight: 600; color: var(--primary); margin-bottom: 1.5rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--secondary); } .classes-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); gap: 1.5rem; } .class-card { background: var(--background); border: 1px solid var(--secondary); border-radius: 8px; padding: 1.5rem; cursor: pointer; transition: all 0.2s; text-decoration: none; color: var(--text); display: block; position: relative; } .class-card:hover { border-color: var(--accent); transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); } .class-card.archived { opacity: 0.6; border-style: dashed; } .course-code { font-size: 0.875rem; font-weight: 600; color: var(--accent); text-transform: uppercase; margin-bottom: 0.5rem; } .class-name { font-size: 1.125rem; font-weight: 600; margin-bottom: 0.5rem; color: var(--text); } .professor { font-size: 0.875rem; color: var(--paynes-gray); margin-bottom: 0.25rem; } .archived-badge { position: absolute; top: 0.75rem; right: 0.75rem; background: var(--paynes-gray); color: var(--white); padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; text-transform: uppercase; } .register-card { background: color-mix(in srgb, var(--accent) 10%, transparent); border: 2px dashed var(--accent); border-radius: 8px; padding: 1.5rem; cursor: pointer; transition: all 0.2s; display: flex; flex-direction: column; align-items: center; justify-content: center; color: var(--accent); } .register-card:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); transform: translateY(-2px); } .register-icon { font-size: 3rem; margin-bottom: 0.5rem; } .register-text { font-weight: 600; font-size: 1rem; } .empty-state { text-align: center; padding: 4rem 2rem; color: var(--paynes-gray); } .empty-state h2 { color: var(--text); margin-bottom: 1rem; } .empty-state button { margin-top: 2rem; padding: 0.75rem 2rem; background: var(--accent); color: var(--white); border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.2s; } .empty-state button:hover { background: color-mix(in srgb, var(--accent) 90%, black); transform: translateY(-2px); } .loading { text-align: center; padding: 4rem 2rem; color: var(--paynes-gray); } .error { background: color-mix(in srgb, red 10%, transparent); border: 1px solid red; color: red; padding: 1rem; border-radius: 4px; margin-bottom: 2rem; } `; override async connectedCallback() { super.connectedCallback(); await this.loadClasses(); window.addEventListener("auth-changed", this.handleAuthChange); } override disconnectedCallback() { super.disconnectedCallback(); window.removeEventListener("auth-changed", this.handleAuthChange); } private handleAuthChange = async () => { await this.loadClasses(); }; private async loadClasses() { this.isLoading = true; this.error = null; try { const response = await fetch("/api/classes"); if (!response.ok) { if (response.status === 401) { this.classes = {}; return; } throw new Error("Failed to load classes"); } const data = await response.json(); this.classes = data.classes || {}; } catch (error) { console.error("Failed to load classes:", error); this.error = "Failed to load classes. Please try again."; } finally { this.isLoading = false; } } private handleRegisterClick() { this.showRegistrationModal = true; } private handleModalClose() { this.showRegistrationModal = false; } private async handleClassJoined() { this.showRegistrationModal = false; await this.loadClasses(); } override render() { if (this.isLoading) { return html`
Loading classes...
`; } if (this.error) { return html`
${this.error}
`; } const semesterKeys = Object.keys(this.classes); const hasClasses = semesterKeys.length > 0; return html`

Your Classes

${ hasClasses ? html` ${semesterKeys.map( (semesterYear) => html`

${semesterYear}

${this.classes[semesterYear]?.map( (cls) => html` ${cls.archived ? html`
Archived
` : ""}
${cls.course_code}
${cls.name}
${cls.professor}
`, )} ${ semesterKeys.indexOf(semesterYear) === 0 ? html`
+
Register for a Class
` : "" }
`, )} ` : html`

No classes yet

You haven't been enrolled in any classes.

` } `; } }