diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts
index c4ee4ba..f3e8766 100644
--- a/src/frontend/src/app/app.component.ts
+++ b/src/frontend/src/app/app.component.ts
@@ -28,9 +28,23 @@ import { ConnectivityOverlayComponent } from '@shared/index';
Estudiantes
Si proporcionas nombre y email, se creara tu perfil de estudiante automaticamente.
-
@if (serverError()) {
error
@@ -265,8 +279,8 @@ export class RegisterComponent {
form = this.fb.group({
username: ['', [Validators.required, Validators.minLength(3)]],
password: ['', [Validators.required, Validators.minLength(6)]],
- name: [''],
- email: ['', Validators.email],
+ name: ['', [Validators.required, Validators.minLength(3)]],
+ email: ['', [Validators.required, Validators.email]],
});
loading = signal(false);
@@ -291,14 +305,14 @@ export class RegisterComponent {
this.authService.register(
username!,
password!,
- name || undefined,
- email || undefined
+ name!,
+ email!
).subscribe({
next: (response) => {
this.loading.set(false);
if (response.success) {
this.notification.success('Cuenta creada exitosamente!');
- this.router.navigate(['/students']);
+ this.router.navigate(['/dashboard']);
} else {
this.serverError.set(response.error ?? 'Error al crear la cuenta');
}
diff --git a/src/frontend/src/app/features/dashboard/pages/student-dashboard/student-dashboard.component.ts b/src/frontend/src/app/features/dashboard/pages/student-dashboard/student-dashboard.component.ts
new file mode 100644
index 0000000..507d85d
--- /dev/null
+++ b/src/frontend/src/app/features/dashboard/pages/student-dashboard/student-dashboard.component.ts
@@ -0,0 +1,488 @@
+import { Component, inject, signal, computed, OnInit } from '@angular/core';
+import { RouterLink } from '@angular/router';
+import { Apollo } from 'apollo-angular';
+import { MatIconModule } from '@angular/material/icon';
+import { MatButtonModule } from '@angular/material/button';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
+import { AuthService } from '@core/services/auth.service';
+import { GET_STUDENT } from '@core/graphql/queries/students.queries';
+
+interface Enrollment {
+ id: number;
+ subjectId: number;
+ subjectName: string;
+ credits: number;
+ professorName: string;
+ enrolledAt: string;
+}
+
+interface Student {
+ id: number;
+ name: string;
+ email: string;
+ totalCredits: number;
+ enrollments: Enrollment[];
+}
+
+@Component({
+ selector: 'app-student-dashboard',
+ standalone: true,
+ imports: [RouterLink, MatIconModule, MatButtonModule, MatProgressSpinnerModule],
+ template: `
+
+ @if (loading()) {
+
+
+
Cargando tu informacion...
+
+ } @else if (error()) {
+
+
error_outline
+
Error al cargar datos
+
{{ error() }}
+
+
+ } @else if (student()) {
+
+
+
+
+
+
+
+ @if (student()!.enrollments.length === 0) {
+
+ } @else {
+
+
+ @if (canEnrollMore()) {
+
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
book
+
+ 10 materias
+ disponibles en total
+
+
+
+
stars
+
+ 3 creditos
+ por cada materia
+
+
+
+
check_circle
+
+ Maximo 3 materias
+ puedes inscribir (9 creditos)
+
+
+
+
person
+
+ 5 profesores
+ cada uno imparte 2 materias
+
+
+
+
warning
+
+ Restriccion
+ No puedes tener 2 materias con el mismo profesor
+
+
+
+
+
+ }
+
+ `,
+ styles: [`
+ .dashboard {
+ max-width: 1000px;
+ margin: 0 auto;
+ }
+
+ .loading-container, .error-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 400px;
+ gap: 1rem;
+ text-align: center;
+ }
+
+ .error-container mat-icon {
+ font-size: 64px;
+ width: 64px;
+ height: 64px;
+ color: var(--error);
+ }
+
+ .dashboard-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 2rem;
+ padding: 1.5rem;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 1rem;
+ color: white;
+ }
+
+ .welcome h1 {
+ margin: 0 0 0.25rem;
+ font-size: 1.5rem;
+ font-weight: 600;
+ }
+
+ .welcome .email {
+ margin: 0;
+ opacity: 0.9;
+ font-size: 0.875rem;
+ }
+
+ .credits-badge {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ background: rgba(255, 255, 255, 0.2);
+ padding: 1rem 1.5rem;
+ border-radius: 0.75rem;
+ }
+
+ .credits-value {
+ font-size: 2rem;
+ font-weight: 700;
+ }
+
+ .credits-label {
+ font-size: 0.75rem;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ opacity: 0.9;
+ }
+
+ .dashboard-grid {
+ display: grid;
+ gap: 1.5rem;
+ grid-template-columns: 1fr;
+ }
+
+ @media (min-width: 768px) {
+ .dashboard-grid {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ .enrollments-card {
+ grid-row: span 2;
+ }
+ }
+
+ .card {
+ background: white;
+ border-radius: 1rem;
+ padding: 1.5rem;
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+ }
+
+ .card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+ }
+
+ .card-header h2 {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin: 0;
+ font-size: 1.125rem;
+ font-weight: 600;
+ }
+
+ .card-header mat-icon {
+ color: var(--accent);
+ }
+
+ .count-badge {
+ background: var(--bg-secondary);
+ padding: 0.25rem 0.75rem;
+ border-radius: 9999px;
+ font-size: 0.875rem;
+ font-weight: 500;
+ }
+
+ .empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 2rem;
+ text-align: center;
+ color: var(--text-secondary);
+ }
+
+ .empty-state mat-icon {
+ font-size: 48px;
+ width: 48px;
+ height: 48px;
+ margin-bottom: 1rem;
+ opacity: 0.5;
+ }
+
+ .empty-state p {
+ margin: 0 0 1rem;
+ }
+
+ .enrollments-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ }
+
+ .enrollment-item {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1rem;
+ border-radius: 0.5rem;
+ background: var(--bg-secondary);
+ margin-bottom: 0.5rem;
+ }
+
+ .subject-info {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ }
+
+ .subject-name {
+ font-weight: 500;
+ }
+
+ .professor {
+ font-size: 0.8125rem;
+ color: var(--text-secondary);
+ }
+
+ .credits {
+ font-size: 0.8125rem;
+ color: var(--text-secondary);
+ background: white;
+ padding: 0.25rem 0.75rem;
+ border-radius: 9999px;
+ }
+
+ .card-actions {
+ margin-top: 1rem;
+ padding-top: 1rem;
+ border-top: 1px solid var(--border-light);
+ }
+
+ .actions-grid {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .action-btn {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 1rem;
+ background: var(--bg-secondary);
+ border-radius: 0.75rem;
+ text-decoration: none;
+ color: inherit;
+ transition: all 0.2s;
+ }
+
+ .action-btn:hover {
+ background: var(--border-light);
+ transform: translateX(4px);
+ }
+
+ .action-btn mat-icon {
+ color: var(--accent);
+ }
+
+ .action-btn span {
+ font-weight: 500;
+ }
+
+ .action-btn small {
+ display: block;
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ font-weight: normal;
+ }
+
+ .info-content {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ }
+
+ .info-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem;
+ background: var(--bg-secondary);
+ border-radius: 0.5rem;
+ }
+
+ .info-item mat-icon {
+ color: var(--accent);
+ }
+
+ .info-item div {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .info-item strong {
+ font-size: 0.875rem;
+ }
+
+ .info-item span {
+ font-size: 0.75rem;
+ color: var(--text-secondary);
+ }
+
+ .info-item.warning {
+ background: rgba(245, 158, 11, 0.1);
+ }
+
+ .info-item.warning mat-icon {
+ color: #f59e0b;
+ }
+ `]
+})
+export class StudentDashboardComponent implements OnInit {
+ private apollo = inject(Apollo);
+ private authService = inject(AuthService);
+
+ student = signal
(null);
+ loading = signal(true);
+ error = signal(null);
+
+ studentId = computed(() => this.authService.studentId());
+ canEnrollMore = computed(() => (this.student()?.enrollments.length ?? 0) < 3);
+
+ ngOnInit(): void {
+ this.loadStudentData();
+ }
+
+ loadStudentData(): void {
+ const id = this.studentId();
+ if (!id) {
+ this.error.set('No se encontro tu perfil de estudiante');
+ this.loading.set(false);
+ return;
+ }
+
+ this.loading.set(true);
+ this.error.set(null);
+
+ this.apollo
+ .query<{ student: Student }>({
+ query: GET_STUDENT,
+ variables: { id },
+ fetchPolicy: 'network-only',
+ })
+ .subscribe({
+ next: (result) => {
+ if (result.data?.student) {
+ this.student.set(result.data.student);
+ }
+ this.loading.set(false);
+ },
+ error: (err) => {
+ this.error.set(err.message || 'Error al cargar datos');
+ this.loading.set(false);
+ },
+ });
+ }
+}