wip
This commit is contained in:
@@ -62,11 +62,12 @@ Angular CLI does not come with an end-to-end testing framework by default. You c
|
|||||||
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
|
||||||
|
|
||||||
|
|
||||||
-implement total visits and total clicks on register button
|
|
||||||
|
|
||||||
|
|
||||||
minor things
|
-fix upload of the image in edit mode
|
||||||
|
-add deletion button to the card
|
||||||
-centralize scss check the same styles for differnet buttons
|
-centralize scss check the same styles for differnet buttons
|
||||||
-adjust to mobile format
|
-adjust to mobile format
|
||||||
|
|
||||||
****animations transitions (prev next cards)
|
****animations transitions (prev next cards)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<div class="container app-list">
|
<div class="container app-list">
|
||||||
<div class="header-row">
|
<div class="header-row">
|
||||||
<div class="list-header-top">
|
<div class="list-header-top">
|
||||||
<button mat-button type="button" (click)="goBack()" class="back-button">
|
<button mat-button type="button" routerLink="/landing" class="back-button">
|
||||||
Go Back
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
<h2 class="page-title">Applications</h2>
|
<h2 class="page-title">Applications</h2>
|
||||||
@@ -92,6 +92,15 @@
|
|||||||
<p>Age: {{ application.age }}</p>
|
<p>Age: {{ application.age }}</p>
|
||||||
<p>City: {{ application.cityOrRegion }}</p>
|
<p>City: {{ application.cityOrRegion }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
color="warn"
|
||||||
|
class="delete-button"
|
||||||
|
(click)="onDeleteClick($event ,application.id)"
|
||||||
|
[attr.aria-label]="'Delete ' + application.fullName"
|
||||||
|
>
|
||||||
|
<mat-icon>delete</mat-icon>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -131,6 +131,33 @@
|
|||||||
color: #00ffff;
|
color: #00ffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.delete-button {
|
||||||
|
margin-left: 12px;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
min-width: 40px;
|
||||||
|
background: rgba(255, 68, 68, 0.1);
|
||||||
|
border: 1px solid #ff4444;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #ff4444;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button:hover {
|
||||||
|
background: rgba(255, 68, 68, 0.2);
|
||||||
|
color: #ff6666;
|
||||||
|
box-shadow: 0 0 20px rgba(255, 68, 68, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-color: #ff6666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-button mat-icon {
|
||||||
|
font-size: 20px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.charts-row {
|
.charts-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { SocketIOService } from '../../services/socket-io.service';
|
|||||||
import { BaseChartDirective } from 'ng2-charts';
|
import { BaseChartDirective } from 'ng2-charts';
|
||||||
import { ChartData, ChartOptions, Chart, registerables } from 'chart.js';
|
import { ChartData, ChartOptions, Chart, registerables } from 'chart.js';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
Chart.register(...registerables);
|
Chart.register(...registerables);
|
||||||
|
|
||||||
@@ -32,10 +33,10 @@ Chart.register(...registerables);
|
|||||||
styleUrls: ['./application-list.component.scss']
|
styleUrls: ['./application-list.component.scss']
|
||||||
})
|
})
|
||||||
export class ApplicationListComponent implements OnInit {
|
export class ApplicationListComponent implements OnInit {
|
||||||
private dataService = inject(CandidateDataService);
|
dataService = inject(CandidateDataService);
|
||||||
private socketService = inject(SocketIOService);
|
socketService = inject(SocketIOService);
|
||||||
private router = inject(Router);
|
router = inject(Router);
|
||||||
|
snackBar = inject(MatSnackBar)
|
||||||
environment = environment;
|
environment = environment;
|
||||||
|
|
||||||
applicationList = signal<any[]>([]);
|
applicationList = signal<any[]>([]);
|
||||||
@@ -192,11 +193,16 @@ export class ApplicationListComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
this.availableCities.set(this.getUniqueCities(this.applicationList()));
|
this.availableCities.set(this.getUniqueCities(this.applicationList()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.socketService.onCandidateDeleted().subscribe(deletedCandidateId => {
|
||||||
|
this.applicationList.update(list =>
|
||||||
|
list.filter(app => app.id !== deletedCandidateId)
|
||||||
|
);
|
||||||
|
this.availableCities.set(this.getUniqueCities(this.applicationList()));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
this.router.navigate(['/landing']);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUniqueCities(data: any[]): City[] {
|
getUniqueCities(data: any[]): City[] {
|
||||||
const seen: string[] = [];
|
const seen: string[] = [];
|
||||||
@@ -209,4 +215,24 @@ export class ApplicationListComponent implements OnInit {
|
|||||||
})
|
})
|
||||||
.sort((a, b) => a.name.localeCompare(b.name));
|
.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDeleteClick(event: any, id: number) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.dataService.deleteCandidate(id).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.snackBar.open('✅ Application deleted!', 'Close', {
|
||||||
|
duration: 5000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.snackBar.open('Error deleting application!', 'Close', {
|
||||||
|
duration: 5000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="container application">
|
<div class="container application">
|
||||||
<header class="header">
|
<header class="header">
|
||||||
<button mat-button type="button" (click)="goBack()" class="back-button">
|
<button mat-button type="button" routerLink="/application-list" class="back-button">
|
||||||
<mat-icon>arrow_back</mat-icon>
|
<mat-icon>arrow_back</mat-icon>
|
||||||
Go Back
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
@@ -15,7 +15,6 @@
|
|||||||
} @else if (hasApplicationData) {
|
} @else if (hasApplicationData) {
|
||||||
<div class="content-wrapper">
|
<div class="content-wrapper">
|
||||||
|
|
||||||
<!-- Navigation -->
|
|
||||||
<div class="navigation-controls">
|
<div class="navigation-controls">
|
||||||
<button
|
<button
|
||||||
mat-icon-button
|
mat-icon-button
|
||||||
@@ -42,10 +41,8 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Application Card -->
|
|
||||||
<div class="application-card">
|
<div class="application-card">
|
||||||
|
|
||||||
<!-- Profile Section -->
|
|
||||||
<div class="profile-section">
|
<div class="profile-section">
|
||||||
@if (currentApplication().profileImage) {
|
@if (currentApplication().profileImage) {
|
||||||
<div class="profile-image-container">
|
<div class="profile-image-container">
|
||||||
@@ -84,7 +81,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Application Details -->
|
|
||||||
<div class="details-grid">
|
<div class="details-grid">
|
||||||
@if (getHobbiesArray().length > 0) {
|
@if (getHobbiesArray().length > 0) {
|
||||||
<div class="detail-section">
|
<div class="detail-section">
|
||||||
@@ -114,7 +110,6 @@
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Edit Button or Info -->
|
|
||||||
<div class="edit-controls">
|
<div class="edit-controls">
|
||||||
@if (canEdit()) {
|
@if (canEdit()) {
|
||||||
<button
|
<button
|
||||||
@@ -128,9 +123,17 @@
|
|||||||
} @else {
|
} @else {
|
||||||
<p class="edit-info">You can edit your application only during 3 days</p>
|
<p class="edit-info">You can edit your application only during 3 days</p>
|
||||||
}
|
}
|
||||||
|
<button
|
||||||
|
mat-raised-button
|
||||||
|
color="warn"
|
||||||
|
class="delete-button"
|
||||||
|
(click)="onDeleteClick($event ,currentApplication().id)"
|
||||||
|
[attr.aria-label]="'Delete ' + currentApplication().fullName"
|
||||||
|
>
|
||||||
|
Delete Application
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Raw JSON Data -->
|
|
||||||
<details class="raw-data-section">
|
<details class="raw-data-section">
|
||||||
<summary>View Raw Data</summary>
|
<summary>View Raw Data</summary>
|
||||||
<pre class="raw-data">{{ currentApplication() | json }}</pre>
|
<pre class="raw-data">{{ currentApplication() | json }}</pre>
|
||||||
@@ -141,7 +144,7 @@
|
|||||||
<div class="no-data">
|
<div class="no-data">
|
||||||
<mat-icon class="no-data-icon">inbox</mat-icon>
|
<mat-icon class="no-data-icon">inbox</mat-icon>
|
||||||
<p>No application data available</p>
|
<p>No application data available</p>
|
||||||
<button mat-button (click)="goBack()" class="back-button">
|
<button mat-button routerLink="/application-list" class="back-button">
|
||||||
<mat-icon>arrow_back</mat-icon>
|
<mat-icon>arrow_back</mat-icon>
|
||||||
Return to List
|
Return to List
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, inject, OnInit, signal, computed } from '@angular/core';
|
import { Component, inject, OnInit, signal, computed } from '@angular/core';
|
||||||
import { CandidateDataService } from '../../services/candidate-data.service';
|
import { CandidateDataService } from '../../services/candidate-data.service';
|
||||||
import { ActivatedRoute, Router, RouterModule } from '@angular/router';
|
import { ActivatedRoute, Router, RouterLink, RouterModule } from '@angular/router';
|
||||||
import { MatIconModule } from '@angular/material/icon';
|
import { MatIconModule } from '@angular/material/icon';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
@@ -9,6 +9,7 @@ import { MatChipsModule } from '@angular/material/chips';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { environment } from '../../../environments/environment';
|
import { environment } from '../../../environments/environment';
|
||||||
import { SocketIOService } from '../../services/socket-io.service';
|
import { SocketIOService } from '../../services/socket-io.service';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-application',
|
selector: 'app-application',
|
||||||
@@ -19,17 +20,20 @@ import { SocketIOService } from '../../services/socket-io.service';
|
|||||||
MatProgressSpinnerModule,
|
MatProgressSpinnerModule,
|
||||||
MatCardModule,
|
MatCardModule,
|
||||||
MatChipsModule,
|
MatChipsModule,
|
||||||
CommonModule
|
CommonModule,
|
||||||
|
RouterLink
|
||||||
],
|
],
|
||||||
templateUrl: './application.component.html',
|
templateUrl: './application.component.html',
|
||||||
styleUrls: ['./application.component.scss']
|
styleUrls: ['./application.component.scss']
|
||||||
})
|
})
|
||||||
export class ApplicationComponent implements OnInit{
|
export class ApplicationComponent implements OnInit {
|
||||||
dataService = inject(CandidateDataService);
|
dataService = inject(CandidateDataService);
|
||||||
socketService = inject(SocketIOService);
|
socketService = inject(SocketIOService);
|
||||||
activatedRoute = inject(ActivatedRoute);
|
activatedRoute = inject(ActivatedRoute);
|
||||||
|
snackBar = inject(MatSnackBar);
|
||||||
router = inject(Router);
|
router = inject(Router);
|
||||||
|
|
||||||
|
|
||||||
currentApplication = signal<any>(null);
|
currentApplication = signal<any>(null);
|
||||||
applicationList = signal<any[]>([]);
|
applicationList = signal<any[]>([]);
|
||||||
currentIndex = signal<number>(-1);
|
currentIndex = signal<number>(-1);
|
||||||
@@ -76,13 +80,13 @@ export class ApplicationComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editApplication(): void {
|
editApplication() {
|
||||||
const app = this.currentApplication();
|
const app = this.currentApplication();
|
||||||
if (!app) return;
|
if (!app) return;
|
||||||
this.router.navigate(['/application', app.id, 'edit']);
|
this.router.navigate(['/application', app.id, 'edit']);
|
||||||
}
|
}
|
||||||
|
|
||||||
initializeApplication(): void {
|
initializeApplication() {
|
||||||
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
const id = this.activatedRoute.snapshot.paramMap.get('id');
|
||||||
if (!id) {
|
if (!id) {
|
||||||
alert('Invalid route');
|
alert('Invalid route');
|
||||||
@@ -103,7 +107,7 @@ export class ApplicationComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadApplication(id: number): void {
|
loadApplication(id: number) {
|
||||||
if (this.currentApplication()?.id === id) {
|
if (this.currentApplication()?.id === id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -125,7 +129,7 @@ export class ApplicationComponent implements OnInit{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
goToPrevious(): void {
|
goToPrevious() {
|
||||||
if (this.canGoToPrevious()) {
|
if (this.canGoToPrevious()) {
|
||||||
const newIndex = this.currentIndex() - 1;
|
const newIndex = this.currentIndex() - 1;
|
||||||
this.currentIndex.set(newIndex);
|
this.currentIndex.set(newIndex);
|
||||||
@@ -134,7 +138,7 @@ export class ApplicationComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goToNext(): void {
|
goToNext() {
|
||||||
if (this.canGoToNext()) {
|
if (this.canGoToNext()) {
|
||||||
const newIndex = this.currentIndex() + 1;
|
const newIndex = this.currentIndex() + 1;
|
||||||
this.currentIndex.set(newIndex);
|
this.currentIndex.set(newIndex);
|
||||||
@@ -143,9 +147,7 @@ export class ApplicationComponent implements OnInit{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
this.router.navigate(['/application-list']);
|
|
||||||
}
|
|
||||||
|
|
||||||
getHobbiesArray(): string[] {
|
getHobbiesArray(): string[] {
|
||||||
const app = this.currentApplication();
|
const app = this.currentApplication();
|
||||||
@@ -157,7 +159,7 @@ export class ApplicationComponent implements OnInit{
|
|||||||
return Array.isArray(app.hobbies) ? app.hobbies : [];
|
return Array.isArray(app.hobbies) ? app.hobbies : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
formatDate(dateString: string): string {
|
formatDate(dateString: string) {
|
||||||
if (!dateString) return 'Not specified';
|
if (!dateString) return 'Not specified';
|
||||||
try {
|
try {
|
||||||
return new Date(dateString).toLocaleDateString();
|
return new Date(dateString).toLocaleDateString();
|
||||||
@@ -165,6 +167,25 @@ export class ApplicationComponent implements OnInit{
|
|||||||
return dateString;
|
return dateString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
onDeleteClick(event: any, id: number) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.dataService.deleteCandidate(id).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.snackBar.open('✅ Application deleted!', 'Close', {
|
||||||
|
duration: 5000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.snackBar.open('Error deleting application!', 'Close', {
|
||||||
|
duration: 5000,
|
||||||
|
horizontalPosition: 'center',
|
||||||
|
verticalPosition: 'top',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,14 +1,23 @@
|
|||||||
|
|
||||||
<div class="container landing">
|
<div class="container landing">
|
||||||
<h2>Welcome to IISA</h2>
|
<h2>Welcome to IISA</h2>
|
||||||
<p class="intro">Join the mission or track your application below.</p>
|
<p class="intro">Join the mission or track your application below.</p>
|
||||||
|
|
||||||
<div class="button-group">
|
<div class="button-group">
|
||||||
<button mat-button routerLink="/application/new" class="primary-btn">
|
<button
|
||||||
|
mat-button
|
||||||
|
routerLink="/application/new"
|
||||||
|
class="primary-btn"
|
||||||
|
(click)="onRegisterClick()">
|
||||||
Apply for Registration
|
Apply for Registration
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button mat-button routerLink="/application-list" class="secondary-btn">
|
<button mat-button routerLink="/application-list" class="secondary-btn">
|
||||||
View Applications
|
View Applications
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<p>👀 Total Visits: {{ stats.totalVisits() }}</p>
|
||||||
|
<p>🖱️ Register Button Clicks: {{ stats.totalClicks() }}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -93,6 +93,14 @@ button.secondary-btn:hover {
|
|||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 16px;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Responsive adjustments */
|
/* Responsive adjustments */
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
.landing h2 {
|
.landing h2 {
|
||||||
|
|||||||
@@ -1,15 +1,24 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit, inject } from '@angular/core';
|
||||||
import { MatButtonModule } from '@angular/material/button';
|
import { MatButtonModule } from '@angular/material/button';
|
||||||
import { RouterLink } from '@angular/router';
|
import { RouterLink } from '@angular/router';
|
||||||
import { RouterLinkActive } from "../../../../node_modules/@angular/router/router_module.d-Bx9ArA6K";
|
import { StatsService } from '../../services/stats.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-landing',
|
selector: 'app-landing',
|
||||||
imports: [MatButtonModule, RouterLink,],
|
standalone: true,
|
||||||
|
imports: [MatButtonModule, RouterLink],
|
||||||
templateUrl: './landing.component.html',
|
templateUrl: './landing.component.html',
|
||||||
styleUrl: './landing.component.scss'
|
styleUrls: ['./landing.component.scss']
|
||||||
})
|
})
|
||||||
export class LandingComponent {
|
export class LandingComponent implements OnInit {
|
||||||
|
stats = inject(StatsService);
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
this.stats.loadInitial();
|
||||||
|
this.stats.incrementVisit().subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
onRegisterClick() {
|
||||||
|
this.stats.incrementClick().subscribe();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
Cancel Editing
|
Cancel Editing
|
||||||
</button>
|
</button>
|
||||||
}@else {
|
}@else {
|
||||||
<button mat-button type="button" (click)="goBack()" class="button">
|
<button mat-button type="button" routerLink="/landing" class="button">
|
||||||
Go Back
|
Go Back
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,7 +168,5 @@ export class RegistrationComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
goBack(): void {
|
|
||||||
this.router.navigate(['/landing']);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,5 +42,12 @@ export class CandidateDataService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteCandidate(id: number) {
|
||||||
|
return this.httpClient.delete(`${environment.hostUrl}/app/candidate/${id}`).pipe(
|
||||||
|
tap(() => {
|
||||||
|
this.cachedApplicationList = this.cachedApplicationList.filter(c => c.id !== id);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -25,10 +25,27 @@ export class SocketIOService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
onCandidateDeleted(): Observable<any> {
|
||||||
this.socket.disconnect();
|
return new Observable(observer => {
|
||||||
|
this.socket.on('candidateDeleted', (data) => {
|
||||||
|
observer.next(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onStatsUpdated(): Observable<any> {
|
||||||
|
return new Observable(observer => {
|
||||||
|
this.socket.on('statsUpdated', (data) => {
|
||||||
|
observer.next(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// disconnect() {
|
||||||
|
// this.socket.disconnect();
|
||||||
|
// }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
36
src/app/services/stats.service.ts
Normal file
36
src/app/services/stats.service.ts
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import { Injectable, signal, effect, inject } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { SocketIOService } from './socket-io.service';
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class StatsService {
|
||||||
|
http = inject(HttpClient);
|
||||||
|
socket = inject(SocketIOService);
|
||||||
|
|
||||||
|
|
||||||
|
totalVisits = signal(0);
|
||||||
|
totalClicks = signal(0);
|
||||||
|
|
||||||
|
private socketEffect = effect(() => {
|
||||||
|
this.socket.onStatsUpdated().subscribe(stats => {
|
||||||
|
this.totalVisits.set(stats.totalVisits);
|
||||||
|
this.totalClicks.set(stats.totalClicks);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
loadInitial() {
|
||||||
|
this.http.get<any>(`${environment.hostUrl}/stats`).subscribe(stats => {
|
||||||
|
this.totalVisits.set(stats.totalVisits);
|
||||||
|
this.totalClicks.set(stats.totalClicks);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementVisit() {
|
||||||
|
return this.http.post(`${environment.hostUrl}/stats/visit`, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementClick() {
|
||||||
|
return this.http.post(`${environment.hostUrl}/stats/click`, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user