fix
This commit is contained in:
@@ -110,6 +110,7 @@ button.secondary-btn:hover {
|
||||
/* Applicant card layout */
|
||||
.listItem {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 1.2rem;
|
||||
padding: 1rem 1.4rem;
|
||||
@@ -149,7 +150,8 @@ button.secondary-btn:hover {
|
||||
}
|
||||
|
||||
.delete-button {
|
||||
margin-left: 12px;
|
||||
margin-left: auto;
|
||||
margin-right: 1rem;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { SocketIOService } from '../../services/socket-io.service';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { trigger, transition } from '@angular/animations';
|
||||
import { slideLeft, slideRight } from './slide.animation';
|
||||
import { tap } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-application',
|
||||
@@ -68,10 +69,11 @@ export class ApplicationComponent implements OnInit {
|
||||
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.dataService.cachedApplicationList.length > 0) {
|
||||
this.applicationList.set(this.dataService.cachedApplicationList);
|
||||
if (this.dataService.cachedApplicationList().length > 0) {
|
||||
this.applicationList.set(this.dataService.cachedApplicationList());
|
||||
this.initializeApplication();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
this.dataService.loadCandidateList().subscribe({
|
||||
next: (data) => {
|
||||
this.applicationList.set(data);
|
||||
@@ -82,7 +84,7 @@ export class ApplicationComponent implements OnInit {
|
||||
alert('Error loading candidate list');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
editApplication() {
|
||||
@@ -108,7 +110,7 @@ export class ApplicationComponent implements OnInit {
|
||||
|
||||
if (this.currentIndex() !== foundIndex) {
|
||||
this.currentIndex.set(foundIndex);
|
||||
this.loadApplication(applicationId);
|
||||
this.loadApplication(applicationId)?.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +120,7 @@ export class ApplicationComponent implements OnInit {
|
||||
}
|
||||
|
||||
this.currentApplication.set(null);
|
||||
this.dataService.getApplicationDetails(id).subscribe({
|
||||
return this.dataService.getApplicationDetails(id).pipe(tap({
|
||||
next: (data) => {
|
||||
this.currentApplication.set(data);
|
||||
const currentRoute = this.router.url;
|
||||
@@ -131,7 +133,7 @@ export class ApplicationComponent implements OnInit {
|
||||
console.error('Error loading application details:', error);
|
||||
alert('Error loading application details');
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
goToPrevious() {
|
||||
@@ -139,7 +141,7 @@ export class ApplicationComponent implements OnInit {
|
||||
const newIndex = this.currentIndex() - 1;
|
||||
this.currentIndex.set(newIndex);
|
||||
const prevId = this.applicationList()[newIndex].id;
|
||||
this.loadApplication(prevId);
|
||||
this.loadApplication(prevId)?.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,7 +150,7 @@ export class ApplicationComponent implements OnInit {
|
||||
const newIndex = this.currentIndex() + 1;
|
||||
this.currentIndex.set(newIndex);
|
||||
const nextId = this.applicationList()[newIndex].id;
|
||||
this.loadApplication(nextId);
|
||||
this.loadApplication(nextId)?.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="fileUploadWrapper">
|
||||
<mat-label>Profile Photo</mat-label>
|
||||
<mat-label>Profile Photo*</mat-label>
|
||||
|
||||
<div class="fileUploadContainer" [ngStyle]="{ 'margin-top': value ? '5px' : '20px' }">
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
<button
|
||||
mat-raised-button
|
||||
color="primary"
|
||||
type="button"
|
||||
(click)="fileInput.click()">
|
||||
type="button">
|
||||
Browse
|
||||
</button>
|
||||
<small style="margin: 10px 0; display: block;">Drag and drop here</small>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Component, effect, forwardRef, inject, input } from '@angular/core';
|
||||
import { ControlValueAccessor, FormsModule, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
|
||||
import { Component, effect, forwardRef, inject, input, viewChild } from '@angular/core';
|
||||
import { ControlValueAccessor, FormsModule, NG_VALIDATORS, NG_VALUE_ACCESSOR, ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
@@ -25,13 +25,15 @@ import { CommonModule } from '@angular/common';
|
||||
provide: NG_VALUE_ACCESSOR,
|
||||
useExisting: forwardRef(() => ImageInputComponent),
|
||||
multi: true
|
||||
}
|
||||
},
|
||||
],
|
||||
templateUrl: './image-input.component.html',
|
||||
styleUrl: './image-input.component.scss'
|
||||
})
|
||||
export class ImageInputComponent implements ControlValueAccessor {
|
||||
fileInput = viewChild<HTMLInputElement>('fileInput');
|
||||
value: File | null = null;
|
||||
touched = false;
|
||||
disabled = false;
|
||||
previewUrl: string | null = null;
|
||||
src = input<string | null>(null);
|
||||
@@ -57,6 +59,7 @@ export class ImageInputComponent implements ControlValueAccessor {
|
||||
this.previewUrl = null;
|
||||
}
|
||||
}
|
||||
|
||||
registerOnChange(fn: any): void {
|
||||
this.onChange = fn;
|
||||
}
|
||||
@@ -71,16 +74,21 @@ export class ImageInputComponent implements ControlValueAccessor {
|
||||
|
||||
|
||||
setFileData(event: Event): void {
|
||||
this.markAsTouched();
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input?.files?.[0]) {
|
||||
this.value = input.files[0];
|
||||
|
||||
this.previewUrl = URL.createObjectURL(this.value);
|
||||
console.log(this.previewUrl)
|
||||
this.onChange(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
markAsTouched() {
|
||||
if (this.touched) return;
|
||||
this.onTouched();
|
||||
this.touched = true;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.value = null;
|
||||
this.isDisplayOnly = false;
|
||||
|
||||
@@ -86,7 +86,6 @@ export class LeafletMapComponent implements AfterViewInit, ControlValueAccessor
|
||||
if (exact) this.onSelectCity(exact.name);
|
||||
|
||||
else this.onTouched();
|
||||
console.log(this.marker);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
<!-- Profile Image Upload -->
|
||||
<div class="image-upload">
|
||||
<app-image-input [src]="this.previewUrl" formControlName="profileImage"></app-image-input>
|
||||
@if (form.get('profileImage')?.touched && form.get('profileImage')?.hasError('required')) {
|
||||
<mat-error>Image is required</mat-error>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Full Name -->
|
||||
@@ -44,6 +47,9 @@
|
||||
@if (form.get('email')?.hasError('email')) {
|
||||
<mat-error>Invalid email</mat-error>
|
||||
}
|
||||
@if (form.get('email')?.hasError('required')) {
|
||||
<mat-error>Email is required</mat-error>
|
||||
}
|
||||
</mat-form-field>
|
||||
|
||||
<!-- Phone Number -->
|
||||
|
||||
@@ -51,6 +51,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 1rem;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.image-upload img {
|
||||
@@ -101,7 +103,6 @@ mat-error {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Buttons */
|
||||
button {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AfterViewInit, Component, ElementRef, inject, input, OnInit, QueryList, signal, ViewChildren } from '@angular/core';
|
||||
import { AfterViewInit, Component, ElementRef, inject, input, OnInit, QueryList, Renderer2, signal, ViewChildren } from '@angular/core';
|
||||
import { FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
@@ -44,6 +44,7 @@ export class RegistrationComponent implements OnInit {
|
||||
activatedRoute = inject(ActivatedRoute);
|
||||
snackBar = inject(MatSnackBar)
|
||||
socketService = inject(SocketIOService);
|
||||
renderer = inject(Renderer2);
|
||||
|
||||
originalImageUrl: string | null = null;
|
||||
previewUrl: string | null = null;
|
||||
@@ -118,6 +119,7 @@ export class RegistrationComponent implements OnInit {
|
||||
}
|
||||
|
||||
onSubmit() {
|
||||
this.form.markAllAsTouched();
|
||||
if (!this.form.valid) {
|
||||
this.scrollToFirstInvalidField();
|
||||
return;
|
||||
@@ -156,14 +158,18 @@ export class RegistrationComponent implements OnInit {
|
||||
});
|
||||
} else {
|
||||
this.dataService.submitCandidateForm(formData).subscribe({
|
||||
next: (newCandidate) => {
|
||||
next: (newCandidate: any) => {
|
||||
this.snackBar.open('✅ Application saved!', 'Close', {
|
||||
duration: 5000,
|
||||
horizontalPosition: 'center',
|
||||
verticalPosition: 'top',
|
||||
});
|
||||
this.dataService.cachedApplicationList.update((data) => {
|
||||
return { ...data, newCandidate };
|
||||
})
|
||||
this.socketService.socket.emit('candidateRegistered', newCandidate);
|
||||
this.form.reset();
|
||||
this.applicationId.set(newCandidate.id);
|
||||
this.router.navigate([`/application/${this.applicationId()}`])
|
||||
},
|
||||
error: err => alert('Error submitting form'),
|
||||
});
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { HttpClient } from "@angular/common/http";
|
||||
import { inject, Injectable, signal } from "@angular/core";
|
||||
import { environment } from "../../environments/environment.development";
|
||||
import { environment } from "../../environments/environment";
|
||||
import { delay, tap } from "rxjs";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class CandidateDataService {
|
||||
httpClient = inject(HttpClient)
|
||||
isCandidatesListLoading = signal(false)
|
||||
isApplicationDetailsLoading = signal(false)
|
||||
cachedApplicationList: any[] = []
|
||||
httpClient = inject(HttpClient);
|
||||
isCandidatesListLoading = signal(false);
|
||||
isApplicationDetailsLoading = signal(false);
|
||||
cachedApplicationList = signal<any[]>([]);
|
||||
|
||||
loadCandidateList() {
|
||||
this.isCandidatesListLoading.set(true)
|
||||
@@ -18,7 +18,7 @@ export class CandidateDataService {
|
||||
delay(500),
|
||||
tap((data) => {
|
||||
this.isCandidatesListLoading.set(false);
|
||||
this.cachedApplicationList = data;
|
||||
this.cachedApplicationList.set(data);
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -45,7 +45,7 @@ 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);
|
||||
this.cachedApplicationList.set(this.cachedApplicationList().filter(c => c.id !== id));
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Observable } from 'rxjs';
|
||||
import { io } from "socket.io-client";
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SocketIOService {
|
||||
|
||||
socket = io('ws://localhost:3000');
|
||||
socket = io(`${environment.socketUrl}`);
|
||||
|
||||
onCandidateRegistered(): Observable<any> {
|
||||
return new Observable(observer => {
|
||||
this.socket.on('candidateRegistered', (data) => {
|
||||
console.log(data);
|
||||
observer.next(data);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export const environment = {
|
||||
hostUrl: 'http://localhost:3000',
|
||||
mapTilerApiKey: '9LJZ0OppHyT3LzvQW3ce'
|
||||
mapTilerApiKey: '9LJZ0OppHyT3LzvQW3ce',
|
||||
socketUrl: 'ws://localhost:3000'
|
||||
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const environment = {
|
||||
hostUrl: 'https://iisa.novikov.click',
|
||||
mapTilerApiKey: '9LJZ0OppHyT3LzvQW3ce'
|
||||
|
||||
mapTilerApiKey: '9LJZ0OppHyT3LzvQW3ce',
|
||||
socketUrl: 'https://iisa.novikov.click'
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export const environment = {
|
||||
hostUrl: '',
|
||||
mapTilerApiKey:''
|
||||
mapTilerApiKey: '',
|
||||
socketUrl: ''
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user