Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion frontend/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Routes } from '@angular/router';
import { RegisterComponent } from './auth/register/register.component';
import { LoginComponent } from './auth/login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';

export const routes: Routes = [
{ path: 'register', component: RegisterComponent },
{ path: 'login', component: LoginComponent },
{ path: '', redirectTo: 'register', pathMatch: 'full' }
{ path: 'dashboard', component: DashboardComponent },
{ path: '', redirectTo: 'login', pathMatch: 'full' }, // default to login
{ path: '**', redirectTo: 'login' } // fallback route
];
13 changes: 12 additions & 1 deletion frontend/src/app/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ interface LoginDto {

interface LoginResponse {
access_token: string;
user: { id: number; username: string; role: string };
}

@Injectable({
providedIn: 'root',
})
export class AuthService {
private apiUrl = 'http://localhost:3000/api/auth';
private _currentUser: { id: number; username: string; role: string } | null = null;

constructor(private http: HttpClient) {}

Expand All @@ -33,14 +35,15 @@ export class AuthService {
login(dto: LoginDto): Observable<LoginResponse> {
return this.http.post<LoginResponse>(`${this.apiUrl}/login`, dto).pipe(
tap((res) => {
// Save JWT in localStorage
localStorage.setItem('token', res.access_token);
this._currentUser = res.user;
})
);
}

logout(): void {
localStorage.removeItem('token');
this._currentUser = null;
}

getToken(): string | null {
Expand All @@ -50,4 +53,12 @@ export class AuthService {
isLoggedIn(): boolean {
return !!this.getToken();
}

getCurrentUserId(): number | null {
return this._currentUser?.id ?? null;
}

getCurrentUser(): { id: number; username: string; role: string } | null {
return this._currentUser;
}
}
6 changes: 5 additions & 1 deletion frontend/src/app/auth/login/login.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ export class LoginComponent {
this.auth.login(this.loginForm.value).subscribe({
next: () => {
this.error = '';
this.router.navigate(['/events']);
this.message = 'Login successful!';
this.cd.detectChanges();

// Navigate to dashboard instead of /events
this.router.navigate(['/dashboard']);
},
error: (err) => {
this.error = err.error?.message ?? 'Invalid email or password';
Expand Down
15 changes: 15 additions & 0 deletions frontend/src/app/dashboard/dashboard.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="dashboard-container">
<div class="dashboard-header">
<span class="logo">Event Platform</span>
<nav>
<a routerLink="/dashboard">Manage Events</a>
<a routerLink="/profile">Profile</a>
<a routerLink="/logout">Logout</a>
</nav>
</div>

<div class="dashboard-body">
<app-event-create-form></app-event-create-form>
<app-report-progress></app-report-progress>
</div>
</div>
50 changes: 50 additions & 0 deletions frontend/src/app/dashboard/dashboard.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
.dashboard-container {
padding: 20px;
font-family: Arial, sans-serif;

header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;

.logo {
font-weight: bold;
font-size: 24px;
}

nav a {
margin-left: 15px;
text-decoration: none;
color: #333;
}
}

section {
margin-bottom: 30px;

h3 {
margin-bottom: 10px;
}

.status {
margin-top: 10px;

.progress-bar {
width: 100%;
height: 10px;
background-color: #eee;
border-radius: 5px;
overflow: hidden;
margin-top: 5px;

.progress-fill {
height: 100%;
background-color: #4caf50;
width: 0%;
transition: width 0.3s ease;
}
}
}
}
}
13 changes: 13 additions & 0 deletions frontend/src/app/dashboard/dashboard.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Component } from '@angular/core';
import { RouterModule } from '@angular/router';
import { EventCreateFormComponent } from './event-create-form/event-create-form.component';
import { ReportProgressComponent } from './report-progress/report-progress.component';

@Component({
selector: 'app-dashboard',
standalone: true,
imports: [RouterModule, EventCreateFormComponent, ReportProgressComponent],
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.scss'], // <-- add this
})
export class DashboardComponent {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="event-create-container">
<h3>Create New Event</h3>
<form [formGroup]="eventForm" (ngSubmit)="onSubmit()">
<label>Title<input formControlName="title" /></label>
<label>Location<input formControlName="location" /></label>
<label>Date<input type="date" formControlName="event_date" /></label>
<label>Description<textarea formControlName="description"></textarea></label>
<label>Capacity<input type="number" formControlName="capacity" /></label>

<button type="submit">Create Event</button>
</form>

<p class="message" *ngIf="message">{{ message }}</p>
<p class="error" *ngIf="error">{{ error }}</p>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { EventService, CreateEventPayload } from '../../services/event.service';

@Component({
selector: 'app-event-create-form',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './event-create-form.component.html',
})
export class EventCreateFormComponent {
eventForm: FormGroup;
message: string = '';
error: string = '';

constructor(private fb: FormBuilder, private eventService: EventService) {
this.eventForm = this.fb.group({
title: ['', Validators.required],
location: ['', Validators.required],
event_date: ['', Validators.required],
description: [''],
capacity: [1, [Validators.required, Validators.min(1)]],
});
}

onSubmit() {
if (this.eventForm.valid) {
const payload: CreateEventPayload = {
title: this.eventForm.value.title!,
location: this.eventForm.value.location!,
event_date: this.eventForm.value.event_date!,
description: this.eventForm.value.description || '',
capacity: this.eventForm.value.capacity!,
};

this.eventService.createEvent(payload).subscribe({
next: () => {
this.message = 'Event created successfully!';
this.error = '';
this.eventForm.reset();
},
error: (err) => {
this.error = err.error?.message ?? 'Event creation failed';
this.message = '';
},
});
} else {
this.error = 'Please fill in all required fields correctly.';
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<div class="report-container">
<h3>Participation Reports</h3>
<label>Select Event</label>
<select [(ngModel)]="selectedEventId">
<option *ngFor="let e of events" [value]="e.event_id">{{ e.title }}</option>
</select>

<button (click)="generateReport()" [disabled]="!selectedEventId">Generate Report</button>

<div *ngIf="status()">
<p>Status: {{ status() }}</p>
<progress [value]="progress()" max="100"></progress>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<div class="report-progress-container">
<h3>Participation Reports</h3>

<label for="eventSelect">Select Event</label>
<select [(ngModel)]="selectedEventId" id="eventSelect">
<option *ngFor="let e of events" [value]="e.event_id">{{ e.title }}</option>
</select>

<button (click)="generateReport()">Generate Report</button>

<div *ngIf="statusMessage">
<p>Status: {{ statusMessage }}</p>
<progress [value]="progress" max="100"></progress>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { EventService, EventItem } from '../../services/event.service';
import { AuthService } from '../../auth/auth.service';

@Component({
selector: 'app-report-progress',
standalone: true,
imports: [CommonModule, FormsModule],
templateUrl: './report-progress.component.html',
})
export class ReportProgressComponent {
events: EventItem[] = [];
selectedEventId: number | null = null;
statusMessage = '';
progressValue = 0;

constructor(
private eventService: EventService,
private authService: AuthService
) {
this.loadEvents();
}

loadEvents() {
const userId = this.authService.getCurrentUserId();
if (userId) {
this.eventService.getEventsByUser(userId).subscribe((e: EventItem[]) => {
this.events = e;
});
}
}

generateReport() {
if (!this.selectedEventId) return;

this.statusMessage = 'Generating... (do not close)';
this.progressValue = 0;

const interval = setInterval(() => {
this.progressValue += 10;
if (this.progressValue >= 100) {
clearInterval(interval);
this.statusMessage = 'Report generation completed!';
}
}, 200);
}

status() {
return this.statusMessage;
}

progress() {
return this.progressValue;
}
}
34 changes: 34 additions & 0 deletions frontend/src/app/services/event.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

export interface CreateEventPayload {
title: string;
location: string;
event_date: string;
description?: string;
capacity: number;
}

export interface EventItem {
event_id: number;
title: string;
[key: string]: any;
}

@Injectable({
providedIn: 'root',
})
export class EventService {
private apiUrl = 'http://localhost:3000/api/events';

constructor(private http: HttpClient) {}

createEvent(payload: CreateEventPayload): Observable<any> {
return this.http.post(`${this.apiUrl}`, payload);
}

getEventsByUser(userId?: number): Observable<EventItem[]> {
return this.http.get<EventItem[]>(`${this.apiUrl}/my/events`);
}
}
14 changes: 14 additions & 0 deletions frontend/src/app/services/report.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root'
})
export class ReportService {
constructor(private http: HttpClient) {}

generateReport(eventId: number): Observable<any> {
return this.http.get(`/api/reports/${eventId}`);
}
}